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)

edited January 2017 in Help! with 2.0
Hi all,

Right now, I've got a very makeshift inventory system going. Basically, I've created a variable for the player's weapon and armor ($player.weapon and $player.armor respectively), and I'm changing the value depending on what they've got in the slot.

However, it occurs to me that this isn't the most flexible approach I could be using, and doesn't leave a lot of growth room for different items or scenarios. E.g. Having multiple weapons in your inventory, dropping weapons, selling weapons, using one-use items, etc. Ideally, the inventory would be accessible from the side-menu - I already know how to create links in it and have already added a few.

I'm sure I could probably come up with some sort of system to do this. But I'm pretty sure given the prevalence of SugarCube games, I'd probably be reinventing the wheel.

In short, does anyone have any inventory system code that I could take and throw into my game? I've used Google to scour the site, but I haven't run across any inventory system code that seems explicitly safe to use for Sugarcube 2.0.

A link to an existing explanation is fine if I'm just doubling up on a common request, which I'm fairly sure I am. :)
«1

Comments

  • Let me first say that you are unlikely to find something to meet your exact requirements. That said, you should certainly be able to find something which you can built upon.

    You may wish to start looking at the Making sidebar Inventory items droppable thread, specifically at my first post about an object-based version of the inventory system detailed within.
  • edited January 2017
    Thank you! I agree with you about the exact requirements, and I'm happy to build upon a solid base and muddle my way along. :)

    EDIT: Though after entering the code from this thread in a test story, it seems to be breaking quite a bit. I've entered in a few different variants and it's still happening. No idea what I'm doing wrong.
  • Any idea what the bug is? I'll keep trying to figure it out in the meantime.
  • edited January 2017
    AHAH! I think I figured it out. I hadn't used <<initInv>>. Threw it into StoryInit and a preliminary look shows it working okay.

    Now it's just figuring out a way to check for and use <<if>> functions with inventory items to make things happen. I believe in the original it was a custom macro called <<isInInv>>.
  • edited January 2017
    Ugh. Tried about three dozen combinations and I can't seem to interact with the inventory system at all to do what I want. E.g. Check if individual items are in the inventory, then use this as a condition for an <<if>> statement.

    This basically means I've got an inventory system which shows items that I can pick up and drop, but I can't create any meaningful interactions with said items.

    After playing around with this for the last 48 hours, I'm going to shelve this code for now and continue working on the meat of my game, unless anyone's got any suggestions. I'm starting to feel like I need a bit more knowledge of Javascript.

    Inventory System Javascript:
    window.getInv = function () {
    	return State.variables.inventory;
    };
    
    Macro.add([ "initInv", "emptyInv" ], {
    	handler : function () {
    		State.variables.inventory = [];
    	}
    });
    
    Macro.add("addToInv", {
    	handler : function () {
    		if (this.args.length === 0) {
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[0];
    		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    			return this.error("inventory item malformed");
    		}
    		var idx = State.variables.inventory.findIndex(function (item) {
    			return item.id === obj.id;
    		});
    		if (idx === -1) {
    			State.variables.inventory.push(obj);
    		}
    		else {
    			State.variables.inventory[idx].count++;
    		}
    	}
    });
    
    Macro.add("removeFromInv", {
    	handler : function () {
    		if (this.args.length === 0) {
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[0];
    		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    			return this.error("inventory item malformed");
    		}
    		var idx = State.variables.inventory.findIndex(function (item) {
    			return item.id === obj.id;
    		});
    		if (idx !== -1) {
    			if (State.variables.inventory[idx].count > 1) {
    				State.variables.inventory[idx].count--;
    			}
    			else {
    				State.variables.inventory.splice(idx , 1);
    			}
    		}
    	}
    });
    
    Macro.add("inv", {
    	handler : function () {
    		if (State.variables.inventory.length === 0) {
    			new Wikifier(this.output, 'nothing');
    		} else {
    			new Wikifier(
    				this.output,
    				State.variables.inventory.map(function (item) {
    					var temp = [];
    					for (var i = 0; i < item.count; i++) {
    						temp.push(item.name);
    					}
    					return temp.join(', ');
    				}).join(', ')
    			);
    		}
    	}
    });
    
    Macro.add("listInv", {
    	handler : function () {
    		var wrapper = document.createElement("div");
    		$(wrapper)
    			.attr("id", "inventory-list")
    			.appendTo(this.output);
    		this.self.buildList(wrapper);
    	},
    	buildList : function (wrapper) {
    		var	self = this;
    		if (State.variables.inventory.length === 0) {
    			new Wikifier(wrapper, "nothing");
    		} else {
    			State.variables.inventory.forEach(function (item) {
    				var	entry		=	document.createDocumentFragment(),
    						dropEl	=	document.createElement("a");
    				$(entry)
    					.append(item.name)
    					.append("\u00A0")
    					.append("x" + item.count)
    					.append("\u00A0")
    					.append(dropEl)
    					.append("<br>");
    				$(dropEl)
    					.text("(drop)")
    					.ariaClick(function () {
    						var obj = item;
    						var idx = State.variables.inventory.findIndex(function (item) {
    							return item.id === obj.id;
    						});
    						if (idx !== -1) {
    							if (State.variables.inventory[idx].count > 1) {
    								State.variables.inventory[idx].count--;
    							}
    							else {
    								State.variables.inventory.splice(idx , 1);
    							}
    						}
    						$(wrapper)
    							.empty()
    							.append(self.buildList.call(self, wrapper));
    					});
    				$(wrapper).append(entry);
    			});
    		}
    	}
    });
    
    
  • If you look at the addToInv macro's code you will see the following four lines, which are used to determine if an obj with a particular ID already exist within the inventory array.
    var idx = State.variables.inventory.findIndex(function (item) {
    	return item.id === obj.id;
    });
    if (idx === -1) {
    


    You could use a slightly modified version of that code combinded with an <<if>> macro within a passage.
    <<set
    $apples to {
    	id   : "apples.fresh",
    	name : "Bag of apples",
    	cost : 5
    },
    $poisonedApples to {
    	id   : "apples.poisoned",
    	name : "Bag of apples",
    	cost : 5
    }
    >>
    
    <<addToInv $poisonedApples>>
    
    <<if $inventory.findIndex(function (item) {
    	return item.id === State.variables.poisonedApples.id;
    }) > -1>>I found some poisoned apples<<else>>There are no poisoned apples<</if>>
    


    To make the above <<if>> macro a little less complex you could add a findByID helper method to the $inventory array during it's initialisation by changing the initInv / emptyInv macro(s) code.

    note: A more experienced Javascript coder may be able to supply a better implementation of the following method.
    Macro.add([ "initInv", "emptyInv" ], {
    	handler : function () {
    		State.variables.inventory = [];
    		
    		State.variables.inventory.findByID = function(obj) {
    			if (this.length === 0) {
    				return false;
    			}
    			if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    				return false;
    			}
    			var idx = this.findIndex(function (item) {
    				return item.id === obj.id;
    			});
    			return (idx !== -1);
    		};
    	}
    });
    


    The follow example uses the above findByID helper method.
    <<set
    $apples to {
    	id   : "apples.fresh",
    	name : "Bag of apples",
    	cost : 5
    },
    $poisonedApples to {
    	id   : "apples.poisoned",
    	name : "Bag of apples",
    	cost : 5
    }
    >>
    
    <<addToInv $poisonedApples>>
    
    <<if ! $inventory.findByID($apples)>>There are no apples<<else>>I found some apples<</if>>
    
    <<if $inventory.findByID($poisonedApples)>>I found some poisoned apples<<else>>There are no poisoned apples<</if>>
    
  • greyelf wrote: »
    If you look at the addToInv macro's code you will see the following four lines, which are used to determine if an obj with a particular ID already exist within the inventory array....

    The follow example uses the above findByID helper method.

    Thank you very much! I've added this into my code and it's working very well. This should be enough to keep me moving forward with the inventory system. :)

  • edited January 2017
    Hi,

    Just in case :

    The object in the inventory passage will be a link (don't forget to name it like the passage you create for the object). Can be usefull in some context.
    The findByID is inside too.
    // Inventory
    window.getInv = function () {
    	return State.variables.inventory;
    };
    
    Macro.add([ "initInv", "emptyInv" ], {
    	handler : function () {
    		State.variables.inventory = [];
    		
    		State.variables.inventory.findByID = function(obj) {
    			if (this.length === 0) {
    				return false;
    			}
    			if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    				return false;
    			}
    			var idx = this.findIndex(function (item) {
    				return item.id === obj.id;
    			});
    			return (idx !== -1);
    		};
    	}
    });
    
    Macro.add("addToInv", {
    	handler : function () {
    		if (this.args.length === 0) {
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[0];
    		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    			return this.error("inventory item malformed");
    		}
    		var idx = State.variables.inventory.findIndex(function (item) {
    			return item.id === obj.id;
    		});
    		if (idx === -1) {
    			State.variables.inventory.push(obj);
    		}
    	}
    });
    
    Macro.add("removeFromInv", {
    	handler : function () {
    		if (this.args.length === 0) {
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[0];
    		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    			return this.error("inventory item malformed");
    		}
    		var idx = State.variables.inventory.findIndex(function (item) {
    			return item.id === obj.id;
    		});
    		if (idx !== -1) {
    			State.variables.inventory.splice(idx , 1);
    		}
    	}
    });
    
    Macro.add("inv", {
    	handler : function () {
    		if (State.variables.inventory.length === 0) {
    			new Wikifier(this.output, 'nothing');
    		} else {
    			new Wikifier(
    				this.output,
    				'[[' + State.variables.inventory.map(function (item) {
    					return item.name;
    				}).join(']]<br>[[') + ']]');
    		}
    	}
    });
    
    Macro.add("listInv", {
    	handler : function () {
    		var wrapper = document.createElement("div");
    		$(wrapper)
    			.attr("id", "inventory-list")
    			.appendTo(this.output);
    		this.self.buildList(wrapper);
    	},
    	buildList : function (wrapper) {
    		var	self = this;
    		if (State.variables.inventory.length === 0) {
    			new Wikifier(wrapper, "nothing");
    		} else {
    			State.variables.inventory.forEach(function (item) {
    				var	entry  = document.createDocumentFragment(),
    					dropEl = document.createElement("a");
    				$(entry)
    					.append(item.name)
    					.append("\u00A0")
    					.append(dropEl)
    					.append("<br>");
    				$(dropEl)
    					.text("(drop)")
    					.ariaClick(function () {
    						var obj = item;
    						var idx = State.variables.inventory.findIndex(function (item) {
    							return item.id === obj.id;
    						});
    						if (idx !== -1) {
    							State.variables.inventory.splice(idx , 1);
    						}
    						$(wrapper)
    							.empty()
    							.append(self.buildList.call(self, wrapper));
    					});
    				$(wrapper).append(entry);
    			});
    		}
    	}
    });
    // End Inventory Macros
    


    Thx for all the person who create this wonderfull inventory system.
  • Alianna wrote: »
    Hi,
    The object in the inventory passage will be a link (don't forget to name it like the passage you create for the object). Can be usefull in some context.
    The findByID is inside too.

    Sorry, I don't understand. わかりません o_o
  • They modified the <<inv>> macro so that the listed item names are passage links and are separated by line breaks, instead of commas. For example, instead of generating:
    Apple, Knife, Soap
    
    Their version generates:
    [[Apple]]<br>[[Knife]]<br>[[Soap]]
    
  • Oh neat! That might open up a lot of options for me. I'll have to play around with this later tonight. ^_^
  • edited January 2017
    I'm playing around with combining Alianna's code with a variant of TheMadExile's. While Alianna's allows for listed item names as passage links, the code below allows for visible multiple item pickup and dropping.

    Trying to figure out a way to combine the two to get a listInv that has items as passage links while retaining the number of items next to it. E.g. Iron sword x2. I'm going to poke around at this for a while, see how many error messages I can take before I move on to the next thing.
    window.getInv = function () {
    	return State.variables.inventory;
    };
    
    Macro.add([ "initInv", "emptyInv" ], {
    	handler : function () {
    		State.variables.inventory = [];
    		
    		State.variables.inventory.findByID = function(obj) {
    			if (this.length === 0) {
    				return false;
    			}
    			if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    				return false;
    			}
    			var idx = this.findIndex(function (item) {
    				return item.id === obj.id;
    			});
    			return (idx !== -1);
    		};
    	}
    });
    
    Macro.add("addToInv", {
    	handler : function () {
    		if (this.args.length === 0) {
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[0];
    		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    			return this.error("inventory item malformed");
    		}
    		var idx = State.variables.inventory.findIndex(function (item) {
    			return item.id === obj.id;
    		});
    		if (idx === -1) {
    			State.variables.inventory.push(obj);
    		}
    		else {
    			State.variables.inventory[idx].count++;
    		}
    	}
    });
    
    Macro.add("removeFromInv", {
    	handler : function () {
    		if (this.args.length === 0) {
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[0];
    		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
    			return this.error("inventory item malformed");
    		}
    		var idx = State.variables.inventory.findIndex(function (item) {
    			return item.id === obj.id;
    		});
    		if (idx !== -1) {
    			if (State.variables.inventory[idx].count > 1) {
    				State.variables.inventory[idx].count--;
    			}
    			else {
    				State.variables.inventory.splice(idx , 1);
    			}
    		}
    	}
    });
    
    Macro.add("inv", {
    	handler : function () {
    		if (State.variables.inventory.length === 0) {
    			new Wikifier(this.output, 'nothing');
    		} else {
    			new Wikifier(
    				this.output,
    				State.variables.inventory.map(function (item) {
    					var temp = [];
    					for (var i = 0; i < item.count; i++) {
    						temp.push(item.name);
    					}
    					return temp.join(', ');
    				}).join(', ')
    			);
    		}
    	}
    });
    
    Macro.add("listInv", {
    	handler : function () {
    		var wrapper = document.createElement("div");
    		$(wrapper)
    			.attr("id", "inventory-list")
    			.appendTo(this.output);
    		this.self.buildList(wrapper);
    	},
    	buildList : function (wrapper) {
    		var	self = this;
    		if (State.variables.inventory.length === 0) {
    			new Wikifier(wrapper, "nothing");
    		} else {
    			State.variables.inventory.forEach(function (item) {
    				var	entry		=	document.createDocumentFragment(),
    						dropEl	=	document.createElement("a");
    				$(entry)
    					.append(item.name)
    					.append("\u00A0")
    					.append("x" + item.count)
    					.append("\u00A0")
    					.append("<br>");
    				$(dropEl)
    					.text("(drop)")
    					.ariaClick(function () {
    						var obj = item;
    						var idx = State.variables.inventory.findIndex(function (item) {
    							return item.id === obj.id;
    						});
    						if (idx !== -1) {
    							if (State.variables.inventory[idx].count > 1) {
    								State.variables.inventory[idx].count--;
    							}
    							else {
    								State.variables.inventory.splice(idx , 1);
    							}
    						}
    						$(wrapper)
    							.empty()
    							.append(self.buildList.call(self, wrapper));
    					});
    				$(wrapper).append(entry);
    			});
    		}
    	}
    });
    
    
    

    Currently kind of cheating by using the current code to reference items to pull things up.
    //* Passage Name - EquipWeapon
    
    <<nobr>><<set 
    
    $startSword to {
    	id		:	"sword.start",
    	count	:	1,
    	name	:	"Rusted Sword",
    	attack	:	0,
    	damage	:	0,
    	cost	:	5
    }>>
    <<addToInv $startSword>>
    <<if ndef $player>><<set $player to {name: "Test", weapon: "", weaponAttack: 0, weaponDamage: 0}>><</if>>
    
    <<if $inventory.findByID($startSword)>><<if $player.weapon isnot $startSword.name>>
    <<click [[$startSword.name|EquipWeapon]]>>
    <<set $player.weapon to $startSword.name>>
    <<set $player.weaponAttack to $startSword.attack>>
    <<set $player.weaponDamage to $startSword.damage>><</click>><<else>>$player.weapon (Equipped)<</if>><</if>><<endnobr>>
    
    
  • DairynGM wrote: »
    Trying to figure out a way to combine the two to get a listInv that has items as passage links while retaining the number of items next to it. E.g. Iron sword x2. […]
    Try the following: (rewritten a bit)
    Macro.add('listInv', {
    	handler : function () {
    		var wrapper = document.createElement('div');
    
    		$(wrapper)
    			.attr('id', 'inventory-list')
    			.appendTo(this.output);
    		this.self.buildList(wrapper);
    	},
    	buildList : function (wrapper) {
    		var self = this;
    		var list = document.createDocumentFragment();
    
    		if (State.variables.inventory.length === 0) {
    			$(list).text('nothing');
    		}
    		else {
    			State.variables.inventory.forEach(function (item) {
    				var dropEl = document.createElement('a');
    				$(dropEl)
    					.text('(drop)')
    					.ariaClick(function () {
    						var cur = item;
    						var idx = State.variables.inventory.findIndex(function (item) {
    							return item.id === cur.id;
    						});
    
    						if (idx !== -1) {
    							if (State.variables.inventory[idx].count > 1) {
    								State.variables.inventory[idx].count--;
    							}
    							else {
    								State.variables.inventory.splice(idx , 1);
    							}
    						}
    
    						self.buildList(wrapper);
    					});
    
    				$(document.createDocumentFragment())
    					.wiki('[[' + item.name + ']]')
    					.append('\u00A0')
    					.append('\u00D7' + item.count)
    					.append('\u00A0')
    					.append(dropEl)
    					.append('<br>')
    					.appendTo(list);
    			});
    		}
    
    		$(wrapper)
    			.empty()
    			.append(list);
    	}
    });
    
    NOTE: I was unsure if you meant to remove the "(drop)" link, so I added it back. If you did, then simply pulling its append it not the way to cleanly remove it.
  • Try the following: (rewritten a bit)...

    NOTE: I was unsure if you meant to remove the "(drop)" link, so I added it back. If you did, then simply pulling its append it not the way to cleanly remove it.

    Thank you so much! :D And yes, it was intentional. I was trying to poke around with the script to cut that feature out. The reason was this:

    * Currently, equipment is 'equipped' by checking if it exists in the inventory, then duplicating the item values to an 'equipped item' variable. This way, I can easily use <<if>> statements with the 'equipped item' variable, and to equip something else, you just overwrite the details with the item you want to equip, or clear them to unequip.

    * If someone dropped an equipped item, you'd still have all its properties in the equipped item variable. E.g. You'd still have a sword equipped even though you dropped it.

    Cutting out the ability to drop something and limiting discarding to selling them at a store seemed like the best way to deal with this without inventing some new macro or significantly altering the existing ones, something my current skillset doesn't allow for.
  • edited January 2017
    DairynGM wrote: »
    […] And yes, it was intentional. I was trying to poke around with the script to cut that feature out. […]
    Ah. Without the need to dynamically repopulate the list—required by the "(drop)" links—the macro could be simplified a bit. For example, this version removes both the "(drop)" link code and the extra complexity required because of it:
    Macro.add('listInv', {
    	handler : function () {
    		var $list = $(document.createDocumentFragment());
    
    		if (State.variables.inventory.length === 0) {
    			$list.text('nothing');
    		}
    		else {
    			State.variables.inventory.forEach(function (item) {
    				$list
    					.wiki('[[' + item.name + ']]')
    					.append('\u00A0\u00D7' + item.count)
    					.append('<br>');
    			});
    		}
    
    		$list.appendTo(this.output);
    	}
    });
    
  • WOW. That's just a tad shorter. =O
  • A really elegenat solution, I need to say. Is there a way to add a picture to the data structure and show it when the items get listed?

    I assume I need something like the following:
    <<set $mytrustyscrewdriver = {
    	id		:	"screwdriver.trusty",
    	count	:	1,
    	name	:	"My trusty screwdriver",
    	cost	:	15 
            image : "<Drive_Letter>:\\Disane\\MyPictures\\screwdriver.jpg"
    }>>    
    

    and then check if the item has an image property when adding it to the list and then append it to the wiki at the end, like this?
    Macro.add('listInv', {
    	handler : function () {
    		var $list = $(document.createDocumentFragment());
    
    		if (State.variables.inventory.length === 0) 
    		{
    			$list.text('nothing');
    		}
    		else 
    		{
    			State.variables.inventory.forEach(function (item) 
    			{
    				$list
    					.wiki('[[' + item.name + ']]')
    					.append('\u00A0\u00D7' + item.count)
    					.append('<br>')
    					.append('<img src=\"' + item.image + '\">');
    			});
    		}
    		$list.appendTo(this.output);
    	}
    });
    

    Well, this worked for me:
    /% Passage Name: Workshop %/
    
    <<nobr>>
    <span id="drawer">
    	<<link "Open your drawer">>
    		<<replace "#drawer">>
    		  <<print "The drawer is opened!">>
    		  <<if $inventory.findByID($mytrustyscrewdriver )>>
    		  	<<link "take screwdriver">>/
    			  	<<replace "#drawer">>
    				  <<print " The screwdriver is taken.">>
    				  <<addToInv $startScrewdriver>>
    				  <<set $has_screwdriver = true>>
    				<</replace>>
    			  <</link>>
    		<</if>>
    		<</replace>>
    	<</link>>
    </span>
    <</nobr>>
    
    <<nobr>>
    <span id="inv">
    		<<print "there's nothing to see here">>
    </span>
    <<link "open inventory">>
    	<<replace "#inv">>
    		<<listInv>>
    		<<if ! $inventory.findByID($mytrustyscrewdriver )>>You can't find your screwdriver in your pocket!<<else>>You've found your trusty screwdriver in your pocket!<</if>>
    	<</replace>>
    <</link>>
    <</nobr>>
    
  • edited January 2017
    I parsed the wrong passage code in, so here we go again:
    You have arrived in your workshop. There's a drawer next to you.
    <<nobr>>
    <<set $mytrustyscrewdriver = {
    	id		:	"screwdriver.trusty",
    	count	:	1,
    	name	:	"Your trusty screwdriver",
    	cost	:	5,
    	image   :   "<Drive_Letter_goes here>:\\<Folder_name_goes_here>\\Disane\\MyPictures\\screwdriver.jpg"
    }>>
    
    <span id="drawer">
    	<<link "Open your drawer">>
    		<<replace "#drawer">>
    		  <<print "The drawer is opened!">>
    		  <<if ! $inventory.findByID($startScrewdriver)>>
    		  	<<link "take screwdriver">>/
    			  	<<replace "#drawer">>
    				  <<print " You pick up your trusty screwdriver.">>
    				  <<addToInv $mytrustyscrewdriver>>
    				<</replace>>
    			  <</link>>
    		<</if>>
    		<</replace>>
    	<</link>>
    </span>
    <</nobr>>
    
    <span id="inv">
    		<<print "Your pocket can be found here:">>
    </span>
    <<link "search pocket for inventory">>
    	<<replace "#inv">>
    		<<listInv>>
    		<<if ! $inventory.findByID($mytrustyscrewdriver)>>You can't find your trusty screwdriver in your pocket!<<else>>You've found your trusty screwdriver in your pocket!<</if>>
    	<</replace>>
    <</link>>
    
  • Disane wrote: »
    […] Is there a way to add a picture to the data structure and show it when the items get listed?

    I assume I need something like the following:
    <<set $mytrustyscrewdriver = {
    	id		:	"screwdriver.trusty",
    	count	:	1,
    	name	:	"My trusty screwdriver",
    	cost	:	15 
            image : "<Drive_Letter>:\\Disane\\MyPictures\\screwdriver.jpg"
    }>>    
    
    As long as you only use that path for Twine 2 Play/Test testing purposes.

    Disane wrote: »
    and then check if the item has an image property when adding it to the list and then append it to the wiki at the end, like this?
    			State.variables.inventory.forEach(function (item) 
    			{
    				$list
    					.wiki('[[' + item.name + ']]')
    					.append('\u00A0\u00D7' + item.count)
    					.append('<br>')
    					.append('<img src=\"' + item.image + '\">');
    			});
    
    First, you're pointless escaping the double quotes there. Beyond that, assuming there were multiple items—e.g. a screwdriver and a hammer—that would make the markup look like following:
    [[My trusty screwdriver]] ×1<br><img src="…">[[My trusty hammer]] ×1<br><img src="…">
    
    Which is probably not what you want.

    I'd probably suggest adding a break, at least, after the image:
    			State.variables.inventory.forEach(function (item) {
    				$list
    					.wiki('[[' + item.name + ']]')
    					.append('\u00A0\u00D7' + item.count)
    					.append('<br>')
    					.append('<img src="' + item.image + '">')
    					.append('<br>');
    			});
    



    Additionally. You're pointless using the <<print>> macro in several places.

    For example, this:
    <span id="inv">
    		<<print "Your pocket can be found here:">>
    </span>
    
    Could, and probably should, be the following:
    <span id="inv">Your pocket can be found here.</span>
    


    And this:
    		  	<<link "take screwdriver">>/
    			  	<<replace "#drawer">>
    				  <<print " You pick up your trusty screwdriver.">>
    				  <<addToInv $mytrustyscrewdriver>>
    				<</replace>>
    			  <</link>>
    
    Where you also have a forward slash inside the <<link>> which is doing nothing absolutely nothing. Could, and probably should, be the following:
    			<<link "take screwdriver">>
    				<<replace "#drawer">>
    					You pick up your trusty screwdriver.
    					<<addToInv $mytrustyscrewdriver>>
    				<</replace>>
    			<</link>>
    
  • edited February 2017
    Thanks for the correction. I guess using <<linkreplace>> could also help out in shortening the whole thing.About the overuse of print. Sorry, about those I'm using so many prog. languages nowadays that I get confused. It's crazy how many technologies one needs to learn to be somewhat competitive on the market. I guess I can simply write the text that I need, the same way as editing HTML.
  • hey,
    I'm trying to make a sense out of the code you out here. So, I decided to write a function that returns the count of items that hold the same "name" property. Now, after some experimentation I understood that "id" property is unique across all items, even the ones that carry the same name, bare different "id" property. Which makes sense, since this property can be used to uniquely identify items inside the inventory.

    Now, all the above could be as false as it is. After I tried implementing a counter function, i did not get the results that would back my understanding. Here are two of my experiments that have all failed:

    Normally the functions should return 2 but they either return nothing or 1. No, matter how many items with the same name are placed in the inventory. Your functions counts them correctly though.
    /% result of <<print countItemByName($fancyWatch.name)>> %/
    Item quantity: 1
    
    /% result of <<print countItemById($fancyWatch.id)>> %/
    Item quantity:
    
    window.countItemByName = function(item_name)
    {
    	var count = 0; 
    	State.variables.playerInventory.forEach(
    	function(item)
    	{
    		if(item.hasOwnProperty("name"))
    		{
    			if (item.name == item_name)
    			{
    				count++;
    			}
    		}
    	});
    	return count;
    };
    
    window.countItemById = function(item_id)
    {
    	State.variables.playerInventory.forEach(
    	function(item)
    	{
    		if(item.hasOwnProperty("id") || item.hasOwnProperty("count"))
    		{
    			if (item.id == item_id)
    			{
    				return item.count;
    			}
    		}
    	});
    };
    

    Could you please explain where I've failed?
  • edited February 2017
    Disane wrote: »
    Could you please explain where I've failed?
    That inventory system is design to only allow a single instance of any particulate item object (based on it's ID) to be stored within it. Each of the item object's has a count property and this property is incremented / decremented when an item is added / removed from the inventory.
  • @greyelf : I kind of suspected that, the problem is that I can't properly access the count information. It always yields "1" no matter what. What I want here is access to that count for each item that is in the inventory, so I can add that info to the passage that describes the item.
  • edited February 2017
    Something like this would be pretty awesome to have
    $inventory.lookUpItem("Fancy Watch").count
    /% or %/
    $inventory.countItems("Fancy Watch")
    

    which then would yield the number of items have the same name and are inside the player's inventory.

    I guess I might need to run with my own implementation then. This one for some reason doesn't operate as I imagined.

    Update: Scratch that, I think, I've suceeded with this one:
    window.countItemByName = function(item_name)
    {
            var item_count = 0;
    	State.variables.playerInventory.forEach(
    	function(item)
    	{
    		if(item.hasOwnProperty("name") || item.hasOwnProperty("count"))
    		{
    			if (item.name == item_name)
    			{
    				item_count = item.count;
    			}
    		}
    	});
            return item_count;
    };
    

    Results:
    460a74f1ee.jpg
    460aaab64f.jpg460ad5202e.jpg
  • edited February 2017
    UPDATE: This post was started before your last one.

    Your problem is that you have fundamentally misunderstood how the <Array>.forEach() method works. Its callback is not a predicate, it does not allow early termination, and it has no return value—other than undefined.

    Try the following for your countItemById() function, which uses the <Array>.find() method:
    window.countItemById = function (searchId) {
    	var item = State.variables.playerInventory.find(function (item) {
    		return item.id === searchId;
    	});
    	return item ? item.count : 0;
    };
    
    NOTE: The predicate function assumes that all members of the $playerInventory array will be objects that conform to the "item object" format shown in this thread—i.e. they must, at least, contain the properties: id and count.
  • edited February 2017
    I guess foreach does work differently here. Hmmm, the example is really useful! Thank you for posting it TheMadExile!
  • edited February 2017
    Alright, my next little project is to move items from one container to the other. Let's say I have a cabinet, a shop (where you can sell used stuff and buy new stuff), my character's inventory and my player character has slots on him for clothing like pants, shirts and shoes. You can use one empty slot to place an item there or switch out an already equipped item with a new item.
    <<set $Cabinet : 
    {
        "id" : "cabinet",
        "inventory" : [],
        "description" : "An old cabinet in which you store your clothes and stuff."
    }>>
    
    /% shop container goes here %/
    
    <<set $Player = {
            "id" : "player",
            "name" : "Thomas",
    	"money" : 500,
            "inventory" : [],
    	"clothing" : 
    	{
    		"hats" : $BaseballHat,
    		"pants" : $OldTrousers,
    		"shirt" : $Tshirt,
    		"shoes" : $Sneakers
    	}
    }>>
    
    <<set $BaseballHat = 
    {
    	"type" : "item",
    	"id" : "baseball.hat",
    	"name" : "Baseball Hat",
    	"count" : 1,
    	"cost" : 50,
    	"description" : "A simple baseball hat inherited from your grandfather." 
    }>>
    
    <<set $Tshirt = 
    {
    	"type" : "item",
    	"id" : "shirt.t",
    	"name" : "T-Shirt",
    	"count" : 1,
    	"cost" : 15,
    	"description" : "Your Tshirt. Not the most elegant piece of clothing."
    }>>
    
    <<set $OldTrousers = 
    {
    	"type" : "item",
    	"id" : "trousers.clothing",
    	"name" : "Trousers",
    	"count" : 1,
    	"cost" : 5,
    	"descrition" : "Old trousers with full of holes. You feel embarrassed wearing it publicly."
    }>>
    
    <<set $Sneakers =
    {
    	"type" : "item",
    	"id" : "sneakers.shoe",
    	"name" : "Sneakers",
    	"count" : 1,
    	"cost" : 15,
    	"description" : "These sneakers are cheap and not too comfortable."
    }>>
    
    <<initInv>>
    
    <<set $GameItem = 
    {
    	"type" : "item",
    	"id" : "item.generic",
    	"name" : "Generic Game Item",
    	"count" : 1,
    	"cost" : 1,
    	"description" : "A generic blue-print for all items"
    }>>
    
    <<set $FancyWatch = 
    {
    	"type" : "item",
    	"id" : "watch.fancy",
    	"name" : "Fancy Watch",
    	"count" : 1,
    	"cost" : 50,
    	"description" : "A fancy looking watch, it looks so special compared to the other watches offered.",
    	"image" : "...\\Disane\\Pics\\fancyWatch.jpg"
    }>>
    
    <<set $Trousers = 
    {
    	"type" : "item",
    	"id" : "trousers.clothing",
    	"name" : "Trousers",
    	"count" : 1,
    	"cost" : 75,
    	"descrition" : "Nice trousers for you to wear.",
    	"image" : "...\\Disane\\Pics\\trousers.jpg"
    }>>
    

    I think I need a function that takes the name of the item I wanna transfer, the container from which I'm transferring the item and the container to which I transfer my item.

    window.transferItem("item_name", $source_container, $target_container);

    Is there a way to move objects from one place to the other? let's say by passing a reference but then removing that same association in the container from which the item was taken from? Do you just make deep copies of the objects (if possible) and remove the ones you don't need in this case?

    So far, i'm thinking about doing the following:
    look up the item by it's name inside the source array. Inspect if the source inventory and see if it has more than one item inside (look up by name then look at the count property). If so decrement the count property of that item and place a reference of that item inside the destination array (if it doesn't exist already, if so, just look it up by name and increment, it's count value).

    Anyways I'll be experimenting with this one. Any ideas are welcome!
  • Disane wrote: »
    Any ideas are welcome!
    Your existing removeFromInv and addToInv macros contain all the sample code you need to implement either a transferItem macro or a transferItem method.

    The removeFromInv macro shows you how to remove an item from an Array (in that case a variable named $inventory), in your new case you would be passing the array in as the second parameter of transferItem.

    The above can also be said about the addToInv macro except it is showing you how to add an item to an Array, and in it's new case you would be passing the array in as the third parameter of transferItem.
  • @greyelf: Thanks! That means, we don't want to implement all these functions for each and every single container in the game, do we? Well, there's one issue and that is State.variables.<variable>. This needs to be turned into a parameter. I tried to implement the following:
    Macro.add([ "initContainer", "emptyContainer" ], 
    {
    	handler : function () 
    	{
    		var container = this.args[0]; 
    		container = [];		
    		container.findByID = function(obj) 
    		{
    			if (this.length === 0) 
    			{
    				return false;
    			}
    			if (typeof obj !== "object" || 
    					!obj.hasOwnProperty("id")) 
    			{
    				return false;
    			}
    			var idx = this.findIndex(function (item) 
    			{
    				return item.id === obj.id;
    			});
    			return (idx !== -1);
    		};
    	}
    });
    
    Macro.add("addToContainer", 
    {
    	handler : function () 
    	{
    		var container = this.args[0];
    		if (this.args.length === 0) 
    		{
    			return this.error("no inventory item specified");
    		}
    		// shouldn't this be container?
    		var obj = this.args[1];
    		//var obj = item;
    		if (typeof obj !== "object" 
    				|| !obj.hasOwnProperty("id")) 
    		{
    			return this.error("inventory item malformed");
    		}
    		var idx = container.findIndex(function (item) 
    		{
    			return item.id === obj.id;
    		});
    		if (idx === -1) 
    		{
    			container.push(obj);
    		}
    		else 
    		{
    			container[idx].count++;
    		}
    	}
    });
    
    Macro.add("removeFromContainer", 
    {
    	handler : function () 
    	{
    		var container = this.args[0];
    		if (this.args.length === 0) 
    		{
    			return this.error("no inventory item specified");
    		}
    		var obj = this.args[1];
    		//var obj = item;
    		if (typeof obj !== "object" || 
    				!obj.hasOwnProperty("id")) 
    		{
    			return this.error("inventory item malformed");
    		}
    		var idx = container.findIndex(function (item) 
    		{
    			return item.id === obj.id;
    		});
    		if (idx !== -1) 
    		{
    			if (container[idx].count > 1) 
    			{
    				container[idx].count--;
    			}
    			else 
    			{
    				container.splice(idx , 1);
    			}
    		}
    	}
    });
    
    Macro.add('listContainer', {
    	handler : function () 
    	{
    		var container = this.args[0];
    		var $list = $(document.createDocumentFragment());
    
    		if (container.length === 0) 
    		{
    			$list.text('nothing');
    		}
    		else 
    		{
    			container.forEach(function (item) 
    			{
    				if (!item.hasOwnProperty("image"))
    				{
    					$list
    						.wiki('[[' + item.name + ']]')
    						.append('\u00A0\u00D7' + item.count)
    						.append('<br>');
    				}
    				else
    				{
    					$list
    						.wiki('[[' + item.name + ']]')
    						.append('\u00A0\u00D7' + item.count)
    						.append('<br>')
    						.append("<img src=\"" + item.image + "\">")
    						.append('<br>');
    				}
    			}); // end of function() - end of foreach()
    		}
    		$list.appendTo(this.output);
    	}
    });
    
    window.countItemByName = function(container, item_name)
    {
    	var item_count = 0; 
    	container.forEach(
    	function(item)
    	{
    		if(item.hasOwnProperty("name") || 
    			 item.hasOwnProperty("count"))
    		{
    			if (item.name == item_name)
    			{
    				item_count = item.count;
    			}
    		}
    	});
    	return item_count;
    };
    
    window.countItemById = function (container, searchId)
    {
    	var item = container.find(function (item) 
    	{
    		return item.id === searchId;
    	});
    	return item ? item.count : 0;
    };
    

    So, what I did is simply, I tried to convert the container into a parameter that needs to be passed to the macro that handles the container and the item. The last two functions are working flawlessly as JS gurantees to make it work properly. Now, the other functions simply fail. For example when I call "addToContainer" it tells me that the passed container doesn't have a "findIndexByID" property. Which, tells me that some garbage is being passed instead of the $player.inventory.

    I sometimes wish I could debug and observe what is happening here. Anyone any ideas how to manage multiple containers?
Sign In or Register to comment.