Source: Apparel/GenericApparel.js

/*****************************************************************************
 * Copyright (c) 2019 Echo Hollow / L. Adamson. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions are met:
 * 
 *     1. Redistributions of source code must retain the above copyright 
 *        notice, this list of conditions and the following disclaimer.
 *     2. Redistributions in binary form must reproduce the above copyright 
 *        notice, this list of conditions and the following disclaimer in the 
 *        documentation and/or other materials provided with the distribution.
 *     3. All advertising materials mentioning features or use of this software 
 *        must display the following acknowledgement:
 *          "This product includes software developed by Echo Hollow."
 *     4. Neither the name of the copyright holder nor the names of its 
 *        contributors may be used to endorse or promote products derived from 
 *        this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
 * EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/

const ClothingSlot = LibEcho.Apparel.ClothingSlot;
const PersistentObject = LibEcho.Persistence.PersistentObject;

/**
 * <i>Unless you are actually extending the library, you probably don't need to use any of these generic {@link LibEcho.Apparel|Apparel} classes. If you are just using the library in your game, you're probably better off using the built-in {@link LibEcho.Apparel|Apparel} classes for your {@link LibEcho.Apparel|Apparel} types instead.</i>
 * 
 * This is the generic {@link LibEcho.Apparel|Apparel} type from which all other {@link LibEcho.Apparel|Apparel} is derived. {@link LibEcho.Persistence.PersistentObject|PersistentObjects} that specify this type in their defaults must also explicitly provide a slots section.
 * 
 * @extends LibEcho.Persistence.PersistentObject
 * @memberof LibEcho.Apparel
 */
class GenericApparel extends PersistentObject
{
    /**
     * <i>You should never instantiate a {@link LibEcho.Persistence.PersistentObject|PersistentObject} directly!  Instead, you must define it with {@link LibEcho.PersistentObject.define|PersistentObject.define()}, and then instantiate it with the {@link LibEcho.PersistentObject.instantiate|obj()} function when you want to use it.  <i><b>You must also never store {@link LibEcho.Persistence.PersistentObject|PersistentObjects} in Sugarcube variables!</b></i>  Just store their ids, instantiate them with {@link LibEcho.Persistence.obj|obj()} as needed, and then let them be garbage-collected.</i>
     * 
     * @param {string} id - The id of the {@link LibEcho.Persistence|Persistence} defaults entry to be associated with this instance.
     */
	constructor( id )
	{
		super( id );
	}
	
	/**
	 * The .genericName property of apparel is overridden to return always return something, otherwise wardrobe UIs will break.
	 */
	get genericName()
	{
		if( !this.internalData.get('genericName') )
			return "(Error: genericName is undefined for PersistentObject '" + this[id] + "')";
		return this.internalData.get( 'genericName' );
	}
	set genericName( value )
	{
		this.internalData.set( 'genericName', value );
	}
	
	/**
	 * The .name property of apparel is overridden to return the persistent genericName if the persistent name is falsy.
	 */
	get name()
	{
		if( this.internalData.get('name') )
			return this.internalData.get( 'name' );
		return this.internalData.get( 'genericName' );
	}
	set name( value )
	{
		this.internalData.set( 'name', value );
	}
	
	/**
	 * The {@link LibEcho.Apparel.ClothingSlot|ClothingSlots} that this article of {@link LibEcho.Apparel|Apparel} occupies. 
	 * 
	 * @type LibEcho.Apparel.ClothingSlot[]
	 */
	get slots()
	{
		if( !this.internalData.has("slots") )
			return this.defaultSlots;
		var slots = this.internalData.get( "slots" );
		for( var i=0; i<slots.length; i++ )
		{
			if( typeof slots[i] != "number" )
				slots[i] = ClothingSlot[slots[i].toUpperCase()];
		}
		return slots;
	}
	set slots( value )
	{
		// FIXME sanity check value
		this.internalData.set( "slots", value );
	}
	
