Conditional Header Problem

Twine Version: 2.3.16
Story Format: Harlowe 3.2.3
I have this code in my page with a header tag.

{(if:$saddlebag is true)[(show:?bag)]
(elseif:$hashand is true)[(unless:$saddlebag is true)[(show:?hand)]]
(else:)[ ]}

All of the ‘show:’ macros work like they’re supposed to, but the “if:” and “elseif:” macros show up even if they’re explicitly set to false. Is this something a ‘cond:’ macro would fix? Is there an overall issue with ‘if:’ macros in headers?

If knowing what the rest of that page looks like is helpful, here it is:

(replace:?sidebar)[♦]
{(if:$saddlebag is true)[(show:?bag)]
(elseif:$hashand is true)[(unless:$saddlebag is true)[(show:?hand)]]
(else:)[ ]}
[(append:?sidebar)[Bag]]<bag|
[(append:?sidebar)[&#9830;Hands]]<hand|
(click:'Bag')[(go-to:'Bag')]
(click:'Hands')[(go-to:'Hands')]

I’m not a conditional statement expert, but I do think there’s something a little off with the execution here. I recommend that you start by reading over the official Harlowe documentation under the The (else-if: ) macro section.

Typically if you’re using a else-if, you’re making another check against the same variable as the main if statement. I believe you’d simply want a new if statement. Another issue is that you’re also using a unless statement inside the elseif statement.

I would simply create a if statement that included both variables. Example:

{(if:$saddlebag is true)[(show:?bag)]
(if:$hashand is true and $saddlebag is false)[(show:?hand)]}

I’m also not sure why you would have an else statement at the end as it’s blank so if the other statements don’t display anything, this last part would be unnecessary?

First a little background…

1: Checking for Boolean true and/or false.

You shouldn’t use the is comparison operator when determining if a Boolean variable equals true or false (eg; is true or is false). The correct way to do those two comparisons is…

<!-- Checking for true -->
(if: $variable)[Seen if variable current equals true]

<!-- Checking for false -->
(if: not $variable)[Seen if variable current equals false]
or
(unless: $variable)[Seen if variable current equals false]

<!-- Checking for both true and false -->
(if: $variable)[Seen if variable current equals true]
(else:)[Seen if variable current equals false]

2: Evaluation of an (if:) plus (else-if:) plus (else:) macro sequence.

The conditional expression of each macro is evaluated in 1st to last order, evaluation of expressions stops once one of them evaluates as true, so for a specific expression to be evaluated all the previous expressions must of been false.

eg. In your example…

	(if: $saddlebag is true)[
		(show: ?bag)
	]
	(elseif: $hashand is true)[
		(unless: $saddlebag is true)[
			(show:?hand)
		]
	]
	(else:)[ ]

… the $hashand variable related conditional expression would only be evaluated if the $saddlebag variable equals false, so there is no need for the (unless:) macro call.

3: Using the join operators (and, or) to group individual conditional expressions together.

Instead of using an (else-if:) macro with an (unless:) macro within its associated Hook…

(elseif: $hashand is true)[
		(unless: $saddlebag is true)[
			(show:?hand)
		]
	]

… you could of joined the two condition expressions together to achieve the same outcome…

(elseif: $hashand and not $saddlebag)[
	(show:?hand)
]

…but because of the above point 2, the check to see if $saddlebag variable current equals false is not needed.

4: Conditional Macro call with an empty Hook.

Code like (else:)[ ] is generally a sign that there is a logic issue with the related code structure.

5: The (click:) family of macros vs the (link:) family.

Generally whenever possible you should use one of the (link:) family of macros over those of the (click:) family, as those of the (click:) family do a lot more than just create a link.

Each time you call one of the (click:) macro it adds an Action to a queue that gets handled once the processing of the current Passage has finished. When that Action is executed it scans the entire contents of the story’s page looking for every instance of that macro call’s target, and replaces it with a “link”. And that Action will be executed again each time the contents of the current page is dynamically updated using macros like (append:) & (replace) and any of the (live:) family of macros.

Taking all of the above into consideration a better variation of your example would looks something like the following…

(replace: ?sidebar)[&#9830;]
{
	(if: $saddlebag)[
		(append: ?sidebar)[
			(link-goto: "Bag", "Bag")
		]
	]
	(elseif: $hashand)[
		(append: ?sidebar)[
			(link-goto: "Hands", "Hands")
		]
	]
}
1 Like