Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Adding a variable only once?

Heya! So I hope this is a pretty simple question: is it possible to have a variable added once (like $money += 3 or whatever), but then if the player returns to the passage again, it doesn't add it a second time?

I'm Using SugerCube 2.16.0 and Twine 2.

Any help would be appreciated! Thank you !

Comments

  • edited April 2017
    This is more of a design issue than a code issue. Anything that increments or decrements a variable should usually be tied to player action, not player position.

    Here's a link to a discussion about that from a while ago.

    If that doesn't strike your fancy, you'll need to set up flag variables or use the visited() family of functions to guard your code and prevent it from rerunning when a passage is visited after the first time. I think that's a lot more work, personally.

    With SugarCube, though, the good news is that adding setters to your links is very simple:
    [[link|passage][$var+=10]]
    [[link|passage][$var++]]
    [[link|passage][$var to 0]]
    etc... 
    
  • If you plan on the player to be able to go back to the room multiple times however, you could put in a simple conditional check on the room.

    In StoryInit make something like the following
    <<set $room_one_visited = false>>
    

    Then in the room passage;
    <<if $room_one_visited == false>>
        Ooh, some money!
        <<set $money += 3>>
        <<set $room_one_visited = true>>
    <<else>>
        There's nothing here
    <</if>>
    
  • edited April 2017
    If you plan on the player to be able to go back to the room multiple times however, you could put in a simple conditional check on the room.

    In StoryInit make something like the following
    <<set $room_one_visited = false>>
    

    Then in the room passage;
    <<if $room_one_visited == false>>
        Ooh, some money!
        <<set $money += 3>>
        <<set $room_one_visited = true>>
    <<else>>
        There's nothing here
    <</if>>
    

    This is a really good point. I was sort of assuming that the issue was related to coming back from a menu. If it's not, then you'll need something like this.

    I would personally do something like:
    <<if visited() lte 1>>\
        /%    visited("passage name") will be 1 on the first visit to a given passage; 
              it defaults to the current passage if no arguments are given                    %/ \
        Ooh, some money!
        <<set $money += 3>>\
    <<else>>\
        There's nothing here
    <</if>>\
    

    If you have a lot of passages like this, there's no need to clog up your StoryInit and variable store with a bunch of flag variables, unless you need them for something else. Either way works, though.
  • If you plan on the player to be able to go back to the room multiple times however, you could put in a simple conditional check on the room.
    The canonical way to track if the player has visited a passage is via the visited() function. Whether the player has been to a passage before is already part of the story history, so creating a story variable to track something which is already part of the history is wasteful—you're bloating the variable store for no good end.

    For example:
    <<if visited() is 1>>
    	Ooh, some money! <<set $money += 3>>
    <<else>>
    	There's nothing here.
    <</if>>
    


    <<if $room_one_visited == false>>
    
    Don't do that. You do not need to compare the value of a variable which contains a boolean value to the boolean literals. Conditional expressions resolve to boolean values, so if you already have one, then simply use it.

    In other words, that code is better written as:
    <<if $room_one_visited>>
    
    Though, as noted above, don't use story variables simply to track whether the player has visited a passage before.
  • edited May 2017
    One technique I use is to set a temporary variables on entry:
    [[north][$coins to 3]]
    

    ...and then have an on entry widget that processes and clears the temporary variable...
    <<if $coins gte 0>><<set $gold to $gold + $coins>><<set $coins to 0>><<endif>>
    

    Once $coins has been set to 0, it won't add any more (unless you set it to something else to represent them finding some more money).
  • mykael wrote: »
    One technique I use is to set a temporary variables on entry:
    To be clear, your example is of a story variable being used for a temporary purpose, not an actual temporary variable.

    Beyond that, your example seems somewhat unnecessary as written. Rather than setting $coins with the link and then having the passage add its value to $gold, you could simply have the link add to $gold directly.

    If you had a use case where doing something like that was necessary, then I'd recommend never setting the "temporary" variable until necessary and using the def operator with the <<unset>> macro, so you aren't bloating the story history unnecessarily. For example:
    /* If $coins is defined, attempt to use it.  Elsewise, do nothing. */
    <<if def $coins>><<set $gold to $gold + $coins>><<unset $coins>><</if>>
    
  • Thank you everyone for your feedback, I've been testing through most of the suggestions and got positive results from them.

    I did, though, come to my own solution using a Javascript time system, but it's been giving me all sorts of troubles in itself

    Still, I'm looking for a solution and I'm hopeful I'll find one soon :)

    Thanks for all the suggestions, guys, I'll try some of the ones I wasn't successful with again!
  • edited May 2017
    mykael wrote: »
    One technique I use is to set a temporary variables on entry:
    Beyond that, your example seems somewhat unnecessary as written.

    It gets more interesting if you've got a bunch of checks and things that need to be done after every update to the variable. Consider:
    <<set $health to $health - $injury>>
    <<if $health lt 1>><<goto Dead>><<end>>
    <<if $health gt $max_health>><<set $health to $max_health>><<endif>>
    <<if $health lt 5>><<print "You are at deaths door...">><<endif>>
    

    With my scheme, the checks go into the entry widget and you set $injury to how many hit points they've lost - which can be done as a post link action. A negative value represents healing. Means that as you write the passages, you don't have to worry about all the checks...

    [[Pull the board free|Discovery][$text to "You grab the board and pull.<br><br>Ouch! You stabbed handwith a splinter!<br><br>";$injury to 5]]
    
  • That's a cool system, but it doesn't really fix the problem, you still either need to tie it to link text or use a conditional expression--I don't see how doing both is necessarily an improvement. It's a cool set up, don't get me wrong, but it's not really the issue at hand: your code is essentially equivalent to mine from my initial post--tying link text to action; if it's possible to click that link again, then it wont' work in some stories, though it will in a vast majority of them.
    <<goto Dead>>
    

    Don't take my word for it, but I'm pretty sure using unquoted/un-bracketed arguments is risky, so be careful. If your passage names have spaces, you can get into trouble, since the macros treat argument lists as space-separated. Some characters can get eaten up by the parser, too, though SugarCube seems like it's generally pretty good about that and I haven't had it happen to me, at least not in 2.x.
  • mykael wrote: »
    It gets more interesting if you've got a bunch of checks and things that need to be done after every update to the variable.
    Hence, why I said as written. Although, now that you've provided another example….

    mykael wrote: »
    Consider:
    <<set $health to $health - $injury>>
    <<if $health lt 1>><<goto Dead>><<end>>
    <<if $health gt $max_health>><<set $health to $max_health>><<endif>>
    <<if $health lt 5>><<print "You are at deaths door...">><<endif>>
    
    With my scheme, the checks go into the entry widget and you set $injury to how many hit points they've lost - which can be done as a post link action. A negative value represents healing. Means that as you write the passages, you don't have to worry about all the checks...
    [[Pull the board free|Discovery][$text to "You grab the board and pull.<br><br>Ouch! You stabbed handwith a splinter!<br><br>";$injury to 5]]
    
    That example still doesn't seem to need the intermediate variable. Visibly, you're not doing anything with $injury other than applying it to $health. Meaning you could simply modify $health as appropriate in the link and your widget should still work fine. For example: (I moved the injury to the front to make it easier to see)
    [[Pull the board free|Discovery][$health -= 5;$text to "You grab the board and pull.<br><br>Ouch! You stabbed handwith a splinter!<br><br>"]]
    
    That said. It is certainly possible that in your actual code you are doing something which makes an intermediate variable either necessary or, at the very least, useful/convenient, however, you really haven't shown that here.

    To be clear, I'm not trying to be difficult. I'm simply pointing out that you're doing something which seems unnecessary, based solely on the examples you've given.

    The reason why you don't want to create story variables that you don't need is because you bloat the story history for no good purpose—which is the entire reason the <<unset>> macro (in SugarCube v1 and v2) and temporary variables (in v2) exist. Bloating the history makes the running game/story use more resources and increases the size of saves. Now, I don't want to oversell that, it should not be an issue in most cases—though it can in SugarCube v1 for projects which last a significant number of moments/turns; v2's history was rewritten to be much more resilient to such issues.


    Also, as noted by Chapel. I would recommend against using barewords for passage names, since macro argument lists are whitespace separated. For example:
    → Equivalent (both a single argument)
    <<goto Dead>>
    <<goto "Dead">>
    
    → Not equivalent
    <<goto You're Dead>>    ← two separate arguments
    <<goto "You're Dead">>  ← one argument
    


    PS: In case it was actual code from your project, you have a mistake in the value you assign to $text in the link: You stabbed handwith a splinter!.
Sign In or Register to comment.