+1 vote
by (160 points)

Hello everyone,

I'm quite new to twine, coding and forums in general so please excuse any obnoxious ignorance.

I'm currently trying to make an inventory system with Sugarcube 2.11. Using examples created by others (most of it from this discussion), I've managed to make a system that allows me to relatively easily pick-up, buy and sell items from my inventory. An important part of this is the ability to pick up multiples of an item and having them stack.

My problem arises around the amount of a certain item I have. I will use potatoes as an example. I can successfully buy as many potatoes as necessary using the <<addToConsumables>> macro. I can then also sell these potatoes using the <<removeSingleConsumables>> macro. However, if I sell all the potatoes and then try buying new ones, the amount of potatoes I have does not start from 0. Instead it seems to start from the last maximum amount I bought in one go. 

I imagine that I am incorrectly controlling the "amount" of an item I have in my inventory. Specifically when this amount goes to 0 and then back up.

My question is therefore how can I fix the following javascript to allow me to sell/remove items from my inventory while properly updating the amount I currently have. Thanks for any help on this matter.

<<set $potatoes ={
id: "potatoes",
amount: 1,
cost: 2,
heal: 2,
}>>

Inventory System Javascript:

window.getConsumables = function () {
	return State.variables.Consumables;
};

Macro.add([ "initConsumables", "emptyConsumables" ], {
	handler : function () {
		State.variables.Consumables = [];
		
		State.variables.Consumables.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("addToConsumables", {
	handler : function () {
		if (this.args.length === 0) {
			return this.error("no Consumables item specified");
		}
		var obj = this.args[0];
		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
			return this.error("Consumables item malformed");
		}
		var idx = State.variables.Consumables.findIndex(function (item) {
			return item.id === obj.id;
		});
		if (idx === -1) {
			State.variables.Consumables.push(obj);
		}
		else {
			State.variables.Consumables[idx].amount++;
		}
	}
});

Macro.add("removeSingleConsumables", {
	handler : function () {
		if (this.args.length === 0) {
			return this.error("no Consumables item specified");
		}
		var obj = this.args[0];
		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
			return this.error("Consumables item malformed");
		}
		var idx = State.variables.Consumables.findIndex(function (item) {
			return item.id === obj.id;
		});
		if (idx !== -1) {
			if(State.variables.Consumables[idx].amount > 1){
				State.variables.Consumables[idx].amount--;
			}
			else if (State.variables.Consumables[idx].amount == 1) {
				State.variables.Consumables.splice(idx , 1);
		}}
	}
});

Macro.add("removeFromConsumables", {
	handler : function () {
		if (this.args.length === 0) {
			return this.error("no Consumables item specified");
		}
		var obj = this.args[0];
		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
			return this.error("Consumables item malformed");
		}
		var idx = State.variables.Consumables.findIndex(function (item) {
			return item.id === obj.id;
		});
		if (idx !== -1) {
			State.variables.Consumables.splice(idx , 1);
		}
	}
});

Macro.add("Consumables", {
	handler : function () {
		if (State.variables.Consumables.length === 0) {
			new Wikifier(this.output, 'nothing');
		} else {
			new Wikifier(
				this.output,
				'[[' + State.variables.Consumables.map(function (item) {
					return item.id;
				}).join(']]<br>[[') + ']]');
		}
	}
});

Macro.add('ConsumablesWithLinks', {
	handler : function () {
		var $list = $(document.createDocumentFragment());

		if (State.variables.Consumables.length === 0) {
			$list.text('nothing');
		}
		else {
			State.variables.Consumables.forEach(function (item) {
				$list
					.wiki('[[' + item.id + ']]')
					.append('\u00A0\u00D7' + item.amount)
					.append('<br>');
			});
		}

		$list.appendTo(this.output);
	}
});

 

1 Answer

0 votes
by (62.7k points)
selected by
 
Best answer

So here's the code I used to look at this.

::StoryInit
<<set $potatoes ={
    id: "potatoes",
    amount: 1,
    cost: 2,
    heal: 2,
}>>

<<initConsumables>>

::some passage
<<addToConsumables $potatoes>>
<<run console.log(State.variables.Consumables[0].amount)>>
<<addToConsumables $potatoes>>
<<run console.log(State.variables.Consumables[0].amount)>>
<<removeSingleConsumables $potatoes>>
<<run console.log(State.variables.Consumables[0].amount)>>
<<removeSingleConsumables $potatoes>>
<<run console.log(State.variables.Consumables[0])>>
<<addToConsumables $potatoes>>
<<run console.log(State.variables.Consumables[0].amount)>>

In the console, I got:

1 2 1 undefined 1

Which is in line with what I'd expect.

If you sell all your potatoes, nothing goes to 0, the $potatoes item is instead completely removed from the $Consumables array, so there isn't a 0th item.  That said, it didn't start me in the middle of the count--the first <<addToConsumables $potatoes>> line both at the start of the code and after removing all the potatoes both start me off at 1.

I'm not sure what's happening in your code, and I think we might need to see more of it to figure this out.

by (160 points)

Thank you for the response Chapel.

I understand that the amount doesn't go to zero but that the item is removed entirely, this just keeps my inventory neat. Sorry for not explaining that properly. 

I'm unfamiliar with using the console so couldn't check it with your method. I've been checking the amount using the <<ConsumablesWithLinks>> macro, where the amount of an item is listed next to the item. i.e. [[potatoes]] x4.

The passages below show the code I used to do this (let me know if there is a better way to display multiple passages on the forum). I'm using the same javascript as before.

To test it I click "Buy" 3 times. I then go to [[Consumables]] and see that it says Potatoes x3. I then go back to [[Buy and Sell]] and click "Sell" 3 times. In [[Consumables]] it now says "You are not carrying anything." Everything works up to this point. Now if I click "Buy" one time the [[Consumables]] passages says Potatoes x3 again. Which is obviously not what I want.

I'm not sure what is causing this error then, if it perhaps has something to do with the <<ConsumablesWithLinks>> macro or with how I am implementing it in the passages themselves.

::StoryInit
<<set $potatoes ={
id: "potatoes",
amount: 1,
cost: 2,
heal: 2,
}>>
<<initConsumables>>


::Buy and Sell
<<click "[Buy]   ">><<addToConsumables $potatoes>><</click>> Potatoes
<<click "[Sell]   ">>><<removeSingleConsumables $potatoes>><</click>> Potatoes
[[Consumables]]


::Consumables
<<if $Consumables.length == 0>>
You are not carrying anything.
<<else>>
You are carrying:
<<ConsumablesWithLinks>> <<endif>>
[[Buy and Sell]]

 

by (62.7k points)

Okay, I see what's happening here. Change the <<addToConsumables>> macro to this:

Macro.add("addToConsumables", {
	handler : function () {
		if (this.args.length === 0) {
			return this.error("no Consumables item specified");
		}
		var obj = this.args[0];
		if (typeof obj !== "object" || !obj.hasOwnProperty("id")) {
			return this.error("Consumables item malformed");
		}
		var idx = State.variables.Consumables.findIndex(function (item) {
			return item.id === obj.id;
		});
		if (idx === -1) {
			State.variables.Consumables.push(clone(obj));
		}
		else {
			State.variables.Consumables[idx].amount++;
		}
	}
});

All I did was make it so the $Consumables array was taking a deep copy of the object passed to it.  Before it was just taking a reference.  This version should be a bit safer and help to avoid some weirder issues like this.

Also, note that the <<click>> macro is deprecated; you should use the <<link>> macro instead.

by (160 points)
Awesome, that fixes it! Thanks a lot for the help Chapel.
Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.
...