Howdy, Stranger!

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

Problem with widget in SC 2.6.2

Good evening!

I am working with Twine 2.0.11 using SugarCube 2.6.2. I am having trouble with a widget I wrote. The widget should compare user inputs with given variables and dynamically link to the next passage:
<<widget "evaluate">>
    <<set $level to $args[0]>>
    <<set $rightAnswer to $args[1]>>
    <<set $userAnswer to $args[2]>>
    <<set $focus to $args[3]>>
    
	<<if $userAnswer eq "">>
		<<<<print "Please answer question!">>
    <<elseif $userAnswer eq $rightAnswer>>
        <<set $level +=1>>    
        <<set $nextPassage = $level + "x" + $round >>
        <<goto $nextPassage>>
    <<else>>
        <<set $level -=1>>    
        <<set $nextpassage = $level + "x" + $round >>
        <<goto $nextPassage>>
    <</if>>
<</widget>>

I use the widget for instance from within the following passage:
<<set $level to 1>>
<<set $rightAnswer to "Option A">>
<<set $focus to "Partizipien">>

Choose the right option!

<<radiobutton "$userAnswer" "Option A">>Option A
<<radiobutton "$userAnswer" "Option B">>Option B
<<radiobutton "$userAnswer" "Option C">>Option C


<<button "next round">>
    <<evaluate $level $rightAnswer $userAnswer $focus>>
<</button>>

I get the following error messages:
Error [tw-user-script-0]: Unexpected token <<.

and

Error: <<evaluate>>: error within widget contents (Error: cannot execute macro <<goto>>: Story.has title parameter cannot be undefined).


What am I doing wrong?

Thanks,
richVIE

