Using Loops in Twine

Twine Version: 2.8.1
Harlowe 3.3.8

Hi all - new to Twine and trying to get my head around loops.

I’m creating a test story and want to use a loop (or whatever is best) to run a simple combat scenario.

For each round, scores are calculated using a variable and a random number (mimicking a dice roll) and then subtracting some Stamina depending on whether you or the bad guy win. If either your or the bad guy’s Stamina reaches zero, then game over.

The variables mentioned at the start of the code below are set earlier on in the game; I’ve tried various things to get this to work but it falls over - the code is me trying to write out what I want to happen, but I’m obviously failing at the syntax.

Any help gratefully received!

You have a Skill of (print: $CurrentSkill) and a Stamina of (print: $CurrentStamina)

The Orc has a Skill of (print: $West01OrcSkill) and a Stamina of (print: $West01OrcStamina).

(set: _round to 1)

(for: each _round)
Round: (print: _round)
(set: _MyAttackStrength to $CurrentSkill + (random: 2,12))(print: _MyAttackStrength)
(set: _West01OrcAS to $West01OrcSkill + (random: 2,12))(print: _West01OrcAS)

(if: _MyAttackStrength > _West01OrcAS)[You strike the Orc!](set: $West01OrcStamina to $West01OrcStamina -2)(if: $West01OrcStamina < 1, "You win!")
(if: _MyAttackStrength = _West01OrcAS)[You are evenly matched and must fight again!]
(if: _MyAttackStrength < _West01OrcAS)[The Orc strikes you!](set: $CurrentStamina to $CurrentStamina -2)(if: $CurrentStamina < 1, "The orc has won - you're dead!!")(set: _round to _round + 1)
2 Likes

With macros like (for:) and (if:) an associated Hook is used to contain the content that should be executed/processed when the macro is active (or it’s condition is true).
eg.

(for: each _item, ...$list)[ ... process this content for each item in the Array ... ]

or

(if: $hasLamp)[... process this when the condition is true ...]

I believe the following code in your example…

(if: _MyAttackStrength > _West01OrcAS)[You strike the Orc!](set: $West01OrcStamina to $West01OrcStamina -2)(if: $West01OrcStamina < 1, "You win!")

…should actually be structured like…

(if: _MyAttackStrength > _West01OrcAS)[\
    You strike the Orc!\
    (set: $West01OrcStamina to it - 2)
    (if: $West01OrcStamina < 1)[You win!]
]

Note: Harlowe doesn’t support using a single equals-sign = when doing an equality comparision. The correct operator to use is is

(if: _MyAttackStrength is _West01OrcAS)[You are evenly matched and must fight again!]

And the following code…

(if: _MyAttackStrength < _West01OrcAS)[The Orc strikes you!]\
(set: $CurrentStamina to $CurrentStamina -2)\
(if: $CurrentStamina < 1, "The orc has won - you're dead!!")\

…has the same lack of Hook structural issues as the 1st example…

(if: _MyAttackStrength < _West01OrcAS)[\
    The Orc strikes you!\
    (set: $CurrentStamina to it - 2)
    (if: $CurrentStamina < 1)[The orc has won - you're dead!!]
]

Harlowe doesn’t have an equivalent of the Repeat-Until or the Do-While types of loops, but there are ways to emulate them. And the (for:) macro based loop expects to know in advance how many iterations it will be doing, which it determines by the number of arguments being passed to it by the ... spread operator. eg. the length of the Array.

One such emulation consists of combining a Named Hook with the (rerun:) macro.

|log>[]

(set: _round to 0)
|combat>[{
	(set: _round to it + 1)
	(append: ?log)[<br>Round: _round]
	
	(set: $MyAttackStrength to it + (random: 2, 12))
	(set: $West01OrcAS to it + (random: 2, 12))
	(append: ?log)[<br>$MyAttackStrength<br>$West01OrcAS]
	
	(if: $MyAttackStrength is $West01OrcAS)[
		(append: ?log)[<br>You are evenly matched and must fight again!]
		(rerun: ?combat)
	]
	
	(else-if: $MyAttackStrength > $West01OrcAS)[
		(append: ?log)[<br>You strike the Orc!]
		(set: $West01OrcStamina to it - 2)
		(if: $West01OrcStamina < 1)[
			(append: ?log)[<br>You win!]
		]
		(else:)[
			(rerun: ?combat)
		]
	]
	
	(else:)[
	 	(append: ?log)[<br>The Orc strikes you!]
		(set: $CurrentStamina to it - 2)
		(if: $CurrentStamina < 1)[
			(append: ?log)[The orc has won - you're dead!!]
		]
		(else:)[
			(rerun: ?combat)
		]
	]
}]

Warning: Harlowe doesn’t like doing a large number of iterations in a loop, so the above will actually start preducing related error messages when the number of iterations reach a hard-wired limit.

1 Like

Thanks - there’s a lot there for me to work through, but I appreciate the help.

I’m new to this and coming from an Excel background so struggling to get my head out of the formula/VBA space!

Harlowe doesn’t have an equivalent of the Repeat-Until or the Do-While types of loops, but there are ways to emulate them.

Thanks - that’s really useful to know.

1 Like