Howdy, Stranger!

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

[Solved] Using a JavaScript array element as a link

edited July 2014 in Help! with 1.x
I am trying to create a series of dynamic links in the sidebar based on elements from an array.

So far, as I stumble through learning JavaScript, things have been progressing, however I've hit a bit of a roadblock.

I have succeeded in producing the appropriate sidebar links, however Twine is refusing to acknowledge the existence of passages which do indeed exist. What am I doing wrong?

The link code (in a passage called Recruit which is <<display>>ed in the side bar) is:
<<if $TeamMembers > 0>>\
[[<<$Recruits[0]>>|<<$Recruits[0]>>]]
[[<<$Recruits[1]>>|<<$Recruits[1]>>]]
[[<<$Recruits[2]>>|<<$Recruits[2]>>]]
[[<<$Recruits[3]>>|<<$Recruits[3]>>]]
[[<<$Recruits[4]>>|<<$Recruits[4]>>]]
<<endif>>\
Which works fine in displaying the appropriate text, however it is generating the following error:

[quote]This passage does not exist: Elton Caravaggio

But this passage most certainly does exist as it contains the code line which resulted in "Elton Caravaggio" being pushed into the $Recruits array.
<<if $Recruits.indexOf($Char2.passage) lt 0>>[[Recruit <<$Char2.passage>>|TeamSelection][$Recruits.push($Char2.passage); $TeamMembers += 1]]
Where $Char2.passage is the name of both the Twine passage and the recruit character (in this example, Elton Caravaggio).

The $Recruits array is defined in my Start passage as:
<<set $Recruits = [] >>\
I am using the default Sugarcane format in Twine 1.4.2.

What am I doing that is preventing Twine from recognising a correctly-named passage link? Any help and insight would be greatly appreciated.

