Howdy, Stranger!

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

Toggle selection of a listed array of elements?

edited August 2015 in Help! with 2.0
Hi everyone!

I'm struggling with something that seems simple enough, but that I can't seem to approach elegantly (I'm using Twine 2 and SugarCube 2).

In my game, there are NPCs, and they move from place to place according to their agenda.
In any given location, I can determine what NPCs are there easily using a passage I named <<presentnpcs>> (a simple <<for>> loop parsing the database of NPCs, looking for those whose currentloc attribute matches the one of the current PC's location, itself determined through the tags of the current passage):
<<silently>><<set $presentnpcs to []>>
<<set $tmpKeys to Object.keys($npcroaster)>>
	<<for $i to 0; $i lt $tmpKeys.length; $i++>>
	<<if $npcroaster[$tmpKeys[$i]].currentloc is $pcstats.pcloc>>
    	<<set $presentnpcs.push($npcroaster[$tmpKeys[$i]].name)>>
    <</if>>
	<</for>>
<<unset $tmpKeys>>
<</silently>>

Here is a sample NPC roster with two NPCs for reference:
<<set $npcroster to {
	anton : {
    	name : "Anton",
        strength : 1,
        intelligence : 1,
        honesty : 1,
        trust_in_pc : 0,
        feels_stronger_than_pc : 0,
        feels_smarter_than_pc : 0,
        currentloc : "tavern",
        alive : true,
        npcmoney : 22,
        npcinventory : $antoninventory,
        friendswith : $antonfriends,
        enemywith : $antonenemies,
        neutralwith : $antonneutrals,
        knows : {
        	pcname : {
            name: "pcname",
            description: "you're named $pcbio.name.",
            spreadable : true,
            	},
        	},
        },
	gert : {
    	name : "Gert",
        strength : 0,
        intelligence : 0,
        honesty : 0,
        trust_in_pc : 0,
        feels_stronger_than_pc : 0,
        feels_smarter_than_pc : 0,
        currentloc : "tavern",
        alive : true,
        npcmoney : 22,
        npcinventory : $gertinventory,
        friendswith : $gertfriends,
        enemywith : $gertenemies,
        neutralwith : $gertneutrals,
        knows : {},
     },
}

Now, I'm trying to set-up an elegant "talk to" menu, populated with the names of present NPCs, and with a toggle-type solution to allow the player to select and deselect who, among the present NPCs, he wants to talk to (he can talk to several NPCs at the same time if he so wishes).

Up until the player is done with the first few moments of the game where everything is kind of hard-scripted I've been using an array variable called $talkedtonpcs, containing the names of who the player was talking to. Once he's out of this beginning, he can move anywhere, and talk to any NPC in his location. So I need to populate the $talkedtonpcs array dynamically by displaying a selection menu where one can either click on the names to select and deselect them, or check boxes (<<checkbox>>).

My problem is that I need to do this through a loop to account for the dynamically changing amount of NPCs in a location, as well as their different names. I have tried different approaches, but with my way of using <<for>> loops, I can't find a way to keep the different clicks or boxes working with the different options (i.e. the different NPCs to toggle on or off when selecting who to talk to), because my loop counter, which would be used to dynamically create the links and/or checkboxes, will inevitably end up being a value I can't remember. Thus, I can create the menu, either with clickable names or checkboxes, but when the player uses them, I have no way of telling what NPC he is selecting or deselecting...

Here is my latest attempt using the creation of an object holding the the number of the click/checkbox and its associated name, but I still stumbled on the very same wall induced by the mechanic of the loop (which for a moment, I thought I could escape by building the checkbox/clickable toggle in another passage, before it hit me I couldn't):
<<nobr>>
<<set $talktoname to {}>>
<<if $presentnpcs.length > 0>>
	<<if $talkedtonpcs.length is 0>>
    	You are not talking to anyone right now.
    <<else>>
    	You are talking with <<for $i to 0; $i lt $talkedtonpcs.length; $i++>><<run jQuery.extend($talkedtoname, {$i : $talkedtonpcs[$i]})>><<display talkcheckbox>><<print $talkedtonpcs[$i]>><<if $i is $talkedtonpcs.length>><<elseif $i isnot 0>>.<<else>>, <</if>><</for>>
    <</if>>
<<else>>There's no one to talk to, here.
<</if>>
<</nobr>>

Either I have reached my programming ability, or Twine's limits... can anyone devise a way to do this toggle menu and update the $talkedtonpcs array accordingly?

Comments

  • Update: I have tried another approach by adding the boolean attribute "selected :" to the attributes of my NPCs in my NPC roaster, then using the following code:
    <<nobr>>
    <<set $tmpKeys to Object.keys($npcroster)>>
    <<set $tmpClick to []>>
    <<if $presentnpcs.length > 0>>
            You are talking with 
            <<for $i to 0; $i lt $tmpKeys.length; $i++>>
            <<set $tmpClick.push($tmpKeys[$i])>>
            	<<for $y to 0; $y lt $presentnpcs.length; $y++>>
          			<<if $presentnpcs[$y] is $npcroster[$tmpKeys[$i]].name>>
                		<<if $npcroster[$tmpKeys[$i]].selected is true>>
                    		<<print $npcroster[$tmpKeys[$i]].name>><<checkbox "$npcroster[$tmpClick[$i]].selected" false true checked>>
                        	<<else>>
                        	<<print $npcroster[$tmpKeys[$i]].name>><<checkbox "$npcroster[$tmpClick[$i]].selected" false true>>
                		<</if>>
                        <<if $i is $talkedtonpcs.length>>
                			<<elseif $i isnot 0>>.
                    		<<else>>, 
                    	<</if>>
                    <</if>>
            	<</for>>
            <</for>>
    <<else>>There's no one to talk to, here.
    <</if>>
    <</nobr>>
    

    Of course, since I still have to use $i to determine what variable the checkboxes change (so in that case, $npcroster.npc.selected where npc stands for either anton or gert in my previous $npcroaster setup), it doesn't work either. Everything displays fine, but clicking on the checkboxes doesn't switch the attached attribute true to false and false to true.

    I always come back to that same problem. I've looked at other stuff like the prerender and postrender things that are available in SugarCube, but none of what I read about seems to offer a new approach or a solution.

    Help ! :/
  • edited August 2015
    Without looking too much into it at the moment, you probably have a temporal access issue. In other words, you're probably trying to use a $variable later which has the value you need now (not later). You're also probably making this more complicated than it needs to be.

    You probably need to do something like the following:
    /* Create the list of present NPCs (contains their roster IDs, not names). */
    <<silently>><<set $presentnpcs to []>>
    <<set $tmpKeys to Object.keys($npcroster)>>
    <<for $i to 0; $i lt $tmpKeys.length; $i++>>
    	<<if $npcroster[$tmpKeys[$i]].currentloc is $pcstats.pcloc>>
    		<<set $presentnpcs.push($tmpKeys[$i])>>
    	<</if>>
    <</for>>
    <<unset $tmpKeys>>
    <</silently>>
    
    /* Add "talk to" checkboxes to all present NPCs. */
    <<nobr>>
    <<if $presentnpcs.length gt 0>>\
    You are talking with \
    <<for $i to 0; $i lt $presentnpcs.length; $i++>>
    <<print $npcroster[$presentnpcs[$i]].name>> <<print '<<checkbox "$npcroster[\'' + $presentnpcs[$i] + '\'].selected" false true' + ($npcroster[$presentnpcs[$i]].selected ? ' checked' : '') + '>>'>>
    <<if $i is $talkedtonpcs.length>>
    <<elseif $i isnot 0>>.
    <<else>>, 
    <</if>>
    <</for>>
    <<else>>There's no one to talk to, here.
    <</if>>
    <</nobr>>
    

    PS: You've spelled "npcroster" as "npcroaster" on a couple of occasions. Be careful of misspellings like that.
Sign In or Register to comment.