0 votes
by (270 points)
edited by

I was researching different ways of creating and inventory and I stumbled upon https://twinery.org/forum/discussion/comment/21051/ .  In the latter half user Disane , talks about creating something very similar to what I want.  I'm not a total noob to twine and scripting, but i am pretty beginner and am trying to re-engineer it from the pieces.  Using sugarcube2 and twine2.  Sorry if this looks familiar, I locked my old question and tried to be more specific.

I have gotten to the part were I am planning on altering the equip macro to implement checks to see if a clothing slot is occupied before equipping a new item, but i have a initial stumbling block.  Is it possible to have a clothing item have 2 or more "clothingType" properties, i.e. having overalls occupy both "shirts" and "pants", i can't figure out a good way to fit it in the structure.  

 

most of the original macro's from disane and the madexiles disscusion

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;
		}
	}
});

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;
};
		

Macro.add('listEquipped', {
	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.isEquipped == true)
				{
					$list
						.wiki('[[' + item.name + ']]')
						.append('\u00A0\u00D7' + item.count)
						.append('<br>');
				}
				
			}); // end of function() - end of foreach()
		}
		$list.appendTo(this.output);
	}
});

Macro.add('listWorn', {
	handler : function () 
	{
		var character = this.args[0];
		var $list = $(document.createDocumentFragment());
		var clothes
		if (character.clothing.length === 0) 
		{
			$list.text('nothing');
		}
		else 
		{
			clothes = Object.values(character.clothing)
		
		}
			clothes.forEach(function (item) 
			{
				if (item)
				{
					$list
						.wiki('[[' + item.name + ']]')
						.append('\u00A0\u00D7' + item.count)
						.append('<br>');
				}
			})
	
		$list.appendTo(this.output);
	}
});


StoryInit
<<set $player = {
	"money" : 0,
	"inventory" : [],
	"clothing" : 
	{
		"hats" : [],
		"pants" : [],
		"shirt" : [],
		"shoes" : []
	}
}>>


<<initContainer player.inventory>>	

