Howdy, Stranger!

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

Can one create new variables named after other variables?

edited July 2015 in Help! with 2.0
Hello everyone !

I've started playing around with Twine 2.0 / Harlowe this week to create what is essentially an RPG with a focus on social interactions with NPCs.

I'm stumbling on an issue that I haven't found answered elsewhere (maybe I missed it), regarding the creation of variables with computed names. I'm sure it's just a syntax that I haven't yet found, but it may be simply something one can't do with Harlowe, which I'd found rather odd, but, well :)

Context
My NPC roaster ($npcroaster) is an array of datamaps, each datamap being a single NPC.
Each NPC has a "currentloc" key with a string value (like "tavern", "house", etc.).


When the player's character is in a location, I test that location ($charloc) against the "currentloc" value of every NPC to find out who the NPCs present in that same location are.
The player can talk to anyone in his current location: for that, I'm setting-up an interface element allowing the selection of what NPC(s) he wants to be addressing. He can talk to one NPC, or several NPCs at the same time (if there are enough in that location of course), by toggling "on and off" their names in a list that is rendered when the player clicks on the option "I want to have a chat" :

bb6152f047a5d56e8c800173d70ac6.png
The list of present NPCs is displayed for the player to choose from.


294f3794904c4fe7759bdb89f88a91.png
By clicking on a name, the player selects it as shown above.


3a033ed45e5df6c5d7bd814cd06143.png
Once submitted, the game shows the NPCs in the conversation and offers the option to select them again.


Actual code
I've written the logic and systems for retrieving the present NPCs correctly (then store their names in an separate array called $presentnpcs):
(if: $npcroaster.length > 0)[
   (if: $loopcount > 0)[
      (if: $npcroaster's $loopcount's currentloc is $loctest)[
         (set: $presentnpcs to it + (array: $npcroaster's $loopcount's name))]
         (set: $loopcount to it -1)
         (display: "Determine present NPCs")]
      (else:)[
         (print: $presentnpcs)
         (display: "Talk to menu")]
]
(else:)[
   There are no NPCs in this world!]

... and the mechanic of the selection menu, which uses two passages. One is for displaying the menu:
(set: $cycle1 to 0)(set: $cycle2 to 0)(set: $cycle3 to 0)
|ui>[
   Select who you want to talk with and click OK:
   ||name1>[Anton]| |name2>[Gert]| |name3>[Julie]| ]
   |ok>[(text: "[")OK(text: "]")]
   (click-replace: ?name1)[(set: $click to 1)(display: "logic")]
   (click-replace: ?name2)[(set: $click to 2)(display: "logic")]
   (click-replace: ?name3)[(set: $click to 3)(display: "logic")]
   (click-replace: ?ok)[
      (replace: ?ui)[]You are now talking to:
      |(if: $cycle1 is 1)[Anton|](if: $cycle2 is 1)[Gert|](if: $cycle3 is 1)[Julie|](if: $cycle1 is 0 and $cycle2 is 0 and $cycle3 is 0)[no one|]
      (hook: "talk")[
         (click to reselect)]
      ]
   (click: ?talk)[
      (replace: ?ok)[]
      (replace: ?ui)[(display: "Talk to menu")]
]

... and one for its toggling logic:
(if: $click is 1)[
   (set: $cycle1 to (it +1) % 2)
   (if: $cycle1 is 0)[Anton]
   (else-if: $cycle1 is 1)[(text: "*")Anton(text: "*")]
   (click-replace: ?name1)[
      (set: $click to 1)(display: "logic")]]
(else-if: $click is 2)[
   (set: $cycle2 to (it +1) % 2)
   (if: $cycle2 is 0)[Gert]
   (else-if: $cycle2 is 1)[(text: "*")Gert(text: "*")]
   (click-replace: ?name2)[
      (set: $click to 2)(display: "logic")]]
(else-if: $click is 3)[
   (set: $cycle3 to (it +1) % 2)
   (if: $cycle3 is 0)[Julie]
   (else-if: $cycle3 is 1)[(text: "*")Julie(text: "*")]
   (click-replace: ?name3)[
      (set: $click to 3)(display: "logic")]]

The tricky part for me comes from the fact that the number of NPCs in a location at any given time can be different, of course.
As you can see from above, the mechanic of the selection menu uses (click-replace:) macros that need a different hook for each name (?name1, ?name2, etc for ex.), as well as a different variable for storing identifying the clicks ($click1, click2, etc.).

The above code works very well in my "test" write-up of the menu logic and mechanic, where I have fixed the number of NPCs to 3 for testing purposes, and fixed variables'/hooks' names. However, for this to work in the context of an unknown number of NPCs in a location, I need to write-up a new loop in which each ?name# and $click# will be incremented for each loop iteration, the number of iterations depending on the number of NPCs present (in other words, $presentnpcs.length).

Doing this with hooks is easy enough, but I haven't been able to find how to do it for the creation of new variables based on others - essentially, the system correctly constructs $click1, $click2 etc. but I can't seem to find a way to escape the fact that the result of this construction is considered a string and that, evidently, gives the error:
I can't put a new value into the string "$cycle1".

I've tried a whole lot of options to write it up correctly, but to no avail.

