0 votes
by (130 points)

Hello,

I'd like to offer users the ability to spend a fixed budget. 

They might have £150 at the start and can buy items priced at £60, £80, £100 respectivly. That means they get to chose any single item or could take items 1 & 2.

I'd also like to prevent duplication so they can't buy two instances of item 1 for this scenario.

It would be good to present the cost of each item within the link but extracting cost of $posters etc doesn't seem to work in the context below.

I can get the passage to display a running total of the remaining budget but it doesn't update the (if ) statements to prevent that option being shown as available to purchase.

What am I missing?

(set: $budget to 150)
(set: $posters to (dm: "cost", 80, "Benefit", 200))
(set: $flyers to (dm: "cost", 60, "Benefit", 100))
(set: $socmed to (dm: "cost", 100, "Benefit", 300))
(set: $marketing to (a:))

You have a choice on how to spend your marketing budget of £|budgetStat>[$budget].

(if: $budget < (cost of $posters) or $marketing contains "posters") 
[Either already has or can't afford posters] (else:) [You can:
 (link: "Buy posters")[
      	(set: $budget to it - (cost of $posters))
		(set: $marketing to it + (a: "posters"))
		(replace: ?budgetStat)[|budgetStat>[$budget]]
		(replace: ?marketingStat)[|marketingStat>[$marketing]]
		]]

(if: $budget < (cost of $flyers) or $marketing contains "flyers") 
[Either already has or can't afford flyers] (else:) [You can:
 (link: "Buy flyers")[
      	(set: $budget to it - (cost of $flyers))
		(set: $marketing to it + (a: "flyers"))
		(replace: ?budgetStat)[|budgetStat>[$budget]]
		(replace: ?marketingStat)[|marketingStat>[$marketing]]
		]]
	
(if: $budget < (cost of $socmed) or $marketing contains "socmed") 
[Either already has or can't afford socmed] (else:) [You can:
 (link: "Buy socmed")[
      	(set: $budget to it - (cost of $socmed))
		(set: $marketing to it + (a: "socmed"))
		(replace: ?budgetStat)[|budgetStat>[$budget]]
		(replace: ?marketingStat)[|marketingStat>[$marketing]]
		]]
	
Your remaining budget is: £|budgetStat>[$budget] and your current marketing is |marketingStat>[$marketing].	

Thanks in advance.

1 Answer

+1 vote
by (159k points)

There are a couple of issues with your example:

1. You are including a copy of the named hook declaration within your (replace:) macro's associated hook, when all you should have in there is the replacement content.

<!-- Invalid -->
(replace: ?budgetStat)[|budgetStat>[$budget]]

<!-- Should be -->
(replace: ?budgetStat)[$budget]

2. You are adding invalid white-space or line-breaks between your macros and thier associated hooks, which the Harlowe TwineScript parser is automatically fixing for you. It's not a good idea to rely on it doing that correctly.

<!-- Invalid line-break placement. -->
(if: $budget < (cost of $posters) or $marketing contains "posters") 
[ ... ]

<!-- Invalid white-space placement -->
(else:) [ ... ]

<!-- The above should be.. -->
(if: $budget < (cost of $posters) or $marketing contains "posters")[ ... ]

(else:)[ ... ]


Background info:

a. The non-interative content of a Passage is procressed just before that passage is shown, after which it is ignored until the next time the passage is shown again.

b. Dynamically changing the value of a variable referenced in the conditional expressions of a (if:) macro (after the passage has been shown) does not cause that macro to be re-evaluated again.

This means the conditional expressions of your (if:) macros are only being evaluated at the time the passage was shown. If you want them to be re-evaluated then you need to dynamically add them to the passage again, and one method you can use to do this is to move that content to a child passage and then inject that child passage into the parent each time you want it to be executed.

The following is a modified version of your original example, it includes fixes to the above issues as well as demostrates how to dynamically executed content as needed.

You have a choice on how to spend your marketing budget of £|budgetStat>[$budget].

|options>[(display: "Marketing Options")]
	
Your remaining budget is: £|budgetStat>[$budget] and your current marketing is |marketingStat>[$marketing].	

... you will notice I have added a new options named hook to contain the contents of a new child passage.

The new Marketing Options child passage contains all the condition options content.

{
Posters £(print: cost of $posters):
(if: $budget < (cost of $posters) or $marketing contains "posters")[
	You either already have or can't afford this item.
]
(else:)[
	(link: "Buy")[
		(set: $budget to it - (cost of $posters))
		(set: $marketing to it + (a: "posters"))
		(replace: ?budgetStat)[$budget]
		(replace: ?marketingStat)[$marketing]
		(replace: ?options)[(display: "Marketing Options")]
	]
]
<br><br>
Flyers £(print: cost of $flyers):
(if: $budget < (cost of $flyers) or $marketing contains "flyers")[
	You either already have or can't afford this item.
]
(else:)[
	(link: "Buy")[
		(set: $budget to it - (cost of $flyers))
		(set: $marketing to it + (a: "flyers"))
		(replace: ?budgetStat)[$budget]
		(replace: ?marketingStat)[$marketing]
		(replace: ?options)[(display: "Marketing Options")]
	]
]
<br><br>
Socmed £(print: cost of $socmed):
(if: $budget < (cost of $socmed) or $marketing contains "socmed")[
	You either already have or can't afford this item.
]
(else:)[
	(link: "Buy")[
		(set: $budget to it - (cost of $socmed))
		(set: $marketing to it + (a: "socmed"))
		(replace: ?budgetStat)[|budgetStat>[$budget]]
		(replace: ?marketingStat)[$marketing]
		(replace: ?options)[(display: "Marketing Options")]
	]
]
}

... the above uses:
1. Collapsing Whitespace markup combined with HTML line-breaks (br elements) to control the layout of the options.
2. A (replace: ?options)[(display: "Marketing Options")] call to cause the options to be re-displayed, which in turn causes the (if:) macros to be re-evaluated.

I also restructed and re-worded your original text, and added the cost of the items so that the reader has some idea about the ramifications of their selections.

by (130 points)
Briliiant, thank you for that.
...