Howdy, Stranger!

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

How do I properly copy a datamap object out of an array?

Sugarcube 2, Twine 2

In the "Start" passage I create my objects:

<<set $player to {name: Player, level: 1, exp: 0}>>
<<set $npc1 to {name: NPC1, level: 1, exp: 0}>>
<<set $npc2 to {name: NPC1, level: 1, exp: 0}>>
<<set $characters to [$player, $npc1, $npc2]>>
<<set $attackers to []>>
<<set $victims to []>>

In passage 1 I'm copying the arrays and give the player some choice options:
<<set $attackers to $characters.slice(0)>>
<<set $victims to $characters.slice(0)>>

In passage 2 I want to go through the $attackers array with a loop and choose a victim for each attack. The result of the attack should then change the stats in the $characters array:
<<set $attackers.shuffle()>>
<<for $i to 0; $i lt $attackers.length; $i++>>
<<set $attacker to $attackers[$i]>>
...
...
<<set $characters[$characters.indexOf($attacker)].exp to $characters[$characters.indexOf($attacker)].exp + 100>>
<</for>>

But unfortunately the indexOf-function is providing only -1 as result. If I do everything (passage 1 & passage 2) in one passage it works but I run into some other problems and I dislike the design.
I found a way to work around the problem with the not-working indexOf-function but it's not elegant. My guess is that with the "<<set $attacker to $attackers[$i]>> I'm not copying the datamap-object but just setting a reference to the array.

Would the indexOf-function work if I properly copy the datamap object?

Thank you.

Comments

  • edited January 2016
    I think I found the answer myself. In JavaScript objects with equal properties are not considered equal (as in used for indexOf) if they don't reference back to the same object. So my current guess is that due to changing passages I'm breaking that reference.

    Please correct me if I'm wrong.
  • First. Please use the code tag when you're posting code snippets.

    Second. Generic objects, which is what you're currently using, are not Map objects (which are not called datamaps; that's Harlowe nomenclature). That said, if you wished, you may use Map objects in SugarCube 2.

    Third. You really should not use the starting passage for initialization. The StoryInit special passage exists solely for that purpose.


    You are correct. The story history requires that new moments/states have their own copies of the story variables, so they're cloned upon passage navigation, which breaks references.

    As a workaround, instead of using an array and assigning references, you could use a generic object to hold your character objects and indirectly reference them via their property name/"key". For example:

    In the StoryInit special passage:
    /*
    	Set $characters to an empty generic object,
    	$attackers and $victims to empty arrays.
    */
    <<set $characters to {}>>
    <<set $attackers  to []>>
    <<set $victims    to []>>
    
    /*
    	Add the various character generic objects to $characters.
    */
    <<set $characters["player"] to { name: "Player", level: 1, exp: 0 }>>
    <<set $characters["npc1"]   to { name: "NPC1", level: 1, exp: 0 }>>
    <<set $characters["npc2"]   to { name: "NPC1", level: 1, exp: 0 }>>
    

    In passage 1:
    /*
    	Get all "keys" from $characters (i.e. "player", "npc1", "npc2").
    */
    <<set $attackers to Object.keys($characters)>>
    
    /*
    	Since we just got the keys for $attackers, just copy it.
    */
    <<set $victims to $attackers.slice(0)>>
    

    In passage 2:
    <<set $attackers.shuffle()>>
    <<for $i to 0; $i lt $attackers.length; $i++>>
    <<set $attacker to $attackers[$i]>>
    
    /*
    	Use $characters[$attacker] to reference the chosen attacker.
    */
    
    […]
    
    <<set $characters[$attacker].exp += 100>>
    <</for>>
    /*
    	Unless you need them post-loop, it's a good idea to unset your loop variables.
    */
    <<unset $i>>
    
  • edited January 2016
    Thank you. I apologize for the incorrect use of nomenclature. I'm not a programmer. ;)

    I'm doing if differently by adding a unique index-value to each generic object and looking through the array to find the right generic object with a widget.
    <<widget "attackerindex">>
    <<set $attackerindex to 0>>
    <<set $indexflag to 0>>
    <<for $i to 0; $i lt $enemyarray.length; $i++>>
    <<if $enemyarray[$i].index isnot $attacker.index>>
    <<if $indexflag is 0>><<set $attackerindex++>>
    <</if>>
    <<else>>
    <<set $indexflag to 1>>
    <</if>>
    <</for>>
    <<unset $i>>
    <<unset $indexflag>>
    <</widget>>
    

    It saves me the trouble of going through my whole story to change the code and how to access all the different variables/properties.
Sign In or Register to comment.