So here's my question: does anyone know how to create a new variable with a name that's composed of a string ("click") and a number ?

Comments

  • I would suggest replacing all the $cycleN variables with a single $cycle array variable, where the 1st array element represents $cycle1, the 2nd represents $cycle2, the 3rd represents $cycle3, and so on.
    This would allow you to have as many cycle 'variables' as you need.
  • Hi greyelf,

    Thanks a lot for this alternative :)

    But does that mean that one can't create new variables in such a way in Harlowe ?
  • RaHaN wrote: »
    But does that mean that one can't create new variables in such a way in Harlowe ?
    Not that I know of, but you may want to either PM Leon (Harlowe's creator) and ask him, or wait to see if someone else has a better answer.
  • edited July 2015
    I used your tip, greyelf, and managed to constitute the menu with its hooks, but I'm lost with the logic passage, since it relies on the variable that's used to iterate the loop, which once I'm done establishing the menu, is always equal to 0 (the condition to stop looping).

    Here is what I've written so far, starting with the passage to determine what NPCs are there:
    (if: $npcroaster.length > 0)[
       (if: $loopcount > 0)[
          (if: $npcroaster's $loopcount's currentloc is $loctest)[
             (set: $presentnpcs to it + (array: $npcroaster's $loopcount's name))(set: $click to it + (array: $click.length +1))(set: $cycle to it + (array: 0))(set: $loopnumber to $presentnpcs.length)(set: $loopclick to $presentnpcs.length)
             ]
          (set: $loopcount to it -1)(display: "Determine present NPCs")
          ]
    (else:)[
       (print: $presentnpcs), (print: $click), (print: $cycle), (print: $loopnumber) //this is just to show that part goes well
       (display: "Talk to menu dynamic")
       ]
    ]
    (else:)[There are no NPCs in this world!]
    

    Then the Talk to menu built up dynamically:
    (if: $presentnpcs.length > 0)[
       (if: $loopnumber > 0)[
          (hook: "ui")[
             (hook: (text: $click's $loopnumber))[
                (print: $presentnpcs's $loopnumber)|
                ]
             ]
          (set: $loopnumber to it -1)
          (click-replace: (text: "?" + (text: ($presentnpcs.length - $loopnumber))))[
             (display: "talk to logic")
          ](display: "Talk to menu dynamic")]
    (else:)[
       |ok>[(text: "[")OK(text: "]")]]](click-replace: ?ok)[
          (if: $cycle's $loopnumber is 1)[
             (print: $presentnpcs's $loopnumber)](click to reselect)
             ]
          (else:)[
             There's no one here but you.]
    

    And the logic I'm struggling with:
    (if: $loopclick > 0)[
       (set: $cycle's $loopclick to (it +1) % 2)(if: $cycle's $loopclick is 0)[
          (print: $presentnpcs's $loopclick)
          ]
       (else-if: $cycle's $loopclick is 1)[
          (text: "*")(print: $presentnpcs's $loopclick)(text: "*")
          ]
       (click-replace: (text: "?" + (text: $loopclick)))[
          (set: $loopclick to it -1)(display: "talk to logic")
          ]
       ]
    

    Clearly, I know it doesn't work at least in part because of that $loopclick variable always ending up being 0, but I'm at a lost as to how I should proceed to solve this...

    any ideas?
  • Is there a reason you are trying to build your RPG using Harlowe instead of one of the other story formats that support Author Customization/Extension via Javascript?

    Your example is using Passage Content Nesting (not looping) to dynamically create your desired output, and as found out by @noahmarshall recently in this thread Harlowe will return an error message if the depth of the HTML generated from your nested passages become to deep.

    If you need looping then I would suggest using SugarCube because it has a <<for>> macro.
  • Apart from the fact that I'm completely new to all this, not particularly ! Harlowe seemed easy to learn, and I went with it without looking back I presume...

    I will look into SugarCube, then - but I believe there's not much Harlowe could need for me to achieve what I want with it (and the puzzle aspect of doing it with Harlowe is part of the fun I'm having with this attempt :p)

    Anyway, thanks a ton for your time and help, greyelf !
  • -- There's no syntax highlighting in SugarCube for Twine 2 ? :/
  • The Twine 2 application requires each of the story format developers to implement (write the code for) their own syntax highlighting module, and currently only the developer of Harlowe has done so.
    but I believe there's not much Harlowe could need for me to achieve what I want with it
    based on the issues you are having this does not seem to be the case. *smile*
  • Hahaha

    Well, if only you could create variables dynamically, like I was originally looking to do... let's say it would have taken me much longer to see Harlowe limits with respects to what I want to do :P
  • I think what's impressive about Twine, in general, is that it makes creating interactive fiction appear doable for people who, like me, had no prior background/training/experience with programming or anything even vaguely related. The farthest I'd advanced in writing stories was was learning how to do a section break in Microsoft Word. When I learned how to do that, I'm pretty sure I convinced myself I was the smartest man alive.

    For someone like me, you have to start somewhere, and Harlowe is the default language for Twine, so I would guess that's what a lot of people who know nothing will be starting with, not really having the background/training/experience as a basis for choosing something other than Harlowe.
  • SugarCube has syntax highlighting in Twine 1.4.2.
Sign In or Register to comment.