Howdy, Stranger!

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

Counting amount of equal objects in inventory-array

Hello!
I'm trying to get an inventory-system, which also keeps count of identical items.
For example, if "Shiny Rock" has been added to the array three times, it should display [3x Shiny Rock] or something akin to this.
Currently, the listing will only show 1 of each object, even if a multitude of <<addToInv (object)>> is used
Example: <<addToInv "Ball">><<addToInv "Stone">><<addToInv "Ball">> will only list "Ball, Stone".
I am currently using this code for adding to, as well as listing the inventory:
macros.addToInv = {
	handler : function (place, macroName, params, parser) {
		if (params.length === 0) {
			return throwError(place, "<<" + macroName + ">>: no parameters given");
		}
		if (state.active.variables.inventory.indexOf(params[0]) === -1) {
			state.active.variables.inventory.push(params[0]);
		}
	}
};

macros.listInv = {
	handler : function (place, macroName, params, parser) {
		if (state.active.variables.inventory.length === 0) {
			new Wikifier(place, 'nothing');
		} else {
			var list = state.active.variables.inventory.map(function (item) {
				return item;
			});
			new Wikifier(place, '<div id="inventory-list">' + list.join('\n') + '</div>');
		}
	}
};
My JavaScript has become reaaally rusty since I last used it over 2 years ago, so I've been mixing-and-matching different custom macro's until I found one that I liked.
For reference, I am using a Popup macro to display the <<inventory>> passage.
macros.add("popup", {
	version: { major: 1, minor: 0, revision: 0 },
	handler: function ()
	{
		if (this.args.length < 2)
		{
			var errors = [];
			if (this.args.length < 1) { errors.push("link text"); }
			if (this.args.length < 2) { errors.push("passage name"); }
			return this.error("no " + errors.join(" or ") + " specified");
		}

		var el = document.createElement("a");
		el.innerHTML = this.args[0];
		el.className = "link-internal link-popup";
		el.setAttribute("data-passage", this.args[1]);
		UISystem.addClickHandler(el, null, function(evt) {
			var dialog = document.getElementById("ui-body");
			$(dialog)
				.empty()
				.addClass("dialog popup");
			new Wikifier(dialog, tale.get(evt.target.getAttribute("data-passage")).processText().trim());
			return true;
		});
		this.output.appendChild(el);
	}
});

Anyone know how I could get the nx Object listing?

Comments

  • I suggest changing your $inventory variable from an Array to a Map, which would allow you to store a count against each item in your inventory. You would initialize the variable in your StoryInit passage like so:
    <<set $inventory to new Map()>>
    

    The following are modified versions of your example macros:
    macros.addToInv = {
    	handler: function(place, macroName, params, parser) {
    		if (params.length === 0) {
    			return throwError(place, "<<" + macroName + ">>: no parameters given");
    		}
    
    		var inventory = state.active.variables.inventory;
    		if (! inventory) {
    			return throwError(place, "<<" + macroName + ">>: inventory variable has not been initialized.");
    		}
    
    		if (inventory.has(params[0])) {
    			inventory.set(params[0], inventory.get(params[0]) + 1);
    		} else {
    			inventory.set(params[0], 1);
    		}
    	}
    };
    
    
    macros.listInv = {
    	handler : function (place, macroName, params, parser) {
    		var inventory = state.active.variables.inventory;
    		if (! inventory) {
    			return throwError(place, "<<" + macroName + ">>: inventory variable has not been initialized.");
    		}
    
    		if (inventory.length === 0) {
    			new Wikifier(place, 'nothing');
    		} else {
    			var list = [];
    			inventory.forEach(function(value, key, map) {
    				this.push(value + 'x ' + key);
    			}, list);
    			new Wikifier(place, '<div id="inventory-list">' + list.join('\n') + '</div>');
    		}
    	}
    };
    
  • edited March 2016
    Sadly, the Inventory seems to error out on listInv#inventory.forEach().
    Using <<addToInv "item">> errors with Error: cannot execute macro <<listInv>>: Map.prototype.forEach called on incompatible object.

    I can bypass this by using:
    Object.keys(inventory).forEach... OR
    var propNames = Object.getOwnPropertyNames(inventory);
    And running propNames.forEach.... however, nothing can be seen in the actual popup window. ($inventory thus seems to be null, but the "Nothing" wikifier isn't used)
    Debugging with a simple <<print $inventory>> in the popup window, only lists "[object]", even with different items added to the map.
  • edited March 2016
    It's only in SugarCube v2.x+ that the existence and safety of Map and Set objects is ensured. They are not safe to use in SugarCube v1.x. Additionally, you're erroneously checking the count of the Map's entries with length, instead of the correct size.

    So, for SugarCube v1.x, I'd suggest something like the following (which uses generic objects):
    /*
    	<<addToInv "item name" [number]>>
    
    	e.g.
    		<<addToInv "item name">>   → Adds 1 of the item to $inventory.
    		<<addToInv "item name" 5>> → Adds 5 of the item to $inventory.
    */
    macros.addToInv = {
    	handler : function (place, macroName, params, parser) {
    		if (params.length === 0) {
    			return throwError(place, '<<' + macroName + '>>: no item name specified');
    		}
    
    		var inventory = state.active.variables.inventory;
    
    		if (typeof inventory !== 'object') {
    			return throwError(place, '<<' + macroName + '>>: $inventory variable has not been properly initialized');
    		}
    
    		var
    			itemName  = params[0],
    			itemCount = params.length > 1 ? params[1] : 1;
    
    		if (inventory.hasOwnProperty(itemName)) {
    			itemCount += inventory[itemName];
    		}
    
    		inventory[itemName] = itemCount;
    	}
    };
    
    /*
    	<<listInv>>
    */
    macros.listInv = {
    	handler : function (place, macroName, params, parser) {
    		var inventory = state.active.variables.inventory;
    
    		if (typeof inventory !== 'object') {
    			return throwError(place, '<<' + macroName + '>>: $inventory variable has not been properly initialized');
    		}
    
    		var itemNames = Object.keys(inventory);
    
    		if (itemNames.length === 0) {
    			new Wikifier(place, 'nothing');
    		} else {
    			var itemListings = itemNames.map(function(itemName) {
    				return inventory[itemName] + '\u00D7 ' + itemName;
    			});
    			new Wikifier(place, '<div id="inventory-list">' + itemListings.join('\n') + '</div>');
    		}
    	}
    };
    
  • @TheMadExile: thank you for spotting my length instead of size mistake.

    I used Map because the question was tagged sugarcube 2
  • Thanks for the help guys!
    I could have sworn that I had SugarCube 2, which is why the thread was tagged with v. 2 >_>
    Now I know better... welp, time to fix that
  • Each story format's version is listed within the Formats dialog (sidebar > Formats menu item).
Sign In or Register to comment.