Hi there!
Currently I'm setting up text to automatically read out on a timed delay. However, it occurs to me that waiting for text to pop up may be frustrating for faster readers, who may prefer to see the whole passage just pop up instantly.
I'm using the <<timed>> macro with the actual time as a variable which the player will be able to set to either 1s, 2s or 3s. However, there's no way to set it to 0 or an effective 0. I think the minimum is 40ms, but that causes a very unflattering visual effect of text appearing in a downwards swipe, not all of it to pop up at once. (Like it would if there was no <<timed>> macro stalling its appearance).
Currently I've got this in the CSS
.macro-timed-insert {
transition-duration: 1s;
}
This in my StoryInit
<if $textspeed is "">><<set $textSpeed to "3s">>
And I'm doing something like this:
First non-delayed sentence.
<<timed $textSpeed transition>>
Second sentence.
<<next>>
Third sentence.
<<next>>
Fourth Sentence
Some Passage
<</timed>>
It looks pretty dramatic for certain passages, which is why I really want to find a way to have my cake and eat it too.
I considered wrapping the whole thing in an <<if>> statement and then having an <<else>> version with the same thing above, minus the timed effects, but a guy I learned code off once said "if you're starting to duplicate things, you're probably doing it wrong."
Tl;dr: I'm looking for a way in Sugarcube 2 that I can insert a timed macro to slowly read text, but with the option to remove its effects entirely so people who want to read all the text right away can do that.
Comments
Unless you're using <<remember>>, and you really shouldn't be, that is unlikely to be doing whatever you think it is. At the time the StoryInit special initialization passage is executed, all variables will be undefined, so $textSpeed could never be the empty string—unless you're using <<remember>>.
Also, I don't know if they're transcription errors, however, your <<if>> is missing an opening angle bracket and within its conditional you misspelled $textSpeed as $textspeed.
How, exactly, are you currently handling player modification of $textSpeed and is it, or should it be, stateful? I actually do not recommend using story variables for option settings as it bloats the story history to no good effect—the Setting API and settings object exist for that purpose.
That's not easily doable with <<timed>> as its currently written. It's not really difficult to write a custom solution to do exactly what you want, but the text speed situation should be settled first.
Looking into this helped me find a bug the current <<timed>> and <<repeat>> implementations that triggers when transitions are requested with near minimum delays. It's not related to what you want to accomplish, but I might not have found it for who knows how long if not for thinking about this.
Whoops. I put it in there so I didn't have to run past a passage where it is set, and I could just run into a bug text in any passage with my variable <<timed>> in it and see it at work. Guess that's not an option, though.
First one is transcription error, second is actual error. Thanks for the catch!
Hadn't quite got around to adding the section where users could manipulate this setting. So far I've just been seeing if I can make it work before allowing players to mess with it.
I'm honestly not that familiar with the Setting API and settings object. Been reading up since your post. Not sure what you mean by bloating the story history. Is this something I should be watching out for...? It's going to be a long game.
Another thing I'm not super familiar with is the term "stateful" (so noob. ;_;) I presume stateful means that it needs to remember a variable. If so, yes. I think.
Whatever gets me the end result. <<Timed>> just seemed like the best way to achieve it, but given it only does half of what I need, I agree that I probably won't be able to achieve my end goal with this particular macro.
Glad to help! Even if unintentionally.
I only asked because I didn't know if you were using the default UI. The `Setting` API sets up the Settings dialog and manages the `settings` object. Some people like the dialog, some prefer to roll their own UI.
I can throw together an example project later today. Showing both an example setting and the code needed to achieve the effect you want with it.
Story variables are a permanent part of every moment within the story history from the moment they're introduced onward—unless they're unset. Adding unnecessary story variables makes the history larger—both in memory and its serializations.
Basically, yes.
Whoops.
• To slow down text, I needed to use the <<Timed>> macro in a passage.
• To vary the speed in which text was shown, I would need to use a variable in the <<Timed>> macro.
• To set said variable, I would need to do so in a passage.
• Since StoryInit runs first, this would be the optimal passage to set it in.
• ... And all of this was really roundabout because your solution is infinitely better!
I wasn't aware you could play with these things, so I was using the default UI. o_o SugarCube is so fun and flexible.
Whoa. Right. Mental note to hold back on the story variables then, or unset them once I'm done with them. I'm using a lot of variables to hold things together at the moment. E.g. If someone talks to an NPC, I set a variable so they are ticked off as 'talked to', so the dialogue is not the same when they talk to them again.
Is there a more efficient way to approach the above scenario rather than a <<set $talkedToJoe is true>>?
Oh my god. Thank you so much for taking the time out to do that. This solves all my problems. O_O You are the best.
The reasoning is this. If I were to create a passage with delayed text and the player were to return to this passage, it would read out very slowly for a second time. While the first time, the text is new and the slow reading is dramatic, having it slowly read out text you've already seen would be somewhat painful.
The reason being is that with this game, I reuse some passages a lot. E.g. You're talking to an old man, and there may be different conversation topics. No matter which topic you pick, it loops back to the same starting spot. That means you're viewing the same passage quite a few times.
One mistake that I see people making fairly often is to store data which should be immutable in story variables, when data of that nature is better placed within the setup object or possibly somewhere on window object.
Another is using story variables to hold temporary values and then not unsetting them when they're no longer needed. Temporary variables were added to SugarCube v2 a while ago to help solve that problem.
That's a fairly standard thing to do. I suppose if a conversation was fixed to a particular passage and visit count, you could use the visited() function to check for it instead.
Yes. Open the Story JavaScript and make the following edit.
FIND:
REPLACE WITH:
Visited() does seem like a better choice, but the long term plan is to add a new game plus feature. My current approach was to track all $talkedToX variables in a spreadsheet, then when the time came, deliberately unset all of these upon starting a new game plus. That way I could have 'Achievement' variables or carry over certain things without the slate being completely cleared.
That said, if I erased the story history and opted to use Visited() instead, that would probably be cleaner. I guess it's whether or not removing the story history clears *everything* (meaning a New Game + where you might reset levels but keep money couldn't be done) or if removing the history only does just that, removing the history. Might be one of those rare cases <<remember>> is useful, but I get the sense to be wary of it.
Seems like a bit early in the game to be thinking of this extra feature stuff, but I like to make sure the foundation is solid for later incorporations.
As far as a New Game+ feature. The best way to accomplish that would probably depend on when you wanted the player to be able to activate it.
But after reading about the history kept in SugarCube, I think it only keeps up to 100 passages by default, and you want it less than that for longer games? I can see a way I could get saved history down to 1, since I've disabled the back and forward features and I'm not going to need people to navigate around the history.
Since the game has 400k words, it's probably going to generate a lot more than 100 passages of history. So if I rely on Visited() to indicate you've spoken to an important NPC before, you go off and view 100 passages, then return to talk to that NPC... it'll lose any indication you've spoken to them before. Assuming all this, it's probably smarter to rely on variables to indicate if you've spoken to them.
Also, to be clear. I'm really only talking about carrying data forward into a new game+ at the player's request. Simply having persistent achievements, or other stats, could be handled via <<remember>> without any need for what I'm thinking of.
Length alone isn't a significant issue specifically because of the expiry. The primary source of concern is the size of the variable store within each history moment. Even then, I wouldn't worry about it too much as you can always seamlessly lower the number of navigable moments kept within the history if it ever does become an issue.
Reducing the number of navigable moments kept within the history is fairly simple:
That's incorrect, actually. SugarCube v2's State object does keep track of expired moments, it simply doesn't keep them within the navigable portion of the history.
Think of it as having two histories: a navigable one which tracks all associated data (passage name, variable store, etc) and an expired one which only tracks the passage name.
The expired portion of the history allows the visited family of functions to work regardless of how many moments have expired from the navigable portion.
As a historical note. There was a period in v2's development (v2.0.0–v2.3.3) where the story history did not keep track of expired moments, leading to the very issue of the visited family of functions not being entirely reliable. That oversight was corrected in v2.5.0.
Even keeping in mind that the visited family of functions do not have the issue you thought they did, they do have a couple of limitations in this specific instance which complicates matters:
TIP: I'd suggest keeping your variable names as short as makes sense, as they are a part of the variable store—e.g. $metJoe vs $talkedToJoe. I do not want to oversell this, you should definitely use descriptive names. I'm simply saying, don't go crazy with the descriptions.
Again. Do not read too much meaning into this, you should most definitely use descriptive names. Just use some common sense. For example: Basically, when being descriptive with names in your code, and you should be, brevity is also something to keep in mind.
It would be extremely helpful, but only if you've got the time or the code lying around. I don't want to impose too much, since you've already helped me more than I can even articulate.
Then again, every demo posted here is helpful for the next guy like me to stumble along, so it's never just helping one person, which is kind of nice.
And that would be a good preemptive measure to reduce any memory / processing problems later on?
That does make a lot of sense. And there only being one history was the concern I had. Thank you for clearing this up!
It's a very small demo, only showing how you'd do something like carrying over a value into the NG+—coins in this case. That should give you the idea, hopefully.
Obviously, feel free to ask questions.
Yes. Reducing the navigable history to a single moment reduces all resource usage to the minimum level attainable.
That said, no one should worry too much about that sort of thing in SugarCube v2. Most projects will never need to worry about resource usage. For the rest, as I noted previously, they can always reduce the number of moments within the navigable history if it ever does become an issue.
In your case, I would probably suggest pulling the trigger, since you aren't letting players navigate the history anyway. There's no point in keeping more than one moment if your players will never be able to make use of them.
Thank you so much! I've examined the demo. I get most of it, but I do have two questions about it.
1. Why was a value to increase the coin number in StoryInit instead of a passage? I kind of figured it would initialize as 0 if ndef, then you'd add to it via <<set $coins += 50>> in a passage during gameplay, then it would carry it over to a new game based on what you'd accumulated. I'm probably missing something.
2, It also said /* Values not modified by a NG+. */ in the StoryInit passage. Does that mean that I should for some reason be keeping values in my StoryInit that won't be affected by NG+? The positioning in the StoryInit doesn't seem to be important, from what I can see, since there aren't any sections marked off by code.
Done!
I'd assumed that you would not be carrying over every value from one playthrough to the next, as that's not exactly a common thing to do, thus a section for values that aren't modified by a NG+. What you end up doing is up to you.
As far as the positioning, I thought it was fairly clear. A section at the top commented with Values modified by a NG+ and a section below that commented with Values not modified by a NG+. You don't have to keep them ordered that way, you don't have to keep them separate, you don't even have to have values that aren't touched by a NG+ at all. Again, whether or not you do is up to you.
It's a demo. It made sense when I was writing it that you would not want to carry every single value in your project over in some way for a NG+. Don't read too much into it.
2. So it's just keeping things tidy. That's what I thought, but I thought I'd ask in case I ended up entering values not modified by a NG+ in a really inefficient way when there was a more efficient way available.
Thank you again for the demo and your patience explaining all this to me.