Howdy, Stranger!

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

Harlowe & Conditional Headers

edited January 2016 in Help! with 2.0
Hey,

I have a link to my stats page in my header, and a return button on my stats page to go to the last page. However, the problem is that if you click Stats in the header twice, the return doesn't work as it just takes you back to the stats page.

I'm looking for a way to disable the "stats" button when you're on the stats page, but the only thing that happens is that it makes my header completely blank. Otherwise, a way to disable the header or not add the stats page to the history would also work I think.

Here is the code for my header:
{
(if: $stats is false)[[[Stats|Stats]]]
(else-if: $stats is true)[Stats]
}

And here is the code for my stats page:
{
(set: $return to "(link-goto: \"Return\", (history:)'s last)")
(set: $stats to true)
}

All other pages I have this at the top
{
(set: $stats to false)
}

Comments

  • Nevermind, I fixed here. Here's how for anybody else struggling:

    First page of my story's code:
    {
    (set: $stats to false)
    }
    

    Header's code:
    {
    (if: $stats is false)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    (else-if: $stats is true)[**Stats**]
    }
    

    Stats page code:
    {
    (set: $return to "(link-goto: \"Return\", (history:)'s last)")
    (set: $stats to false)
    }
    

    I figured out why my previous setup didn't work: $stats wasn't set to false until you clicked a button, so the header was missing from my first page. Then, when you went into the Stats menu, the $stats didn't set to true until you actually clicked Return or the Stats button again.

    I fixed the issue by making the actual mouse button click on the Stats button set it to $stats true, and making the $return set $stats back to false.
  • edited January 2016
    Chalk wrote: »
    Header's code:
    {
    (if: $stats is false)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    (else-if: $stats is true)[**Stats**]
    }
    
    Your $stats variable is a Boolean so you don't need to check for both true and false.
    Try either of the following, they produce the same outcome:
    {
    (if: not $stats)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    (else:)[**Stats**]
    }
    
    ... or:
    {
    (if: $stats)[**Stats**]
    (else:)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    }
    
  • greyelf wrote: »
    Chalk wrote: »
    Header's code:
    {
    (if: $stats is false)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    (else-if: $stats is true)[**Stats**]
    }
    
    Your $stats variable is a Boolean so you don't need to check for both true and false.
    Try either of the following, they produce the same outcome:
    {
    (if: not $stats)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    (else:)[**Stats**]
    }
    
    ... or:
    {
    (if: $stats)[**Stats**]
    (else:)[(link: "Stats")[(set: $stats to true)(goto: "Stats")]]
    }
    

    Is there a way to get around the "(history:)'s last" limitation? E.g. If somebody presses Stats and then Inventory, pressing back would only take them back to Stats and not to the proper passage they were at. My current work around is to disable both buttons when you're in either page, but I'd like to enable them again.
  • You can use a Harlowe equivalent to the SugarCube passage tag/prerend technique.

    1. Assign each of your menu related passages (Stats, Inventory, etc) a known tag, this example will use menu.

    2. Create a variable in your startup tagged passage to track the name of the last non menu tagged passage shown to the reader. This example is using a variable named $last
    (set: $last to "")
    
    3. Create a header tagged passage which is going to be used to update the $last variable with the name of the current passage if it is not tagged with menu.
    The name of the header tagged passage can be anything you want but you will need to remember the name because it will be used in a CSS selector later. This example is naming the header tagged passage Passage Tracker and it contains the following code:
    (if: not ((passage:)'s tags contains "menu"))[
    	(set: $last to (passage:)'s name)
    ]
    
    4. Add a Back link to each of the menu tagged passages which uses the $last variable, the Link Text of the link can be something other than Back.
    (link-goto: "Back", $last)
    
    5. Use CSS to hide the contents of the header tagged passage created in step 3, the CSS selector needs to contain the name (title) of the passage. If you named your passage something other than Passage Tracker then you need to change the title part of the CSS selector.
    tw-include[type="header"][title="Passage Tracker"] {
    	display: none;
    }
    
  • edited January 2016
    Thanks a lot! It works perfectly. One final question: I realised something really silly, if you go into the stats or inventory menu and then go back, anything I used (set:) for triggers again. You can use this exploit to get an unlimited amount of stats. I'm currently thinking the best solution would be having a boolean to only allow each (set:) to happen once, but if there's a simpler way to go about doing that it'd be very helpful.

    E.g.
    {
    (set: $DEX to $DEX + 10)
    (set: $INT to $INT + 10)
    (set: $job to "S&R Medic")
    (set: $canshoot to true)
    (set: $canheal to true)
    }
    

    If you go into the Stats menu and then back out, (set: $DEX to $DEX + 10) happens twice.

    This is what I'm thinking of doing:
    {
    (if: $passagename is 0)[
    (set: $DEX to $DEX + 10)
    (set: $INT to $INT + 10)
    (set: $job to "S&R Medic")
    (set: $canshoot to true)
    (set: $canheal to true)
    ](set: $passagename to true)
    }
    

    That is a lot of code I have to go back and change if I'm going to use this method, but it's the only solution I can think of.
  • Why are you changing variables in a Passage designed to display the
    current
    
    value of those variables?

    Your solution is checking to see if the $passagename variable is equal to a number and then assigning a boolean value to it, thus changing the data type of the variable. This is generally not a good idea.
  • edited January 2016
    greyelf wrote: »
    Why are you changing variables in a Passage designed to display the
    current
    
    value of those variables?

    Your solution is checking to see if the $passagename variable is equal to a number and then assigning a boolean value to it, thus changing the data type of the variable. This is generally not a good idea.

    I didn't mean it changes in the stats page, I meant it changes on any other page that has a (set:) function.

    Also the reason I have $passagename set to a number and not to false is because otherwise I would need to define $passagename as false for every single passage I have, whereas the first time it encounters it it's default value is 0 (since I haven't defined it yet, and haven't given it a true or false value).

    For example, say that the following code is for a passage called "ExitBar", I'd use this code:
    {
    (if: $ExitBarVisited is 0)[
    (set: $DEX to $DEX + 10)
    (set: $INT to $INT + 10)
    (set: $job to "S&R Medic")
    (set: $canshoot to true)
    (set: $canheal to true)
    ](set: $ExitBarVisited to true)
    }
    

    I can't think of another way to make sure (set:) functions don't trigger twice, and I really don't want to define every single passage name as false at the beginning of my game.

    Edit: I just had somewhat of an epiphany: would it be possible to use (history)'s last and a menu tag to make sure that (set:) functions don't trigger more than once? Something like:
    {
    (if: not ((history)'s last tags contains "menu"))[
    (set: $DEX to $DEX + 10)
    (set: $INT to $INT + 10)
    (set: $job to "S&R Medic")
    (set: $canshoot to true)
    (set: $canheal to true)
    ]
    }
    

    I'm currently heading to bed so I don't know if this code will work or not, but I'll test it in the morning/wait for feedback (whichever comes first), and I'm not sure if (if: not ((history)'s last tags contains "menu")) is a valid argument.
  • Chalk wrote: »
    Also the reason I have $passagename set to a number and not to false is because otherwise I would need to define $passagename as false for every single passage I have, whereas the first time it encounters it it's default value is 0 (since I haven't defined it yet, and haven't given it a true or false value).
    It is a good idea to assign default values to your story's variables within a startup tagged passage, in your case you could do the following:
    (set: $passagename to false)
    
  • Edit: I just had somewhat of an epiphany: would it be possible to use (history)'s last and a menu tag to make sure that (set:) functions don't trigger more than once? Something like:

    {
    (if: not ((history)'s last tags contains "menu"))[
    (set: $DEX to $DEX + 10)
    (set: $INT to $INT + 10)
    (set: $job to "S&R Medic")
    (set: $canshoot to true)
    (set: $canheal to true)
    ]
    }

    The code you've got there won't work since (history:)'s last just returns the name of the last passage, not a datamap (plus there's some punctuation missing). A working version would be
    (if: not ((passage: (history:)'s last)'s tags contains "menu"))[...]
    

    Note this code won't work on the very first passage, when (history:) is empty.

    Though a simpler way might be to just check the history to see if the player has visited the passage before.
    (if: not ((history:)  contains (passage:)'s name))[...]
    
  • greyelf wrote: »
    Chalk wrote: »
    Also the reason I have $passagename set to a number and not to false is because otherwise I would need to define $passagename as false for every single passage I have, whereas the first time it encounters it it's default value is 0 (since I haven't defined it yet, and haven't given it a true or false value).
    It is a good idea to assign default values to your story's variables within a startup tagged passage, in your case you could do the following:
    (set: $passagename to false)
    

    That would mean I have to assign a default value to every single passage though, which would end up being hundreds of lines of code in the end. Just the character creation and backend passages alone are over 50 passages long, and I'm only about halfway done with it.
    Edit: I just had somewhat of an epiphany: would it be possible to use (history)'s last and a menu tag to make sure that (set:) functions don't trigger more than once? Something like:

    {
    (if: not ((history)'s last tags contains "menu"))[
    (set: $DEX to $DEX + 10)
    (set: $INT to $INT + 10)
    (set: $job to "S&R Medic")
    (set: $canshoot to true)
    (set: $canheal to true)
    ]
    }

    The code you've got there won't work since (history:)'s last just returns the name of the last passage, not a datamap (plus there's some punctuation missing). A working version would be
    (if: not ((passage: (history:)'s last)'s tags contains "menu"))[...]
    

    Note this code won't work on the very first passage, when (history:) is empty.

    Though a simpler way might be to just check the history to see if the player has visited the passage before.
    (if: not ((history:)  contains (passage:)'s name))[...]
    

    Thanks, this is very helpful!
Sign In or Register to comment.