using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Runtime.InteropServices; using System.Text; using SteamNative; namespace Facepunch.Steamworks { public partial class Inventory { /// /// An item definition. This describes an item in your Steam inventory, but is /// not unique to that item. For example, this might be a tshirt, but you might be able to own /// multiple tshirts. /// public class Definition { internal Inventory inventory; public int Id { get; private set; } public string Name { get; set; } public string Description { get; set; } /// /// URL to an image specified by the schema, else empty /// public string IconUrl { get; set; } /// /// URL to an image specified by the schema, else empty /// public string IconLargeUrl { get; set; } /// /// Type can be whatever the schema defines. /// public string Type { get; set; } /// /// If this item can be created using other items this string will contain a comma seperated /// list of definition ids that can be used, ie "100,101;102x5;103x3,104x3" /// public string ExchangeSchema { get; set; } /// /// A list of recepies for creating this item. Can be null if none. /// public Recipe[] Recipes { get; set; } /// /// A list of recepies we're included in /// public Recipe[] IngredientFor { get; set; } public DateTime Created { get; set; } public DateTime Modified { get; set; } /// /// The raw contents of price_category from the schema /// public string PriceCategory { get; set; } /// /// The dollar price from PriceRaw /// public double PriceDollars { get; internal set; } /// /// The price in the local player's currency. The local player's currency /// is available in Invetory.Currency /// public double LocalPrice { get; internal set; } /// /// Local Price but probably how you want to display it (ie, $3.99, £1.99 etc ) /// public string LocalPriceFormatted { get; internal set; } /// /// Returns true if this item can be sold on the marketplace /// public bool Marketable { get; set; } public bool IsGenerator { get { return Type == "generator"; } } private Dictionary customProperties; internal Definition( Inventory i, int id ) { inventory = i; Id = id; SetupCommonProperties(); UpdatePrice(); } /// /// If you're manually occupying the Definition (because maybe you're on a server /// and want to hack around the fact that definitions aren't presented to you), /// you can use this to set propertis. /// public void SetProperty( string name, string value ) { if ( customProperties == null ) customProperties = new Dictionary(); if ( !customProperties.ContainsKey( name ) ) customProperties.Add( name, value ); else customProperties[name] = value; } /// /// Read a raw property from the definition schema /// public T GetProperty( string name ) { string val = GetStringProperty( name ); if ( string.IsNullOrEmpty( val ) ) return default( T ); try { return (T)Convert.ChangeType( val, typeof( T ) ); } catch ( System.Exception ) { return default( T ); } } /// /// Read a raw property from the definition schema /// public string GetStringProperty( string name ) { string val = string.Empty; if ( customProperties != null && customProperties.ContainsKey( name ) ) return customProperties[name]; if ( !inventory.inventory.GetItemDefinitionProperty( Id, name, out val ) ) return string.Empty; return val; } /// /// Read a raw property from the definition schema /// public bool GetBoolProperty( string name ) { string val = GetStringProperty( name ); if ( val.Length == 0 ) return false; if ( val[0] == '0' || val[0] == 'F'|| val[0] == 'f' ) return false; return true; } internal void SetupCommonProperties() { Name = GetStringProperty( "name" ); Description = GetStringProperty( "description" ); Created = GetProperty( "timestamp" ); Modified = GetProperty( "modified" ); ExchangeSchema = GetStringProperty( "exchange" ); IconUrl = GetStringProperty( "icon_url" ); IconLargeUrl = GetStringProperty( "icon_url_large" ); Type = GetStringProperty( "type" ); PriceCategory = GetStringProperty( "price_category" ); Marketable = GetBoolProperty( "marketable" ); if ( !string.IsNullOrEmpty( PriceCategory ) ) { PriceDollars = PriceCategoryToFloat( PriceCategory ); } } /// /// Trigger an item drop. Call this when it's a good time to award /// an item drop to a player. This won't automatically result in giving /// an item to a player. Just call it every minute or so, or on launch. /// ItemDefinition is usually a generator /// public void TriggerItemDrop() { inventory.TriggerItemDrop( Id ); } /// /// Trigger a promo item drop. You can call this at startup, it won't /// give users multiple promo drops. /// public void TriggerPromoDrop() { inventory.TriggerPromoDrop( Id ); } internal void Link( Definition[] definitions ) { LinkExchange( definitions ); } private void LinkExchange( Definition[] definitions ) { if ( string.IsNullOrEmpty( ExchangeSchema ) ) return; var parts = ExchangeSchema.Split( new[] { ';' }, StringSplitOptions.RemoveEmptyEntries ); Recipes = parts.Select( x => Recipe.FromString( x, definitions, this ) ).ToArray(); } internal void InRecipe( Recipe r ) { if ( IngredientFor == null ) IngredientFor = new Recipe[0]; var list = new List( IngredientFor ); list.Add( r ); IngredientFor = list.ToArray(); } internal void UpdatePrice() { if ( inventory.inventory.GetItemPrice( Id, out ulong price) ) { LocalPrice = price / 100.0; LocalPriceFormatted = Utility.FormatPrice( inventory.Currency, price ); } else { LocalPrice = 0; LocalPriceFormatted = null; } } } /// /// Trigger a promo item drop. You can call this at startup, it won't /// give users multiple promo drops. /// public void TriggerPromoDrop( int definitionId ) { SteamNative.SteamInventoryResult_t result = 0; inventory.AddPromoItem( ref result, definitionId ); inventory.DestroyResult( result ); } /// /// Trigger an item drop for this user. This is for timed drops. For promo /// drops use TriggerPromoDrop. /// public void TriggerItemDrop( int definitionId ) { SteamNative.SteamInventoryResult_t result = 0; inventory.TriggerItemDrop( ref result, definitionId ); inventory.DestroyResult( result ); } /// /// Grant all promotional items the user is eligible for. /// public void GrantAllPromoItems() { SteamNative.SteamInventoryResult_t result = 0; inventory.GrantPromoItems( ref result ); inventory.DestroyResult( result ); } /// /// Represents a crafting recepie which was defined using the exchange /// section in the item schema. /// public struct Recipe { public struct Ingredient { /// /// The definition ID of the ingredient. /// public int DefinitionId; /// /// If we don't know about this item definition this might be null. /// In which case, DefinitionId should still hold the correct id. /// public Definition Definition; /// /// The amount of this item needed. Generally this will be 1. /// public int Count; internal static Ingredient FromString( string part, Definition[] definitions ) { var i = new Ingredient(); i.Count = 1; try { if ( part.Contains( 'x' ) ) { var idx = part.IndexOf( 'x' ); int count = 0; if ( int.TryParse( part.Substring( idx + 1 ), out count ) ) i.Count = count; part = part.Substring( 0, idx ); } i.DefinitionId = int.Parse( part ); i.Definition = definitions.FirstOrDefault( x => x.Id == i.DefinitionId ); } catch ( System.Exception ) { return i; } return i; } } /// /// The item that this will create. /// public Definition Result; /// /// The items, with quantity required to create this item. /// public Ingredient[] Ingredients; internal static Recipe FromString( string part, Definition[] definitions, Definition Result ) { var r = new Recipe(); r.Result = Result; var parts = part.Split( new[] { ',' }, StringSplitOptions.RemoveEmptyEntries ); r.Ingredients = parts.Select( x => Ingredient.FromString( x, definitions ) ).Where( x => x.DefinitionId != 0 ).ToArray(); foreach ( var i in r.Ingredients ) { if ( i.Definition == null ) continue; i.Definition.InRecipe( r ); } return r; } } } }