<<set $BaseballHat = 
{
	"type" : "item",
	"isEquipped" : false,
	"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" : 0,
	"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" : false,
	"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 $overalls = 
{
	"type" : "item",
	"isEquipped" : false,
	"clothingType" : "pants", 
	"id" : "overall.clothing",
	"name" : "overalls",
	"count" : 1,
	"cost" : 75,
	"description" : "Nice overalls for you to wear.",
	"image" : "images/trousers.jpg"
}>>

<<addToContainer $player.inventory $BaseballHat>>
etc...



2nd little question, the inventory system I am emulating points each inventory item to its own passage.  Rather than creating a passage for each, how would i script it so that it just displays the "description" property and equip/unequip/back links.

 

1 Answer

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

Is it possible to have a clothing item have 2 or more "clothingType" properties, i.e. having overalls occupy both "shirts" and "pants", i can't figure out a good way to fit it in the structure.

Not directly.  You can, however, make value you're storing in the clothingType property an array, which should meet you needs.  For example:

<<set $Sneakers = {
	"type"         : "item",
	"isEquipped"   : false,
	"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 $overalls = {
	"type"         : "item",
	"isEquipped"   : false,
	"clothingType" : ["shirts", "pants"], 
	"id"           : "overall.clothing",
	"name"         : "overalls",
	"count"        : 1,
	"cost"         : 75,
	"description"  : "Nice overalls for you to wear.",
	"image"        : "images/trousers.jpg"
}>>

That would only require an minor change around your switch statement in the <<equipClothingJS>> macro.  That said, the entire switch statement is redundant, as you may simply use the clothing type value in a property access (via bracket notation).  For example, replace the switch with something like the following:

		clothing.clothingType.forEach(function (type) {
			character.clothing[type].isEquipped = false;
			character.clothing[type] = item;
			item.isEquipped = true;
		});

Possible issues:

  • It seems like the character.clothing[type].isEquipped property should be getting set to true instead of false here.  SNAFU?
  • You're not checking to see if the character was already wearing something in the slots in question when you probably should be.  That raises the obvious follow-on question: what should you do when a collision happens?

 

2nd little question, the inventory system I am emulating points each inventory item to its own passage.  Rather than creating a passage for each, how would i script it so that it just displays the "description" property and equip/unequip/back links.

What do your equip/unequip links look like?

In general, however, you're going to want to modify the code in the various <<list…>> macros that looks like the following:

$list
	.wiki('[[' + item.name + ']]')
	.append('\u00A0\u00D7' + item.count)
	.append('<br>');

A basic step in the direction you want might look like the following:

$list
	.wiki(item.name)
	.append('\u00A0\u00D7' + item.count)
	.wiki(' ' + item.isEquipped
		? '<<link "Unequip"> …code to unequip… <</link>>'
		: '<<link "Equip"> …code to equip… <</link>>'
	)
	.wiki('<br>' + item.description)
	.append('<br><br>');

 

by (270 points)
edited by

Hey thanks so much for all of the help, it was extremely insightful!  You are a scholar and a gentleman.  Real life drama kept from checking out how everything worked, and then it took a few days to apply and then fix mistakes.  Your example on the bracket notation helped me figure out several other bits of code.  The base code from Disane and you has a lot of extra power, in at least one instance it seemed easier to remove an option then to troubleshoot (equipping an item no longer stores a copy from $player.inventory to $player.clothing).  I wasn't very clear with the description of my secondary question and would still like some help with it.  I wanted the listinv macro to create the list as clickable links that lead to an autogenerated passage that had the description and unequip/equip options.  I cobbled together something that almost works (though parts of it seem kinda brute forced).  What I did is created a "itemExamine" passage that runs a "ItemDescrip" macro.  My problem is in creating functioning equip/unequip links inside the itemExamine macro.  I ran into the same problem  in the "listcontainer" marco when passing the item to be described into the "itemExamine" passage. I worked around it by giving every item object a property of "lname" which was the string equivalent of the objects name,  but I  feel like there has to be a better way now that I am running into the problem of how to refer to an item inside a macro.

I know the ItemDescrip macro is also wordier then needed, but I was having issues and doing things explicitly seemed to help me troubleshoot.

Also does the equipClothingJS line "if (conflicts.some(r=> Slots.includes(r))) "  work properly for comparing if two arrays share any values?  I got it from an example online, but when ever I try to follow the logic I end up turned around (seems out of order or something to me).  In my meager testing it seems to work.

Passage: itemExamine 
<<ItemDescrip $currentitem>>

<<return>>

Passage: StoryInit
<<set $player = {
	"money" : 0,
	"inventory" : [],
	"clothing" : 
	{
		"hats" : false,
		"pants" : false,
		"shirt" : false,
		"shoes" : false
	}
}>>
<<set $currentitem to []>>

<<set $BaseballHat = 
{
	"type" : "item",
	"isEquipped" : false,
	"clothingType" : ["hats"],
	"id" : "baseball.hat",
	"name" : "Baseball Hat",
	"lname": "BaseballHat",
	"count" : 1,
	"cost" : 50,
	"description" : "A simple baseball hat inherited from your grandfather.",
	"image" : "images/BaseballHat.jpg"
}>>
<<set $Beret = 
{
	"type" : "item",
	"isEquipped" : false,
	"clothingType" : ["hats"],
	"id" : "beret",
	"name" : "Green Beret",
	"lname": "Beret",
	"count" : 1,
	"cost" : 50,
	"description" : "A green beret with a military insignia.",
	"image" : "images/BaseballHat.jpg"
}>>


<<set $Overalls = {
	"type"         : "item",
	"isEquipped"   : false,
	"clothingType" : ["shirt", "pants"], 
	"id"           : "overall.clothing",
	"name"         : "Overalls",
	"lname": "Overalls",
	"count"        : 1,
	"cost"         : 75,
	"description"  : "Nice overalls for you to wear.",
	"image"        : "images/trousers.jpg"
}>>
....

Javascript:

Macro.add('ItemDescrip', {
	handler : function () 
	{
		var item = this.args[0];
		var $list = $(document.createDocumentFragment());
		var temp = $(document.createDocumentFragment());
		if (item.isEquipped)
			{ 
				temp.wiki(' '+ '<<link "Unequip">> <<equipClothingJS $player ' + item + '>> <</link>>');
			}
		else
			{
			temp.wiki(' ' +'<<link "Equip">> <<unequipClothingJS ' + item + '>> <</link>>');
			}
				
		
		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(temp)
				.append('<br>' + item.description)
			.append('<br><br>');
			
		}
		
		$list.appendTo(this.output);
		
	}

});
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 + '\u00A0\u00D7' + item.count + '|itemExamine][$currentitem = $' + item.lname + ']] <br>');
				}
				else
				{
					$list
					.wiki('[[ ' + item.name + '\u00A0\u00D7' + item.count + '|itemExamine][$currentitem = $' + item.lname + ']] <br>');
			
				}
			}); // end of function() - end of foreach()
		}
		$list.appendTo(this.output);
	}
});

      
      
    } else {
      
      if (!item)
      {
        this.error("could not find item in player's inventory");
      }
      
      clothing.clothingType.forEach(function (type) {
        character.clothing[type] = true;
        //  character.clothing[type] = item;
        item.isEquipped = true;
      });
    }
    
    $list.appendTo(this.output);  
  }
  
});
​

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);
		
		
		var conflicts = (Object.keys(character.clothing)).filter(function(key)
		{
			return character.clothing[key]
		});
		
		var Slots = clothing.clothingType;
		
		var $list = $(document.createDocumentFragment());
					
		
		if (conflicts.some(r=> Slots.includes(r))) 
		{
			$list.text('Wearing ' + clothing.name + ' conflicts with something else that is equipped');
			
			
		} else {
			
			if (!item)
			{
				this.error("could not find item in player's inventory");
			}
			
			clothing.clothingType.forEach(function (type) {
				character.clothing[type] = true;
				//	character.clothing[type] = item;
				item.isEquipped = true;
			});
		}
		
		$list.appendTo(this.output);	
	}
	
});

​
  Macro.add('unequipClothingJS', 
{
  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);
    
    var Types = clothing.clothingType;
    
    var Types = clothing.clothingType;
    var $list = $(document.createDocumentFragment());
    
    
    if (!item)
    {
      this.error("could not find item in player's inventory");
    }
    
    clothing.clothingType.forEach(function (type) {
      character.clothing[type] = false;
      //  character.clothing[type] = item;
      item.isEquipped = false;
    });
      
    
  }
  
});

 

 

...