Comments

  • edited June 2016
    <<<<print "Please answer question!">>

    Only need 2 <<, not 4.

    This is causing the first error message and then the widget is not defining $useranswer properly as a follow on of confusing it with the extra <<, causing the second error.

    If you get an unexpected token error it means you've put the extra stuff it says in somewhere and should first remove the offending characters.
  • What Claretta said. You have too many opening angle brackets.

    Beyond that, you don't actually need to use <<print>> there at all. You could write that bit like the following:
        <<if $userAnswer eq "">>
            Please answer question!
        <<elseif $userAnswer eq $rightAnswer>>
    
  • Thank you so much, Claretta and TheMadExile, that sure was stupid. Oddly, after eliminating the extra <<, the problem persists. Is there some kind of cache I need to empty?

    Anyways - I expanded my widget and encountered more problems. Could you guys give me a push in the right direction? Here is what my widget is supposed to do:

    1. Check, whether $userAnswer matches $rightanswer.
    2. If yes, add "true" to the $answerLog array.
    3. If no, add "false" to the $answerLog array.
    4. Check the last two elements of $answerLog: if both of them are true, increment $level, if both of them are false, decrement $level.
    5. Set $nextpassage to $round+"x"+$level (passage names are ie. 1x2, 1x3, 2x1, 2x4,...
    6. go to $nextpassage.

    My widget now reads as follows
    <<widget "evaluate">>
        <<set $level to $args[0]>>
        <<set $rightAnswer to $args[1]>>
        <<set $userAnswer to $args[2]>>
        <<set $focus to $args[3]>>
        <<set $round +=1>>
    	
    	
    	<<if $userAnswer eq "">>
    		Please answer question!
        <<elseif $userAnswer eq $rightAnswer>>
            <<set $answerLog.push("true")>>
        <<else>>
            <<set $answerLog.push("false")>>  
        <</if>>
    	
    	<<set $index to $answerLog.length>> 
    	<<if $answerLog[$index] eq true && $answerLog[$index-1] eq true>>
    		<<set $level to $level+1>>
    			<<if $level gt 4>>
    				<<set $level to 4>>
    		    <</if>>
    	<<elseif $answerLog[$index] eq false && $answerLog[$index-1] eq false>>
    		<<set $level to $level-1>>
    			<<if $level lt 0>>
    				<<set $level to 0>>
    		    <</if>>
    	<</if>>
    
    	<<set $nextpassage = $level + "x" + $round >>
    	<<goto $nextpassage>>
    	
    <</widget>>
    

    There must be something wrong adding elements to $answerlog.

  • Issues:
    • You're accessing an out-of-bounds index on $answerLog. Since arrays are 0-based, the last index of an array is its length - 1, not length.
    • You're pushing strings (i.e. "true" and "false") into $answerLog, then comparing those values to the boolean literals (i.e. true and false). Do not do that—either use strings or booleans, not both. I've used booleans in the following example.
    • You're generating a lot of excess whitespace. I've addressed some of that in the following example.

    Try something like the following:
    <<widget "evaluate">>
    	<<silently>>
    	<<set $level to $args[0]>>
    	<<set $rightAnswer to $args[1]>>
    	<<set $userAnswer to $args[2]>>
    	<<set $focus to $args[3]>>
    	<<set $round += 1>>
    	<</silently>>\
    	<<nobr>>
    	<<if $userAnswer eq "">>
    		Please answer question!
    	<<elseif $userAnswer eq $rightAnswer>>
    		<<set $answerLog.push(true)>>
    	<<else>>
    		<<set $answerLog.push(false)>>
    	<</if>>
    	<</nobr>>\
    	<<silently>>
    	<<set $lastIndex to $answerLog.length - 1>> 
    	<<if $answerLog[$lastIndex] and $answerLog[$lastIndex - 1]>>
    		<<set $level to $level + 1>>
    		<<if $level gt 4>>
    			<<set $level to 4>>
    		<</if>>
    	<<elseif not $answerLog[$lastIndex] and not $answerLog[$lastIndex - 1]>>
    		<<set $level to $level - 1>>
    		<<if $level lt 0>>
    			<<set $level to 0>>
    		<</if>>
    	<</if>>
    	<<set $nextpassage to $level + "x" + $round>>
    	<<goto $nextpassage>>
    	<</silently>>\
    <</widget>>
    
  • Wow, thanks, TheMadExile. Works much better now, thank you for the detailled explanation of my mistakes. There is still one thing that doesn't work: the widget doesn't check whether the user input is empty (line 10). Or perhaps it does, prints "Please answer question!" but then evaluates the answer as wrong and continues as usual.
    So I thought the correct thing to do would be to pack everything into and <<if>> macro, which won't <<goto>> if the user input is"".
    <<widget "evaluate">>
    	<<silently>>
    	<<set $level to $args[0]>>
    	<<set $rightAnswer to $args[1]>>
    	<<set $userAnswer to $args[2]>>
    	<<set $focus to $args[3]>>
    	<<set $round += 1>>
    	<</silently>>\
    	<<nobr>>
    	<<if $userAnswer eq "">>
    		Please answer question!
    	<<else>>
    		<<if $userAnswer eq $rightAnswer>>
    			<<set $answerLog.push(true)>>
    		<<else>>
    			<<set $answerLog.push(false)>>
    		<</if>>
    		<</nobr>>\
    		<<silently>>
    		<<set $lastIndex to $answerLog.length - 1>> 
    		<<if $answerLog[$lastIndex] and $answerLog[$lastIndex - 1]>>
    			<<set $level to $level + 1>>
    				<<if $level gt 4>>
    					<<set $level to 4>>
    				<</if>>
    		<<elseif not $answerLog[$lastIndex] and not $answerLog[$lastIndex - 1]>>
    			<<set $level to $level - 1>>
    				<<if $level lt 0>>
    					<<set $level to 0>>
    				<</if>>
    		<</if>>
    	        <<set $nextpassage to $level + "x" + $round>>
    	        <<goto $nextpassage>>
    	        <</silently>>\
    	<</if>>
    <</widget>>
    
    Apparently, one or some of the <<if>>-macros aren't properly closed, can you spot the error?
  • edited June 2016
    I can't see it but if you copy macros into Twine 1 with sugarcube installed it will tell you, since it has macro parsing.
  • edited June 2016
    Actually what is happening is your use of silently in the middle of the first <<if>> macro is breaking it up.

    You can't split macros with silently. You'll note that TheMadExile's example doesn't do this. He didn't put a global if around a silently macro.

    Remove line spaces by condensing the lines to all be on the same line instead.
  • @Claretta: Actually, they split the <<nobr>> macro—you may have gotten confused by the two <<silently>> macros.

    @richVIE: Yes, sorry. Here's a version with the nested <<if>> macros and the <<nobr>> removed—since it's not really necessary now:
    <<widget "evaluate">>
    	<<silently>>
    	<<set $level to $args[0]>>
    	<<set $rightAnswer to $args[1]>>
    	<<set $userAnswer to $args[2]>>
    	<<set $focus to $args[3]>>
    	<<set $round += 1>>
    	<</silently>>\
    	<<if $userAnswer eq "">>\
    		Please answer question!\
    	<<else>>\
    		<<silently>>
    		<<if $userAnswer eq $rightAnswer>>
    			<<set $answerLog.push(true)>>
    		<<else>>
    			<<set $answerLog.push(false)>>
    		<</if>>
    		<<set $lastIndex to $answerLog.length - 1>> 
    		<<if $answerLog[$lastIndex] and $answerLog[$lastIndex - 1]>>
    			<<set $level to $level + 1>>
    			<<if $level gt 4>>
    				<<set $level to 4>>
    			<</if>>
    		<<elseif not $answerLog[$lastIndex] and not $answerLog[$lastIndex - 1]>>
    			<<set $level to $level - 1>>
    			<<if $level lt 0>>
    				<<set $level to 0>>
    			<</if>>
    		<</if>>
    		<<set $nextpassage to $level + "x" + $round>>
    		<<goto $nextpassage>>
    		<</silently>>\
    	<</if>>\
    <</widget>>
    
    n.b. One other suggestion is to change any story variable inside the widget which only exists to hold a temporary value into a temporary variable. For example, say that $lastIndex is not used outside of the widget. In that case, it would be better to make it a temporary variable via changing all instances of its sigil from $ to _ (i.e. dollar sign to underscore; e.g. $lastIndex to _lastIndex). Temporary variables are not added to the story variable store and do not live past the passage in which they're used. By not using a story variable to hold temporary values, you save yourself some history bloat.
  • Thank you both so much! Can I bug you with one last thing? I still get the
    Error [tw-user-script-0]: Unexpected token <<.
    
    error each time I start the Twine. For some odd reason, at the first click, I get the error and the preview window closes, but every other time, I can click OK and play without problems.
    I have one passage tagged "widget", the only thing inside is TheMadExile's widget from the last post, so the problem can't be there.
    My starting passage looks like this, I can't spot an error there either:
    <<set $round = 0>>
    <<set $answerLog = []>>
    <<set $path = []>>
    <<set $problems = []>>
    
    At which level do you wish to begin?
    
    <<radiobutton "$level" 0>>1
    <<radiobutton "$level" 1>>2
    <<radiobutton "$level" 2>>3
    <<radiobutton "$level" 3>>4
    <<radiobutton "$level" 4>>5
    
    <<button "let's go!">>
    	<<set $round +=1>>
    	<<set $nextpassage = $level + "x" + $round>>
    	<<goto $nextpassage>>
    <</button>>
    
    Can you?
  • edited June 2016
    The problem is not there. You should check inside StoryInit and any other passages that are accessed on start.
  • Based on the error, check your project's Story JavaScript. You likely have something in there that you should not.
  • That was it! Thank you both very much!
  • Hey everybody,

    sorry, me again. I had time for some extensive testing and noticed, that the widget still doesn't react properly to an empty user input:
    <<if $userAnswer eq "">>\
    		Please answer question!\
    	<<else>>\
    

    does not print anything.

    So my idea was that if you pass an empty variable to the widget, it would think it got only three arguments (instead of four) and therefore wouldn't work.
    So I changed the code to
    <<if $args.length != 4>>\
    		Please answer question!
    	<<else>>\
    
    but it doesn't work either. Do you have any suggestions on how to prevent empty user inputs?
    An additional issue is, that the rest of the widget continues working fine, so each time it is triggered, it evaluetes the user input as false, decrements the $level counter and then links to an inexistent passage.
  • Looking back at your original example now. The widget should be doing what you want, it's the way you're using it which is causing you trouble.

    As noted by its documentation, the <<button>> macro is silent. You cannot simply call your widget via the <<button>> and expect to see output.

    As one suggestion, you could change your <<button "next round">> code to look something like the following:
    <<button "next round">>
    	<<if $userAnswer eq "">>
    		<<replace "#answer-error">>Please answer the question!<</replace>>
    	<<else>>
    		<<evaluate $level $rightAnswer $userAnswer $focus>>
    	<</if>>
    <</button>> <span id="answer-error"></span>
    
    Which would allow your <<evaluate>> widget code to be simplified thus:
    <<widget "evaluate">>\
    	<<silently>>
    	<<set $level to $args[0]>>
    	<<set $rightAnswer to $args[1]>>
    	<<set $userAnswer to $args[2]>>
    	<<set $focus to $args[3]>>
    	<<set $round += 1>>
    	<<if $userAnswer eq $rightAnswer>>
    		<<set $answerLog.push(true)>>
    	<<else>>
    		<<set $answerLog.push(false)>>
    	<</if>>
    	<<set $lastIndex to $answerLog.length - 1>> 
    	<<if $answerLog[$lastIndex] and $answerLog[$lastIndex - 1]>>
    		<<set $level to $level + 1>>
    		<<if $level gt 4>>
    			<<set $level to 4>>
    		<</if>>
    	<<elseif not $answerLog[$lastIndex] and not $answerLog[$lastIndex - 1]>>
    		<<set $level to $level - 1>>
    		<<if $level lt 0>>
    			<<set $level to 0>>
    		<</if>>
    	<</if>>
    	<<set $nextpassage to $level + "x" + $round>>
    	<<goto $nextpassage>>
    	<</silently>>\
    <</widget>>
    
  • Almost there, thank you so much, Mad Exile!

    I tried your button code on several different occasions. It seems it works just fine with text inputs, so
    <<set $rightAnswer = "B">>
    
    Which door do you choose? A, B or C? <<textbox "$userAnswer" "">>
    
    <<button "next round">>
    	<<if $userAnswer eq "">>
    		<<replace "#answer-error">>Please answer the question!<</replace>>
    	<<else>>
    		<<evaluate $level $rightAnswer $userAnswer $focus>>
    	<</if>>
    <</button>> <span id="answer-error"></span>
    

    works perfectly (when leaving the textbox empty).
    But when I use radio buttons, such as here
    <<set $rightAnswer = "B">>
    
    Which door do you choose?
    
    <<radiobutton "$userAnswer" "A">>Door A
    <<radiobutton "$userAnswer" "B">>Door B
    <<radiobutton "$userAnswer" "C">>Door C
    
    <<button "next round">>
    	<<if $userAnswer eq "">>
    		<<replace "#answer-error">>Please answer the question!<</replace>>
    	<<else>>
    		<<evaluate $level $rightAnswer $userAnswer $focus>>
    	<</if>>
    <</button>> <span id="answer-error"></span>
    

    The <<if $userAnswer eq "">> conditions doen't seem to be met when leaving the three radiobuttons unchecked, it directly calls the macro.

    Can you think of a way to deal with that behaviour?
  • If you're not initializing $userAnswer before using it, then that would be why. Unlike the other input macros <<radiobutton>> does not modify the receiver variable unless the player selects a radio button. So, if no choice is made and the variable is undefined, then you get the undefined value. The undefined value is not the same as an empty string, so the check fails.

    Since you will likely be reusing $userAnswer multiple times, it would be better to simply initialize it to the empty string. You'll need to do something similar before asking each question anyway. It's likely best done where you set the correct ($rightAnswer) for each question. Simply do them both at the same time. For example:
    <<set $rightAnswer to "B">>
    <<set $userAnswer to "">>
    
Sign In or Register to comment.