	/**
	 * The default {@link LibEcho.Apparel.ClothingSlot|ClothingSlots} that this article of {@link LibEcho.Apparel|Apparel} occupies. This is returned if there is no entry for the particular article of {@link LibEcho.Apparel|Apparel's} slots in the {@link LibEcho.Persistence|Persistence} system. For {@link LibEcho.Apparel.GenericApparel|GenericApparel}, this returns [].
	 * 
	 * This getter is meant to be overridden in subclasses of {@link LibEcho.Apparel|Apparel}, to make them occupy certain slots by default.
	 * 
	 * @readonly
	 * @type LibEcho.Apparel.ClothingSlot[]
	 */
	get defaultSlots()
	{
		return [];
	}
	
	/**
	 * The inventory category of this object.  Overridden from PersistentObject to return "Apparel" if the true value is falsy.
	 * 
	 * @example
	 * FIXME Add an example.
	 * 
	 * @member {string} inventoryCategory
	 * @memberof LibEcho.Persistence.GenericApparel
	 * @instance
	 */
	get inventoryCategory()
	{
		var cat = this.internalData.get("inventoryCategory");
		if( !cat )
			return "Apparel";
    	return cat;
	}
	set inventoryCategory( value )
	{
		this.internalData.set( "inventoryCategory", value );
	}
    
	/**
	 * The wardrobe category of this object.  Used for wardrobe/worn apparel sorting.  Case-sensitive.  If falsy, then category is calculated based on the item's ClothingSlot values.
	 * 
	 * @example
	 * FIXME Add an example.
	 * 
	 * @member {string} wardrobeCategory
	 * @memberof LibEcho.Persistence.GenericApparel
	 * @instance
	 */
	get wardrobeCategory()
	{
		var cat = this.internalData.get("wardrobeCategory");
		if( !cat )
		{
			if( this.slots.includes(ClothingSlot.UNDERPANTS) || this.slots.includes(ClothingSlot.UNDERSHIRT) || this.slots.includes(ClothingSlot.UNDERLEGS) )
			{
				cat = "Underwear";
			}
			else if( this.slots.includes(ClothingSlot.TORSO) && this.slots.includes(ClothingSlot.LEGS) )
			{
				if( this.constructor.name == 'Dress' ) // HAX!
					cat = "Dresses";
				else
					cat = "Other";
			}
			else if( this.slots.includes(ClothingSlot.TORSO) )
			{
				cat = "Tops";
			}
			else if( this.slots.includes(ClothingSlot.LEGS) )
			{
				cat = "Bottoms";
			}
			else if( this.slots.includes(ClothingSlot.FEET) )
			{
				cat = "Shoes";
			}
			else if( this.slots.includes(ClothingSlot.EARS) || this.slots.includes(ClothingSlot.NECK) || this.slots.includes(ClothingSlot.WRIST) )
			{
				cat = "Jewelry";
			}
			else
			{
				cat = "Other";
			}
		}
    	return cat;
	}
	set wardrobeCategory( value )
	{
		this.internalData.set( "wardrobeCategory", value );
	}
    
	get long()
	{
		if( this.internalData.has('long') )
			return this.internalData.get('long');
		return this.defaultLong;
	}
	set long( value )
	{
		this.internalData.set( 'long', value );
	}
	get defaultLong()
	{
		return false;
	}
   
	get tall()
	{
		if( this.internalData.has('tall') )
			return this.internalData.get('tall');
		return this.defaultTall;
	}
	set tall( value )
	{
		this.internalData.set( 'tall', value );
	}
	get defaultTall()
	{
		return false;
	}
    
	get transparent()
	{
		if( this.internalData.has('transparent') )
			return this.internalData.get('transparent');
		return this.defaultTransparent;
	}
	set transparent( value )
	{
		this.internalData.set( 'transparent', value );
	}
	get defaultTransparent()
	{
		return false;
	}
    
	get thin()
	{
		if( this.internalData.has('thin') )
			return this.internalData.get('thin');
		return this.defaultThin;
	}
	set thin( value )
	{
		this.internalData.set( 'thin', value );
	}
	get defaultThin()
	{
		return false;
	}
	
	/** Item temporarily transparent (an open coat, a pulled up shirt, pulled down pants) */
	// exposing
	
	// FIXME Override masculinity/femininity requirements and bonuses (or lack thereof, rather) here.
}

module.exports = GenericApparel;