using UnityEngine; using System.Collections; /// /// FSM: http://yuml.me/edit/331d4664 /// public class Skin { public enum SkinState { NULL, Initialising, AvailableForApplying, Applied, AvailableForPurchase, Purchasing, Downloading } enum SkinTransition { NULL, AlreadyPurchasedButNotApplied, AlreadyPurchasedAndApplied, NotPurchasedYet, Purchase, StartDownloading, PurchaseFailed, FinishedDownload, FailedDownload, Apply, UnApply } public IAPProduct product; public const string CURRENTLY_APPLIED_SKIN = "CurrentSkin"; const string PURCHASED_KEY_PREFIX = "Purchased"; const string NON_CONSUMABLE_PAYLOAD = "nonconsume"; FSMSystem _fsm; float _skinDownloadProgress; string _pendingTransaction; public float SkinDownloadProgress { get { return _skinDownloadProgress; } } public Skin(IAPProduct product) { NotificationCenter.AddListener(OnApplySkin, NotificationType.ApplySkin); this.product = product; CreateFSM(); //check if it has been purchased, currently applied etc if (HasPurchasedSkin()) { if (IsCurrentlyApplied()) { _fsm.PerformTransition(SkinTransition.AlreadyPurchasedAndApplied); } else { _fsm.PerformTransition(SkinTransition.AlreadyPurchasedButNotApplied); } } else { _fsm.PerformTransition(SkinTransition.NotPurchasedYet); } //#if UNITY_IPHONE //{ // //StoreKitManager.productPurchaseAwaitingConfirmationEvent += OnProductPurchaseAwaitingConfirmationEvent; //} //#endif } bool HasPurchasedSkin() { if (product.productId == "default") { return true; } else { return PlayerPrefs.GetInt(PURCHASED_KEY_PREFIX+product.productId, 0) == 1; } } bool IsCurrentlyApplied() { return PlayerPrefs.GetString(CURRENTLY_APPLIED_SKIN, "") == product.productId; } #region FSM void CreateFSM() { _fsm = new FSMSystem("Skin"); var InitialisingState = new FSMState(SkinState.Initialising); InitialisingState.AddTransition(SkinTransition.AlreadyPurchasedAndApplied, SkinState.Applied); InitialisingState.AddTransition(SkinTransition.AlreadyPurchasedButNotApplied, SkinState.AvailableForApplying); InitialisingState.AddTransition(SkinTransition.NotPurchasedYet, SkinState.AvailableForPurchase); _fsm.AddState(InitialisingState); var AvailableForApplyingState = new FSMState(SkinState.AvailableForApplying); AvailableForApplyingState.AddTransition(SkinTransition.Apply, SkinState.Applied); _fsm.AddState(AvailableForApplyingState); var AppliedState = new FSMState(SkinState.Applied); AppliedState.AddTransition(SkinTransition.UnApply, SkinState.AvailableForApplying); AppliedState.DoBeforeEntering += OnEnteringAppliedState; _fsm.AddState(AppliedState); var AvailableForPurchaseState = new FSMState(SkinState.AvailableForPurchase); AvailableForPurchaseState.AddTransition(SkinTransition.Purchase, SkinState.Purchasing); AvailableForPurchaseState.AddTransition(SkinTransition.StartDownloading, SkinState.Downloading); _fsm.AddState(AvailableForPurchaseState); var PurchasingState = new FSMState(SkinState.Purchasing); PurchasingState.AddTransition(SkinTransition.StartDownloading, SkinState.Downloading); PurchasingState.AddTransition(SkinTransition.PurchaseFailed, SkinState.AvailableForPurchase); PurchasingState.DoBeforeEntering += OnEnteringPurchasingState; PurchasingState.DoBeforeLeaving += OnLeavingPurchasingState; _fsm.AddState(PurchasingState); var DownloadingState = new FSMState(SkinState.Downloading); DownloadingState.AddTransition(SkinTransition.FinishedDownload, SkinState.AvailableForApplying); DownloadingState.AddTransition(SkinTransition.FailedDownload, SkinState.AvailableForPurchase); DownloadingState.DoBeforeEntering += OnEnteringDownloadingState; DownloadingState.DoBeforeLeaving += OnLeavingDownloadingState; _fsm.AddState(DownloadingState); } void OnEnteringPurchasingState() { #if UNITY_EDITOR || UNITY_WEBPLAYER { _fsm.PerformTransition(SkinTransition.StartDownloading); } //Did not use IAP Combo from Prime31 as it wasn't firing properly when the purchase was cancelled. #elif UNITY_IPHONE { //StoreKitManager.purchaseFailedEvent += OnPurchaseFailed; //StoreKitManager.purchaseCancelledEvent += OnPurchaseCancelled; //StoreKitBinding.purchaseProduct(product.productId, 1); } #elif UNITY_ANDROID { GoogleIABManager.purchaseSucceededEvent += OnGooglePurchaseSucceeded; GoogleIABManager.purchaseFailedEvent += OnPurchaseFailed; GoogleIAB.purchaseProduct( product.productId, NON_CONSUMABLE_PAYLOAD ); } #endif } void OnLeavingPurchasingState() { //#if UNITY_IPHONE //{ // StoreKitManager.purchaseFailedEvent -= OnPurchaseFailed; // StoreKitManager.purchaseCancelledEvent -= OnPurchaseCancelled; //} //#elif UNITY_ANDROID //{ // GoogleIABManager.purchaseSucceededEvent -= OnGooglePurchaseSucceeded; // GoogleIABManager.purchaseFailedEvent -= OnPurchaseFailed; //} //#endif } void OnEnteringDownloadingState() { NotificationCenter.AddListener(OnSkinDownloadProgress, NotificationType.SkinDownloadProgress); NotificationCenter.AddListener(OnSkinDownloadCompleted, NotificationType.SkinDownloadCompleted); NotificationCenter.AddListener(OnSkinDownloadFailed, NotificationType.SkinDownloadFailed); SkinDownloadManager.Instance.DownloadSkin(product.productId); } void OnLeavingDownloadingState() { NotificationCenter.RemoveListener(OnSkinDownloadProgress, NotificationType.SkinDownloadProgress); NotificationCenter.RemoveListener(OnSkinDownloadCompleted, NotificationType.SkinDownloadCompleted); NotificationCenter.RemoveListener(OnSkinDownloadFailed, NotificationType.SkinDownloadFailed); } void OnEnteringAppliedState() { if (PlayerPrefs.GetString(CURRENTLY_APPLIED_SKIN, "") == product.productId) { return; } PlayerPrefs.SetString(CURRENTLY_APPLIED_SKIN, product.productId); NotificationCenter.Post(NotificationType.ApplySkin, product.productId); SkinDownloadManager.Instance.ApplyCachedSkin(product.productId); } #endregion #region Download notifications void OnSkinDownloadProgress(Notification note) { object[] parameters = (object[])note.data; string skinId = (string)parameters[0]; if (skinId == product.productId) { _skinDownloadProgress = (float)parameters[1]; } } void OnSkinDownloadCompleted(Notification note) { string skinId = (string)note.data; if (skinId == product.productId) { AVDebug.Log("Download Complete "+product.productId); PlayerPrefs.SetInt(PURCHASED_KEY_PREFIX+product.productId, 1); //#if UNITY_IPHONE // StoreKitBinding.finishPendingTransaction(_pendingTransaction); //#endif _fsm.PerformTransition(SkinTransition.FinishedDownload); } } void OnSkinDownloadFailed(Notification note) { string skinId = (string)note.data; if (skinId == product.productId) { AVDebug.Log("Download Failed "+product.productId); _fsm.PerformTransition(SkinTransition.FailedDownload); } } #endregion #region Public methods public SkinState State { get { return _fsm.CurrentStateID; } } public void Buy() { _fsm.PerformTransition(SkinTransition.Purchase); } public void Apply() { _fsm.PerformTransition(SkinTransition.Apply); } public void UnApply() { _fsm.PerformTransition(SkinTransition.UnApply); } #if UNITY_ANDROID public void GoogleSaidItsAlreadyPurchased() { if (!HasPurchasedSkin()) { _fsm.PerformTransition(SkinTransition.StartDownloading); } } void OnGooglePurchaseSucceeded(GooglePurchase purchase) { if (purchase.productId == product.productId) { AVDebug.Log("Google purchase successful "+product.productId); _fsm.PerformTransition(SkinTransition.StartDownloading); } } #endif #endregion #region Store Kit Callbacks //#if UNITY_IPHONE // public void OnProductPurchaseAwaitingConfirmationEvent(StoreKitTransaction transaction) // { // if (transaction.productIdentifier == product.productId) // { // AVDebug.Log("ProductPurchaseAwaitingConfirmationEvent "+transaction.productIdentifier); // _pendingTransaction = transaction.transactionIdentifier; // _fsm.PerformTransition(SkinTransition.StartDownloading); // } // } //#endif void OnPurchaseFailed(string reason) { AVDebug.Log("Purchase Failed. Reason: "+reason); _fsm.PerformTransition(SkinTransition.PurchaseFailed); } void OnPurchaseCancelled(string reason) { AVDebug.Log("Purchase Cancelled. Reason "+reason); _fsm.PerformTransition(SkinTransition.PurchaseFailed); } #endregion void OnApplySkin(Notification note) { if (State == SkinState.Applied) { string skinId = (string)note.data; if (skinId != product.productId) { UnApply(); } } } }