Howdy, Stranger!

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

Array contains object - problem

Twine 2, SigarCube 2.x

Example setup:
StoryInit: <<set $object to {name: "name"}>> <<set $Array to []>>
Passage: <<if visited() <2>><<set $Array.push($object)>><<endif>> <<print $Array.includes($object)>>
______________________________________________________________________________________________________________
The first time Passage is visited, object is pushed to Array, and Array knows it cointans the object. But it's the only moment it does! Every other time Array will print "false", telling that it does not contain object (or -1 if indexOf is used), even though it still DOES, because $Array[0].name is totally accessible and would print "name" without a problem...

Anyone knows why it doesn't work and how to do it right? It got me stuck so bad right now, I'm totally out of ideas :( It happens only to objects, checking normal variable the same way works without problems.

Comments

  • A bit of background first. The values of all story variables are cloned between moments. This is done so that altering the current moment does not invalidate the rest of the history—i.e. so it doesn't break time.

    Your issue stems from the fact that, currently, during the cloning process object references are not maintained. This means that while the object reference you assign to $object and the copy of that reference you push into $Array initially point to the same object—which is why calling $Array.includes($object) at that point will return true—when you navigate passages each reference will receive its own unique copy of the object—thus calling $Array.includes($object) after navigation will return false since there are two equivalent, but inequal, objects.

    There are a few workarounds. You could give each object you wish to use in this way a unique property and check for that. Alternatively, you could make each such object the child of a parent/master object and use their property name on that master object to access them. Which would be better depends on exactly what you're attempting to do.
  • edited June 2017
    Thank you for answer!
    It's a simple shop/inventory system that I'm trying to achieve (I'm probably reinventing the wheel here).
    I've got two more, follow up questions.
    1. About using parent object. What do you mean by "use their property name on master object to access them". Would that require to for-loop every parent's child to check if it's name == searched name? Like:
    if master[i].name == "searched_name"
    
    ?

    2. I did something weird and it worked. But I don't know why it worked and I think it might create some problems in future. What I've done:
    A ) Put every needed shop variables into JavaScript. So instead of <<set $item to {name: "name"}>> I'm just creating empty <<set $item to "">> and then in Java var item = {name: "name"};
    B ) Create "getAll()" function that looks like this:
    state.active.variables.item1 = item1;
    state.active.variables.item2 = item2;
    ....
    state.active.variables.inventory = inventory;
    
    C ) Run "getAll()" at the beginning of every passage that needs to check if searched item exists in inventory/shop.

    This way it works. The weird part for me is that I can change Twine variables (e.g. set $item.name to "changed") and it still works, even though I [think] am not accessing Java variables, just Twine copies. So I think they are copies of the same value, after I assigned one to another.
    But running "getAll()" that assigns literally every inventory-related variable again and again seems like a really bad idea. But it's very tempting to use it, since I don't have to change any Twine scripting, just transfer every item to Java and call "getAll()" :P
    How bad is that and why does it even work?
  • MadAlice wrote: »
    ...use their property name on master object to access them..
    If I understand the context correctly it means doing something like the following.
    <<set $items to {
    	"bread" : {cost: 20},
    	"milk jug" : {cost: 30},
    	"egg" : {cost: 5}
    }>>
    
    <<set $inventory to []>>
    
    <<set $inventory.push("bread")>>
    
    item $inventory[0] costs <<print $items[$inventory[0]].cost>>
    
    ... where you store all the possible items in your story within a master object, then store the name of related master object element in the Array ($inventory) instead of a reference to the actual element.

    You can then use that name to find the element within the $items object when you need to access information about that element.

    Doing the above should also fix the issues with your other questions.

  • Thank you! Both of you!
    I didn't fully check it yet, but it seems to work as intended. It solves my problems. Although it's a bit more annoying this way, because I can't easily for-loop all items through names. Therefore I did this:
    <<set $items to {
    	0 : {name: "bread", cost: 20},
    	1 : {name: "milk", cost: 30},
    	2 : {name: "egg", cost: 5}
    }>>
    
    To be able to use $itemsb]i[/b in a loop. Let me know if there's a better way to loop through items container! :)
    (Oh, and also I can't use $items.length anymore, so I had to switch to Object.keys($items).length )
  • MadAlice wrote: »
    ... because I can't easily for-loop all items through names.
    Assuming your data looks like the following:
    <<set $items to {
    	"bread" : {cost: 20},
    	"milk jug" : {cost: 30},
    	"egg" : {cost: 5}
    }>>
    
    <<set $inventory to []>>
    <<set $inventory.push("bread")>>
    <<set $inventory.push("milk jug")>>
    
    Then to loop through the elements of the $inventory array you could do the following:
    <<for _i to 0; _i lt $inventory.length; _i++>>
    	<<print (_i + 1) + ". " + $inventory[_i]>>
    <</for>>
    
    And use code like the following to loop through the items in the $items object:
    <<set _names to Object.keys($items)>>
    <<for _i to 0; _i lt _names.length; _i++>>
    	<<print "item " + _names[_i] + " costs " + $items[_names[_i]].cost>>
    <</for>>
    


    note: The main point of using a String key to represent each item in the $items object is that it is a lot easier to remember "big shiny thing" than it is to remember it is item 26.
  • edited June 2017
    Thank you.
    Although I've got to admit I accidentally wasted your time, because I figured it out and just didn't manage to edit my post in time! :(
    Naming keys: 0, 1,... - is impossible, it's like variable name, it cannot start with a number. Therefore I realized I cannot do it and used Object.keys() instead. Which by the way proves I'm stupid, because I already used Object.keys when calling "Object.keys().length", and it still didn't make me realize I can use it to loop through keys anyway! :|

    Thank you again, things are WAY easier to use right now, I hope I can handle the rest of inventory/shop system by myself. Have a great day!
Sign In or Register to comment.