Comments

  • Your code is close, but you incorrectly used the <<display>> macro which is trying to insert the "contents" of the passage not the passage name stored in the array element.

    Try this instead:
    note:
    a. I moved the variable initialization into the StoryInit passage, which is what it is for.
    b. I kept your variable names though gave them my own values.

    :: StoryInit
    <<set $TeamMembers to 1>>
    <<set $Recruits to ['Passage1', 'Passage2'] >>

    :: StoryMenu
    <<if $TeamMembers gt 0>>
    <<print "[[" + $Recruits[0] + "]]">>
    <<print "[[" + $Recruits[1] + "]]">>
    <<endif>>
    I have attached a sample story file containing the above code.

    note:
    If you were using the SugarCube story format you could use it's <<for>> macro to replace the hardwired <<print>> macros. Your StoryMenu would end up looking something like the following.

    :: StoryMenu
    <<if $TeamMembers gt 0>>
    <<for $i to 0; $i lt $Recruits.length; $i++>>
    <<print "[[" + $Recruits[$i] + "]]">>
    <</for>>
    <<endif>>
  • You have a syntax error.  The passage component of the wiki link markup only accepts JavaScript, not wiki text.

    Try this:
    [[<<$Recruits[0]>>|$Recruits[0] ]]
    The space in there after the $Recruits[0] is required (otherwise the triple closing square-bracket would be ambiguous to the parser).

    And there's actually no reason, in the code you've shown, to use the link text component, since you're using the same thing for both the link text and passage, so you could simply do this:
    [[$Recruits[0] ]]
    greyelf's suggestion:
    <<print "[[" + $Recruits[0] + "]]">>
    Also works, if you prefer that.  It also has the advantage of being what you have to do, in cases where you need the $variable to be evaluated early (which isn't required by your current code, but it's something to keep in mind).
  • greyelf wrote:

    Your code is close, but you incorrectly used the <<display>> macro which is trying to insert the "contents" of the passage not the passage name stored in the array element.


    Actually.  There are two macro shorthand macro syntaxes in the vanilla headers.  One calls &lt;&lt;print&gt;&gt;, while the other calls &lt;&lt;display&gt;&gt;.  The one used by Freebooted is the &lt;&lt;print&gt;&gt; calling version.  His code was failing because of the reason I outlined above.

    The &lt;&lt;print&gt;&gt; calling version is used when the shorthand identifier starts with the Twine variable sigil, the dollar sign ($).  The &lt;&lt;display&gt;&gt; calling version is used when the shorthand identifier matches the name of an existing passage.

    The logic goes something like this, as I recall: (pseudo-code)

    if identifier is macro name: call macro[identifier]
    else if identifier is $variable: call macro[print] identifier
    else if identifier is passage name: call macro[display] identifier
    else throw error
  • TheMadExile wrote:

    Actually.  There are two macro shorthand macro syntaxes in the vanilla headers.  One calls &lt;&lt;print&gt;&gt;, while the other calls &lt;&lt;display&gt;&gt;.

    How silly and confusing. I hope the documentation is clear that the functionality changes based on the context.

    So, if you wanted to display the contents of a passage using a variable to indicate which passage, you would have to name the variable without a "$"?
  • greyelf wrote:

    I hope the documentation is clear that the functionality changes based on the context.


    More or less.  The wiki page for each, &lt;&lt;display&gt;&gt; and &lt;&lt;print&gt;&gt;, does describe their shorthand forms, however, neither mentions that there's another macro with a similar shorthand form (I'm unsure if there's another wiki page that might).  So, that might be a nice addition.


    greyelf wrote:

    So, if you wanted to display the contents of a passage using a variable to indicate which passage, you would have to name the variable without a "$"?


    IIRC, you cannot do so directly, the current code simply doesn't allow it.  You'd have to force the early evaluation of the variable to do so, in which case you're better off simply calling the non-shorthand form of &lt;&lt;display&gt;&gt;:
    <<display $passageName>>
    Currently, the only time it might make sense to use indirection here is if you wanted to call a pseudo-macro with arguments.  For example:
    <<print '<<' + $pseudoMacroName + ' ' + $someArgument + '>>'>>
    Though, I wouldn't expect that need to come up very often (and there are other ways to do it besides).
  • Thanks for the feedback folks. That's given me a few things to try.

    I found amending the syntax of the code in my Recruits passage as per TheMadExile's advice from my original:
    [[<<$Recruits[0]>>|<<$Recruits[0]>>]]
    [[<<$Recruits[1]>>|<<$Recruits[1]>>]]
    [[<<$Recruits[2]>>|<<$Recruits[2]>>]]
    [[<<$Recruits[3]>>|<<$Recruits[3]>>]]
    [[<<$Recruits[4]>>|<<$Recruits[4]>>]]
    to
    [[$Recruits[0] ]]
    [[$Recruits[1] ]]
    [[$Recruits[2] ]]
    [[$Recruits[3] ]]
    [[$Recruits[4] ]]
    Did indeed resolve the problem of the broken link, however it also caused the unpopulated array elements to display, resulting in a series of errors stating that $Recruits[1-4] do not exist (which they wouldn't until further characters were recruited). This didn't happen with the earlier code which couldn't execute the link, but which successfully displayed text from populated elements while ignoring unpopulated ones.

    The
    <<print "[[" + $Recruits[0] + "]]">>
    solution also produces an 'undefined' error for each empty element.

    The middle ground which successfully enables the link without unpopulated elements generating errors seems to be:
    [[<<$Recruits[0]>>|$Recruits[0] ]]
    [[<<$Recruits[1]>>|$Recruits[1] ]]
    [[<<$Recruits[2]>>|$Recruits[2] ]]
    [[<<$Recruits[3]>>|$Recruits[3] ]]
    [[<<$Recruits[4]>>|$Recruits[4] ]]
    Which means I can now recruit and dismiss team members successfully and display their names in the sidebar without empty team slots producing errors. I'm happy with the results, even if I'm still not entirely clear as to why it works.

    Thanks again for all the help, it's very good of you to take the time.

    (and thanks to Greyelf for the housekeeping tip regarding StoryInit)
  • Freebooted wrote:

    The middle ground which successfully enables the link without unpopulated elements generating errors seems to be:
    [[<<$Recruits[0]>>|$Recruits[0] ]]
    [[<<$Recruits[1]>>|$Recruits[1] ]]
    [[<<$Recruits[2]>>|$Recruits[2] ]]
    [[<<$Recruits[3]>>|$Recruits[3] ]]
    [[<<$Recruits[4]>>|$Recruits[4] ]]
    Which means I can now recruit and dismiss team members successfully and display their names in the sidebar without empty team slots producing errors. I'm happy with the results, even if I'm still not entirely clear as to why it works.


    It "works" because the broken links generated by that code, and it does generate broken links, are "invisible" because &lt;&lt;print&gt;&gt; emits nothing in the case of an undefined value.  Inspecting the DOM will show the broken links clearly.

    I didn't realize $Recruits might not contain as many recruits as you're trying to print.  Since I now do, I'd probably suggest something like this:

    <<if $TeamMembers > 0>>\
    <<if $Recruits[0]>>[[$Recruits[0] ]]<br><<endif>>\
    <<if $Recruits[1]>>[[$Recruits[1] ]]<br><<endif>>\
    <<if $Recruits[2]>>[[$Recruits[2] ]]<br><<endif>>\
    <<if $Recruits[3]>>[[$Recruits[3] ]]<br><<endif>>\
    <<if $Recruits[4]>>[[$Recruits[4] ]]<br><<endif>>\
    <<endif>>
    Which doesn't emit invisible broken links.

    PS: If your $TeamMembers variable is simply a count of the team members you currently have and $Recruits only contains recruits (none of the array elements contain empty values of any kind), then you should be able to replace it with $Recruits.length.
  • Thanks TheMadExile, that works perfectly. I had a niggling suspicion that my 'solution' was a bit of a sloppy hack job.

    My use of $TeamMembers was a bit of a throwback to an earlier version which relied far less on JavaScript. I had been using $Recruits.length elsewhere but had failed to replace $TeamMembers in some instances. Thanks, I've now addressed that too and things are starting to look a lot tidier.
Sign In or Register to comment.