0 votes
by (870 points)

So I'm trying to make an RPG in Twine.

I made a reaper function to remove expired status effects, to be run in PassageDone. To avoid having to write the same thing twice, I wanted to pass whether it ran through the enemy or ally party as an argument to a widget.

This is the reaper widget:

<<for _i to 0; _i < $args[0].length; _i++>>
 <<for _j to 0; _j < $args[0][_i].effects.length; _j++>>
  <<if $args[0][_i].effects[_j].duration eq 0>>
  <<script>
  	state.active.variables.args[0][state.active.variables.i].effects[state.active.variables.j].splice(state.active.variables.j, 1);
  <</script>>
 <</for>>
<</for>>

(Tangential question: can array functions be used with <<set>>? I tried it before and it didn't seem to work, but I could have been mistaken.)

This is my PassageDone content:

<<if def $party>>
<<effectmanager "$party">>
<</if>>
<<if def $enemies>>
<<effectmanager "$enemies">>
<</if>>

It is my understanding that passing the variable name in quotes like this passes the variable itself to the widget and not the value. Is that correct?

When I start the story, a popup error tells me the for loop is referring to an undefined object, even though the if statement should prevent it being called in that case. Even stranger, it keeps throwing the same error even when the $party and $enemies arrays are fully defined.

I have tried using the Stupid Print Trick (with the <<print>> encompassing the entire code), but it tells me the <<print>> macro has "bad expression: Invalid or unexpected token".

I can see it being possible that I'm not passing the variable to the widget correctly, but I'm completely baffled why the <<if def>> isn't working.

1 Answer

+1 vote
by (68.6k points)
selected by
 
Best answer

In the future, if you're going to show a widget, please show the entire widget (incl. the <<widget>> bits), not simply some chunk of its contents, how you're calling it, and what you're calling it with.  Making us guess about some or all of these things is not conducive to getting you the help you want.

 

  Tangential question: can array functions be used with <<set>>?

Yes.

 

It is my understanding that passing the variable name in quotes like this passes the variable itself to the widget and not the value. Is that correct?

Partially, however, there's more to it than that.  That passes the name, which may then be used to assign a new value to the variable, though not directly (see: State.getVar() and State.setVar()).  If you're not attempting to assign a value to it, however, then doing so is unnecessary.  In your case, you simply seem to be attempting to modify the existing array and object members of said array.

 

When I start the story, a popup error tells me the for loop is referring to an undefined object, even though the if statement should prevent it being called in that case. Even stranger, it keeps throwing the same error even when the $party and $enemies arrays are fully defined.

Because you're attempting to operate on the variable's name, a string.

 

Anyway.  Moving on to your shown code example.  You have several issues:

  1. You're passing the name of the variable, rather than its value, and attempting to use the name directly, rather than using it to pull the value.  As noted above, for your use case, you should simply be able to pass the variable as normal.
  2. You're using <<script>> when there's no need.
  3. You're using v1 APIs in v2 (i.e. state.active.variables vs. State.variables).
  4. You're attempting to access temporary variables as though they were story variables (i.e. […].variables.i vs. […].temporary.i).
  5. You seem to be attempting to remove the effect if the duration is 0, but you're splicing the effect itself, rather than the effects array.

Try something like the following: (using <<for…range>> and the <Array>.deleteAt() method for readability)

<<silently>>
	<<for _member range $args[0]>>
		<<for _j, _effect range _member.effects>>
			<<if _effect.duration is 0>>
				<<run _member.effects.deleteAt(_j)>>
			<</if>>
		<</for>>
	<</for>>
<</silently>>

I wrapped it within a <<silently>> because you don't need output to be generated by this thing (not that it matters much if you only call in within PassageDone, but best practices).

Also.  As noted above, try calling the widget thus:

<<if def $party>>
	<<effectmanager $party>>
<</if>>
<<if def $enemies>>
	<<effectmanager $enemies>>
<</if>>

 

by (870 points)
Oh, I see. I misunderstood how the Stupid Print Trick worked. I implemented your changes and it works like a charm. Thanks!
by (68.6k points)
The Stupid Print Trick(tm) was mostly only necessary to force early evaluation of (usually loop) variables when used with asynchronous code (e.g. the contents of a <<link>> macro, because they're only evaluated when the player activates the link).  A usage that has been supplanted by the <<capture>> macro.
...