Howdy, Stranger!

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

Inventory System (Sugarcube 2.0 / Twine 2)

2»

Comments

  • edited February 2017
    JavaScript's current type system only supports passing parameters by copy and SugarCube adheres to the same model. Do not confuse passing a reference type by copy, which is what you do with objects in JavaScript, with passing any type by reference.

    What this means is that if you're doing something like the following:
    <<addToContainer $player.inventory $FancyWatch>>
    
    Then the following part of your <<initContainer>>/<<emptyContainer>> code:
    var container = this.args[0];
    container = [];
    
    Will not work because you're overwriting container with a new value.

    What you need to be doing there is something like the following instead:
    // Assign the local copy of the array reference to container.
    var container = this.args[0];
    
    // Use the <Array>.splice() method to empty the array.
    container.splice(0, container.length);
    
    This also means that the containers must be initialized as arrays before passing them to the macro.


    UPDATE: Of course, you're also redefining the findByID() expando method every time you call it, which is not a great idea. I'd suggest something like the following:
    Macro.add(['initContainer', 'emptyContainer'], {
    	handler : function ()  {
    		var container = this.args[0];
    
    		if (!Array.isArray(container)) {
    			return this.error('container is not an array');
    		}
    
    		container.splice(0, container.length);
    
    		if (!container.findByID) {
    			container.findByID = function(obj) {
    				if (
    					this.length === 0 ||
    					typeof obj !== 'object' ||
    					!obj.hasOwnProperty('id')
    				) {
    					return false;
    				}
    
    				return !!this.find(function (item) {
    					return item.id === obj.id;
    				});
    			};
    		}
    	}
    });
    

    ALSO: @greyelf Honestly, findByID() is not a great name for that method. With the standard library taken into account, its name makes it sound like it returns the object, rather than whether it exists. I think that hasById() or existsById(), something like that, would be less confusing.
  • edited February 2017
    Hey TheMadExile, I really appreciate the help and the explanation. Though I think I still might have problems, This is how I'm initializing my player:
    <<set $player = {
    	"money" : 500,
    	"inventory" : [],
    	"clothing" : 
    	{
    		"hats" : $BaseballHat,
    		"pants" : $OldTrousers,
    		"shirt" : $Tshirt,
    		"shoes" : $Sneakers
    	}
    }>>
    
    /% Player Inventory %/
    <<initContainer $player.inventory>>
    

    now, when I'm shopping and I'm calling inside a widget:
     <<addToContainer $player.inventory,  $args[0]>>
    
    where
    args[0]
    
    is
    $fancyWatch
    
    this is how I call the widget
    <<buyOnce $fancyWatch>>
    
    I get this:
    cannot find property findIndex: of undefined
    

    I'll go over this and see where things are going bad. I'm basically trying to generalize the application of this code for multiple containers without copying all the macros and applying them to different containers (that would be a disaster and bigger disaster to maintain).

    It appears inside
    addToContainer
    
    There's an issue with this:
    var idx = container.findIndex(function (item) 
    {
    	return item.id === obj.id;
    });
    
  • edited February 2017
    i added an in-between check to the parameter to see if the passed ref was or was not an array:
    if (!Array.isArray(container)) {
    			return this.error('container is not an array');
    		}
    

    this one yields an error, telling me that what i'm passing is not an array.

    Update: I had to slap my forehead after finding the solution. It appears I was passing the arguments to the macro in a wrong way.
    /% the correct way of passing more than one argument to a macro %/
    For some reason <<addToContainer $player.inventory $args[0]>>
    
  • edited February 2017
    At least one of your problems is that you're placing a spurious comma within the macro invocation.

    From SugarCube v2's macro documentation (right at the top):
    Macros fall into two broad categories based on the kind of arguments they accept: those that want an expression (e.g. <<set>> and <<print>>) and those that want discrete arguments separated by whitespace (e.g. <<link>> and <<audio>>). The documentation for each macro will tell you what it expects.
    All of the inventory macros shown within this thread so far fall into the latter category—i.e. those that want discrete arguments separated by whitespace.

    The following from your example:
    <<addToContainer $player.inventory,  $args[0]>>
    
    Yields three arguments to the macro: the current value of $player.inventory, a string containing a comma (","), and the current value of $args[0].

    Try the following instead:
    <<addToContainer $player.inventory $args[0]>>
    
  • edited February 2017
    Yeah, the comma was the cause of all the trouble. Now, I have an inventory system that somewhat works. I'll try and get the transfer macro implemented and finish my shopping demo.
  • Hey,
    I'm trying to write code to equip items that I have in my inventory.

    Now, I've written a JS function that is supposed to return an object from my inventory:
    /*
    It is supposed to search a container for an item ID and return the item
    container - the container that we search through
    searchId - the ID of the item we are looking for and will return when found
    */
    window.getItemById = function(container, searchId)
    {
    	var searched_item = container.find(function(item)
    	{
    		return item.id === searchId;
    	});
    	if (searched_item)
    	{
    		return searched_item;
    	}
    	else
    	{
    		return -1;
    	}
    }
    

    Now, this returns an undefined, which I can't do anything with. It's not even a -1 value.
    This is the experimental code that I'm running the JS function in.
    I'm trying to pass getItemById() to $item.
    I know that I'm missing lots of checks to make the code bullet proof. It's just frustrating that I can't return an object and store it just like that.
    <<nobr>><<linkreplace "Equip trousers">>
    	<<if countItemById($player.inventory, $trousers.id) > 0>>
    		<<if $player.clothing.pants !== $trousers>>
    			<<set $item is getItemById($player.inventory, $trousers.id)>>
    			<<if typeof $item !== "undefined">>
    				Found trousers inside your inventory!
    			<<else>>
    				YOU FAIL!
    			<</if>>
    		<<else>>
    			Trousers already equipped!
    		<</if>>
    	<<else>>
    		There are no trousers in your inventory!
    	<</if>>
    <</linkreplace>><</nobr>>
    <<back>>
    

    Any help is welcome!
  • edited February 2017
    I tried turning this into a macro and passing $item back as an argument. It also yields an undefined. Is there any way to return an element of an array inside JS back to SugarCube?

    Update:
    <<nobr>><<linkreplace "Equip trousers">>
    	<<if countItemById($player.inventory, $trousers.id) > 0>>
    		<<if $player.clothing.pants !== $trousers>>
    			<<set $item is "undefined">>
    			<<for _i = 0; _i < $player.inventory.length; _i++>> 
    				<<if $player.inventory[_i].id === $trousers.id>>
    					<<set $item = $player.inventory[_i]>>
    				<</if>>
    			<</for>>
    			<<if typeof $item !== "undefined">>
    				Found trousers inside your inventory!
    			<<else>>
    				<<if $item == "undefined">>
    					YOU FAIL!
    				<</if>>
    				You fail again!
    			<</if>>
    		<<else>>
    			Trousers already equipped!
    		<</if>>
    	<<else>>
    		There are no trousers in your inventory!
    	<</if>>
    <</linkreplace>><</nobr>>
    <<back>>
    
    Interestingly this works when I look up the object in SugarCube.

    This begs the question:
    Is it possible to return values from JS function or macro to SugarCube and continue working with it there? I don't like the solution that I got right now, cause it's ugly as hell, I'd rather have a JS macro that takes care of looking up values inside the array.
  • You're using the wrong operator in the <<set $item …>>. The assignment operator is to, not is, which is the strict equality/identity operator. This:
    <<set $item is getItemById($player.inventory, $trousers.id)>>
    
    Should be:
    <<set $item to getItemById($player.inventory, $trousers.id)>>
    
  • edited February 2017
    @color:green;FOUND : $returned_item.name@@color:yellow;it is not : $inventory[_i].id@@
    	<</if>>
    <</nobr>><</widget>>%/
    

    Yeah, I go that part fixed and understood. Thank you! But what about the code above? For some reason I always get returned_item undefined. To me, it appears that the third argument is not passed to the widget correctly. I'm passing an array, a ref and another ref. I'm trying to return an item and I can't get a hold of it. This is how I call the widget:
    @
    	<</if>>
    <</linkreplace>>
    <</nobr>>
    <<back>>
    

    The player has a structure something like the following:
    <<set $player = {
    	"money" : 500,
    	"inventory" : [],
    	"clothing" : 
    	{
    		"hats" : $BaseballHat,
    		"pants" : $OldTrousers,
    		"shirts" : $Tshirt,
    		"shoes" : $Sneakers
    	}
    }>>
    
    /% Player Inventory %/
    <<initContainer $player.inventory>>
    <<addToContainer $player.inventory $BaseballHat>>
    <<addToContainer $player.inventory $OldTrousers>>
    <<addToContainer $player.inventory $Tshirt>>
    <<addToContainer $player.inventory $Sneakers>>
    

    where hats, pants, shirts and shoes are basically pointing on an item in the inventory and these items have a value, isEquipped = false or true. It seems the widget approach has failed me, I can't get the item to be assigned to this slot and I can't set the isEquipped value.

    Right, now I'm restructuring the whole thing and try to do it in JavaScript by passing the player and the item to macro and look up the type of the clothing (yeah, I introduced another property, these objects are just growing) and based on that assign it to the proper slot if it hasn't been assigned yet.
  • It might be faster to list what isn't wrong with it. Try this:
    @color:green;FOUND: _inventory[_i].id / _inventory[_i].name@@color:yellow;It is not: _inventory[_i].id@@
    	<</if>>
    <</nobr>><</widget>>
    
    I can't be certain that you aren't doing something wrong elsewhere, however, that should be closer to correct at least. Among the corrections, I changed several story variables to temporary variables as it seemed like that was their intended purpose.


    Also, in your <<linkreplace>> the following line:
    		<<if $player.clothing.pants !== $trousers>>
    
    Should probably be comparing the object's IDs, rather than the objects directly. For example:
    		<<if $player.clothing.pants.id !== $trousers.id>>
    
  • edited February 2017
    oh, there's "break" inside the for loop, that's useful, is there a "continue" as well? I was also wondering how scope worked here. So, local variables start with an underscore.
    Thank you for fixing this mess!

    Though
    <<if $player.clothing.pants.id !== $trousers.id>>
    
    is not working it appears that
    player.clothing.pants
    
    is undefined and doesn't have an
    id
    
    property. I might be misunderstanding what this should do.
    << set $player = 
    {
    ...
    "clothing" : {
    ...
    "pants" : $OldTrousers,
    }
    }>>
    
    where $OldTrousers:
    
    <<set $OldTrousers = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"id" : "trousers.clothing.old",
    	"name" : "Old Trousers",
    	"count" : 1,
    	"cost" : 5,
    	"descrition" : "Old trousers with full of holes. You feel embarassed wearing it publicly.",
    	"image" : "<...>\\Disane\\<..>\\OldTrousers.jpg"
    }>>
    

    Update: Yep, this won't cut it:
    "pants" : $OldTrousers

    I need to do either this:
    <<set $player.clothing.pants to $OldTrousers>>
    
    or
    <<set $player.clothing.pants to $playerInventory[1]>>
    
    and it seems to work.
  • edited February 2017
    Now comes my question about scope in SugarCube:
    Before equipping ($player.clothing.pants):
    [object Object]
    item
    Old Trousers
    trousers.clothing.old
    
    equipping code running: 
    FOUND: trousers.clothing / Trousers\ \ 
    Old Trousers was unequipped 
    
    Trousers was equipped ($player.clothing.pants): 
    After equipping: 
    [object Object] 
    item 
    Old Trousers 
    trousers.clothing.old
    

    so basically the <<widget>> has no influence on the "outside" value of $player.clothing.pants.

    My code:
    @
    	<</if>>
    After equipping:
    $player.clothing.pants 
    $player.clothing.pants.type 
    $player.clothing.pants.name 
    $player.clothing.pants.id
    <</linkreplace>>
    <</nobr>>
    
    @color:green;FOUND: _inventory[_i].id / _inventory[_i].name@@color:yellow;It is not: _inventory[_i].id@@
    	<</if>>
    <</nobr>><</widget>>
    

    Now I tried to use $clothingSlot or $args[2] and writing their values to see if anything changes in the outside value. This wasn't the case. I must be failing to understand some key concept here about SugarCube's concept of scope.

    Normally, JS would apply the changes to the passed values. Here, the case is different.
  • Disane wrote: »
    […] I might be misunderstanding what this should do.
    << set $player = 
    {
    ...
    "clothing" : {
    ...
    "pants" : $OldTrousers,
    }
    }>>
    
    where $OldTrousers:
    <<set $OldTrousers = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"id" : "trousers.clothing.old",
    	"name" : "Old Trousers",
    	"count" : 1,
    	"cost" : 5,
    	"descrition" : "Old trousers with full of holes. You feel embarassed wearing it publicly.",
    	"image" : "<...>\\Disane\\<..>\\OldTrousers.jpg"
    }>>
    
    That should work fine, as long as you're defining $OldTrousers before $player. You cannot reference $OldTrousers within $player before you've defined it, obviously—attempting to do so will yield an undefined value, because you hadn't defined it yet.
  • edited February 2017
    That should work fine, as long as you're defining $OldTrousers before $player. You cannot reference $OldTrousers within $player before you've defined it, obviously—attempting to do so will yield an undefined value, because you hadn't defined it yet.

    Aha! That makes sense. Thank you! Can you also answer my question about scope in the case of <<widgets>>? Check my question above.

    Update:

    This JS code worked:
    Macro.add('equipClothingJS', 
    {
    	handler : function() 
    	{
    		var character = this.args[0];
    		var clothing = this.args[1];
    		
    		if(!character.hasOwnProperty('inventory'))
    		{
    			this.error('the passed character has no inventory');
    		}
    		if(!clothing.hasOwnProperty('clothingType'))
    		{
    			this.error('clothing provided has no ClothingType defined');
    		}
    		var item = getItemById(character.inventory, clothing.id);
    		if (!item)
    		{
    			this.error("could not find item in player's inventory");
    		}
    		switch(clothing.clothingType)
    		{
    			case "hats":		
    				character.clothing.hats.isEquipped = false;
    				character.clothing.hats = item;
    				item.isEquipped = true;
    				break;
    			case "shirts":
    				character.clothing.shirts.isEquipped = false;
    				character.clothing.shirts = item;
    				item.isEquipped = true;
    				break;
    			case "pants":
    				character.clothing.pants.isEquipped = false;
    				character.clothing.pants = item;
    				item.isEquipped = true;
    				break;
    			case "shoes":
    				character.clothing.shoes.isEquipped = false;
    				character.clothing.shoes = item;
    				item.isEquipped = true;
    				break;
    		}
    	}
    });
    
    Before equipping:
    [object Object]
    item
    Old Trousers
    trousers.clothing.old
    
    After equipping: 
    [object Object] 
    item 
    Trousers trousers.clothing 
    

    Now, the code is probably a piece of garbage and needs refactoring even maybe a complete rethinking, but it kinda works.

    Is there a way to achieve the same using a <<widget>>?
  • You're passing $player.clothing.pants into the widget, meaning you're passing a copy of the pants property's value. You cannot change the value of the pants property, when you pass a copy of its value—you'll only change the copy. You must pass its parent object instead—$player.clothing in this case.

    Try:
    @color:green;FOUND: _inventory[_i].id / _inventory[_i].name@@color:yellow;It is not: _inventory[_i].id@@
    	<</if>>
    <</nobr>><</widget>>
    
    Which would be used thus:
    /* <<equipClothing inventoryObj equipmentObj clothingObj clothingObjPropertyName>> */
    <<equipClothing $player.inventory $trousers $player.clothing "pants">>
    


    There are probably ways to simply that a bit.
  • edited February 2017
    I have no idea what this thread is even about anymore. All I can see is a wall of code, and I'm wondering what it's added functionality is compared to the original. o_o
  • DairynGM wrote: »
    I have no idea what this thread is even about anymore.
    It is still about Inventory systems but the last page or so is related to how @Disane wants their specific inventory system to work, which may not be how you want yours to work.

    One of the main issues faced by anyone trying to explain how to implement a non-trivial inventory system is the fact that the design quickly become very specific to the needs of the person asking for that explanation.
  • edited February 2017
    You're passing $player.clothing.pants into the widget, meaning you're passing a copy of the pants property's value. You cannot change the value of the pants property, when you pass a copy of its value—you'll only change the copy. You must pass its parent object instead—$player.clothing in this case.

    ...

    Which would be used thus:
    /* <<equipClothing inventoryObj equipmentObj clothingObj clothingObjPropertyName>> */
    <<equipClothing $player.inventory $trousers $player.clothing "pants">>
    

    There are probably ways to simply that a bit.

    Thank you! I was not aware of this. I mean, I understand the fundamental of copies and how they are different, but the fact that you needed to pass the object that held the slot, was something I have not thought of.

    Alright, so, now we need 4 arguments for the widget. That escalated quickly.

    I'll try this one out and see if its enough to finish the demo.

    @greyelf : nah, it's not the design i'm planning on rolling with. I'm merely stretching my legs to write a demo, see if I understand how SugarCube works. TheMadExile and you sir have been very helful and I'm thankful for that.
  • I'm happy to announce that TheMadExile's solution works flawlessly. I'm on my way to finish this demo. I really hope that I'll not bump into any more hindrances or if so ... well, there's still the fun part finding out what has gone wrong.

    This is going to be my first "real" Twine game, although not a full game, hehe.
  • Alright this really puts the icing on the cake for me. I have no clue what could cause this error, but he <<widget>> that I wrote works on all my other items except for the Watch, which is somehow very-very special, since according to JS it doesn't have an "id", although I defined it and I can add and remove this object from my inventory, yet, the following script tells me, there's no id to be found:
    @\
    	<</if>>\
    <</widget>>
    

    Definition of my items in StoryInit:
    <<set $player = 
    {
    	"name" : "Player",
    	"id" : "player",
    	"money" : 500,
    	"inventory" : [],
    	"clothing" : 
    	{
    		"hats" : $BaseballHat,
    		"pants" : $OldTrousers,
    		"shirts" : $Tshirt,
    		"shoes" : $Sneakers
    	}
    }>>
    
    <<set $BaseballHat = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "hats",
    	"id" : "baseball.hat",
    	"name" : "Baseball Hat",
    	"count" : 1,
    	"cost" : 50,
    	"description" : "A simple baseball hat inherited from your grandfather.",
    	"image" : "images/BaseballHat.jpg"
    }>>
    
    
    <<set $Tshirt = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "shirts",
    	"id" : "tshirt",
    	"name" : "T Shirt",
    	"count" : 1,
    	"cost" : 15,
    	"description" : "Your Tshirt. Not the most elegant piece of clothing.",
    	"image" : "images/Tshirt.jpg"
    }>>
    
    <<set $OldTrousers = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "pants",
    	"id" : "trousers.clothing.old",
    	"name" : "Old Trousers",
    	"count" : 1,
    	"cost" : 5,
    	"description" : "Old trousers with full of holes. You feel embarassed wearing it publicly.",
    	"image" : "images/OldTrousers.jpg"
    }>>
    
    <<set $Sneakers =
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "shoes",
    	"id" : "sneakers.shoes",
    	"name" : "Sneakers",
    	"count" : 1,
    	"cost" : 15,
    	"description" : "These sneakers are cheap and not too comfortable.",
    	"image" : "images/Sneakers.jpg"
    }>>
    
    <<set $GameItem = 
    {
    	"type" : "item",
    	"isEquipped" : false,
    	"id" : "item.generic",
    	"name" : "Generic Game Item",
    	"count" : 1,
    	"cost" : 1,
    	"description" : "A generic blueprint for all items"
    }>>
    
    <<set $Watch = 
    {
    	"type" : "item",
    	"clothingType" : "none",
    	"isEquipped" : false,
    	"id" : "watch.wrist",
    	"name" : "Watch",
    	"count" : 1,
    	"cost" : 50,
    	"description" : "A fancy looking watch, it looks so special compared to the other watches offered.",
    	"image" : "images/fancyWatch.jpg"
    }>>
    
    <<set $Trousers = 
    {
    	"type" : "item",
    	"isEquipped" : false,
    	"clothingType" : "pants",
    	"id" : "trousers.clothing",
    	"name" : "Trousers",
    	"count" : 1,
    	"cost" : 75,
    	"description" : "Nice trousers for you to wear.",
    	"image" : "images/trousers.jpg"
    }>>
    
    <<set $Milk = {
    	"type" : "item",
    	"isConsumable" : true,
    	"id" : "milk",
    	"name" : "Milk",
    	"count" : 1,
    	"cost" : 3,
    	"description" : "Some delicious cow milk."
    }>>
    
    /% Player Inventory %/
    <<initContainer $player.inventory>>
    <<addToContainer $player.inventory $BaseballHat>>
    <<addToContainer $player.inventory $OldTrousers>>
    <<addToContainer $player.inventory $Tshirt>>
    <<addToContainer $player.inventory $Sneakers>>
    <<set $player.clothing.hats to $player.inventory[0]>>
    <<set $player.clothing.pants to $player.inventory[1]>>
    <<set $player.clothing.shirts to $player.inventory[2]>>
    <<set $player.clothing.shoes to $player.inventory[3]>>
    

    Here's how I call the item from the passage called "Watch":
    <<inspectOwnedItem $player %Watch>>
    <<back>>
    

    I'm suspecting that the word "watch" or "Watch" has a special meaning somewhere and it escapes the script, making it crash. I had a similar script crash when I had an item named "T-Shirt". Basically the same script could not run the <<if>> statement and could not find it's <</if>> and <<else>> keywords and I had to rename the object to make it work. Every other item worked only these two did not. So, I decided to rename the T-Shirt to Tshirt and everything went fine and dandy. Now, the question is, what is so special about "Watch"? The script is telling me that this item has no "id" property.

    Any ideas?
  • edited February 2017
    Assuming that it's not a transcription error, look at how you're passing the watch's story variable to the widget very closely—hint: story variables do not start with a percent sign (%).

    As for the t-shirt. Assuming you tried naming the story variable something like $T-Shirt, then that's your problem, it's not a valid variable name.

    SEE: SugarCube v2's variables documentation.
  • wow, damn, I guess working on a small screen does have it's disadvantages. OMG, really sorry about that! What an embarrassing error on my part.
  • JavaScript question:

    So, I tried to turn my getItemById() into an inner function of all of my containers:
    Macro.add(['initContainer', 'emptyContainer'], 
    {
    	handler : function ()  
    	{
    		var container = this.args[0];
    		if (!Array.isArray(container)) {
    			return this.error('container is not an array');
    		}
    
    		container.splice(0, container.length);
    
    		// if findByID() has not been defined
    		if (!container.findByID) 
    		{
    			// define new member function findByID
    			container.findByID = function(obj) 
    			{
    				if (
    					this.length === 0 ||
    					typeof obj !== 'object' ||
    					!obj.hasOwnProperty('id')
    				) {
    					return false;
    				}
    				return !!this.find(function (item) 
    				{
    					return item.id === obj.id;
    				});
    			};
    		}
    		if(!container.getItemById)
                    {
    			container.getItemById = function(searchId)
    			{
    					var searched_item = container.find(function(item)
    					{
    						console.log("getItemById, item.id: " + item.id);
    						return item.id === searchId;
    					});
    					return searched_item;
    			};
    		}
    	}
    });
    
    // define outside container
    window.getItemById = function(searchId)
    {
    	var searched_item = container.find(function(item)
    	{
    		console.log("getItemById, item.id: " + item.id);
    		return item.id === searchId;
          });
           return searched_item;
    }
    
    // say we have 
    <<initContainer $player.inventory>>
    <<addToContainer $player.inventory $boots>> 
    //inside some JS function:
    var item1 = container.getItemById(boots.id)
    var item2 = getItemById(boots.id)
    
    // when comparing the two
    item1 === item2
    false
    

    Can anyone explain to me why is this happening?
    I tried to implement an equip/unequip mechanic and i use the container.getItemById() variant which pointed on an object that wasn't actually inside the player's inventory, so but when I used the getItemById() variant, it worked as intended and changed the item that was inside the actual container and thus worked nicely.

    I really wish to know what the differences are. I'm thinking that the member function (method) is grabbing something different, maybe a copy of the object.
  • Disane wrote: »
    		if(!container.getItemById)
                    {
    			container.getItemById = function(searchId)
    			{
    					var searched_item = container.find(function(item)
    					{
    						console.log("getItemById, item.id: " + item.id);
    						return item.id === searchId;
    					});
    					return searched_item;
    			};
    		}
    
    You're not using this, so the method is not acting upon its owner object. You need to change the following line:
    var searched_item = container.find(function(item)
    
    To the following instead:
    var searched_item = this.find(function(item)
    


    Disane wrote: »
    window.getItemById = function(searchId)
    {
    	var searched_item = container.find(function(item)
    	{
    		console.log("getItemById, item.id: " + item.id);
    		return item.id === searchId;
          });
           return searched_item;
    }
    
    That function shouldn't even be working unless you've defined a container variable someplace in-scope for it to capture. Even if you did, unless you're reassigning that container every turn, it's unlikely to be referring to what it should be and/or you think it is.

    You should probably be passing the appropriate container in as a parameter. For example:
    window.getItemById = function (container, searchId) {
    	if (!Array.isArray(container)) {
    		return null;
    	}
    	var searched_item = container.find(function (item) {
    		console.log("getItemById, item.id: " + item.id);
    		return item.id === searchId;
    	});
    	return searched_item;
    };
    



    Correct usage of both in TwineScript:
    var item1 = $player.inventory.getItemById(someId);
    var item2 = getItemById($player.inventory, someId);
    

    Correct usage of both in pure JavaScript:
    var sv    = State.varaibles;
    var item1 = sv.player.inventory.getItemById(someId);
    var item2 = getItemById(sv.player.inventory, someId);
    
  • oh, sorry, yeah
    window.getItemById() does indeed have two arguments (my bad, for incorrect source)
    window.getItemById(container, searchId)
    

    Oh wow, yeah my mistake there. I have no idea how I missed the "this" keyword...
  • Disane wrote: »
    <<set $player = 
    {
    	"name" : "Player",
    	"id" : "player",
    	"money" : 500,
    	"inventory" : [],
    	"clothing" : 
    	{
    		"hats" : $BaseballHat,
    		"pants" : $OldTrousers,
    		"shirts" : $Tshirt,
    		"shoes" : $Sneakers
    	}
    }>>
    
    <<set $BaseballHat = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "hats",
    	"id" : "baseball.hat",
    	"name" : "Baseball Hat",
    	"count" : 1,
    	"cost" : 50,
    	"description" : "A simple baseball hat inherited from your grandfather.",
    	"image" : "images/BaseballHat.jpg"
    }>>
    
    
    <<set $Tshirt = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "shirts",
    	"id" : "tshirt",
    	"name" : "T Shirt",
    	"count" : 1,
    	"cost" : 15,
    	"description" : "Your Tshirt. Not the most elegant piece of clothing.",
    	"image" : "images/Tshirt.jpg"
    }>>
    
    <<set $OldTrousers = 
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "pants",
    	"id" : "trousers.clothing.old",
    	"name" : "Old Trousers",
    	"count" : 1,
    	"cost" : 5,
    	"description" : "Old trousers with full of holes. You feel embarassed wearing it publicly.",
    	"image" : "images/OldTrousers.jpg"
    }>>
    
    <<set $Sneakers =
    {
    	"type" : "item",
    	"isEquipped" : true,
    	"clothingType" : "shoes",
    	"id" : "sneakers.shoes",
    	"name" : "Sneakers",
    	"count" : 1,
    	"cost" : 15,
    	"description" : "These sneakers are cheap and not too comfortable.",
    	"image" : "images/Sneakers.jpg"
    }>>
    
    <<set $GameItem = 
    {
    	"type" : "item",
    	"isEquipped" : false,
    	"id" : "item.generic",
    	"name" : "Generic Game Item",
    	"count" : 1,
    	"cost" : 1,
    	"description" : "A generic blueprint for all items"
    }>>
    
    <<set $Watch = 
    {
    	"type" : "item",
    	"clothingType" : "none",
    	"isEquipped" : false,
    	"id" : "watch.wrist",
    	"name" : "Watch",
    	"count" : 1,
    	"cost" : 50,
    	"description" : "A fancy looking watch, it looks so special compared to the other watches offered.",
    	"image" : "images/fancyWatch.jpg"
    }>>
    
    <<set $Trousers = 
    {
    	"type" : "item",
    	"isEquipped" : false,
    	"clothingType" : "pants",
    	"id" : "trousers.clothing",
    	"name" : "Trousers",
    	"count" : 1,
    	"cost" : 75,
    	"description" : "Nice trousers for you to wear.",
    	"image" : "images/trousers.jpg"
    }>>
    
    <<set $Milk = {
    	"type" : "item",
    	"isConsumable" : true,
    	"id" : "milk",
    	"name" : "Milk",
    	"count" : 1,
    	"cost" : 3,
    	"description" : "Some delicious cow milk."
    }>>
    

    I don't mean to intrude, or even de-rail, but I've been following this topic closely as I'm nearing the point that I am going to need to start implementing an inventory system of my own. And I was curious what the advantages of, or rather why individual objects for each individual item?

    For example, I have my npcs setup in the following way:
    <<set $npcs to {
    	players_mother : {
    		name			: "Courtney",
    		age				: 36,
    		height			: 163,
    		hairColor		: 0,
    		eyeColor		: 0,
    		skinTone		: 0
    	},
    	players_brother : {
    		name			: "Jason",
    		age				: 16,
    		height			: 163,
    		hairColor		: 0,
    		eyeColor		: 0,
    		skinTone		: 0
    	}
    }>>
    

    Which I can then reference as $npcs["players_mother"] which makes it easy when developing a story and helps avoid "variable creep". So to use your item declarations I would have used something like:
    <<set $items to {
    			milk : {
    				type			: "item",
    				isConsumable	: true,
    				name			: "Milk",
    				count			: 1,
    				cost			: 3,
    				desc			: "Some delicious cow milk."
    			},
    			trousers : {
    				type			: "item",
    				isEquipped		: false,
    				clothingType	: "pants",
    				id				: "trousers.clothing",
    				name			: "Trousers",
    				count			: 1,
    				cost			: 75,
    				desc			: "Nice trousers for you to wear.",
    				image			: "images/trousers.jpg"
    			}
    	}>>
    

    While still giving the player a $player.inventory container for them to be added to.

    With this being just a rough example, but I am curious why one implementation over the other?
  • edited March 2017
    It's mostly a design decision that me and the op decided to go for our own games. If you are planning on having more than one object of the same type - say for example: 5x rocks and you don't want each individual rock to be displayed in your inventory, taking up precious screen space and naming each rock individually: rock0, rock1.. rockN-1, rockN then going for a quantity based item system might be better.

    I also need to add that not everything about the inventory system (that I had in mind) is covered here. In my game, I'm using clones of the items that I have, since I like to keep unique instances in memory, so I can adjust my item's properties individually, without "corrupting" the properties of the rest of the items with the same id and name.
    Now, if one of the item's have meaningful differences then I tend to assign a different ID to them dynamically. So this way if I have 3 "Silver Sword's" and I decide to enchant one of them, my "Silver Sword" after adding an echantemnt to it can become an "Enchanted Silver Sword", which has a higher price value and does more damage to certain enemy types. Now, I will have 2 "Silver Swords" in my inventory and 1 "Enchanted Silver Sword". This way I also prevent loosing the enchantment (and the enchantment properties) when moving items from a container over to the player's inventory or selling or buying items.

    If you are not building RPG elements into your game, then this kind of inventory system might be an overkill, it is you the writer/designer/programmer who needs to decide in the design phase of your game/story what you want to go for.
Sign In or Register to comment.