diff --git a/Assets/Scenes/NewShop.unity b/Assets/Scenes/NewShop.unity
index 6683239..a8b3b9d 100644
--- a/Assets/Scenes/NewShop.unity
+++ b/Assets/Scenes/NewShop.unity
@@ -3601,6 +3601,7 @@ MonoBehaviour:
instructionText: {fileID: 360948192}
layoutGroup: {fileID: 2049417198}
ownModel: {fileID: 1237921498}
+ tradePartner: {fileID: 1237921497}
--- !u!114 &336428457
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -8397,6 +8398,7 @@ MonoBehaviour:
instructionText: {fileID: 1416129356}
layoutGroup: {fileID: 1865708447}
ownModel: {fileID: 132445007}
+ tradePartner: {fileID: 1237921497}
infoPanel: {fileID: 78292608}
--- !u!1 &831899927
GameObject:
@@ -14665,6 +14667,7 @@ MonoBehaviour:
instructionText: {fileID: 403716179}
layoutGroup: {fileID: 981903572}
ownModel: {fileID: 1237921498}
+ tradePartner: {fileID: 132445008}
infoPanel: {fileID: 1800161527}
--- !u!114 &1296916391
MonoBehaviour:
@@ -15287,6 +15290,7 @@ MonoBehaviour:
instructionText: {fileID: 1242189692}
layoutGroup: {fileID: 312492670}
ownModel: {fileID: 842666264}
+ tradePartner: {fileID: 1237921497}
infoPanel: {fileID: 2013250540}
--- !u!114 &1373665447
MonoBehaviour:
@@ -15701,6 +15705,7 @@ MonoBehaviour:
instructionText: {fileID: 1881459943}
layoutGroup: {fileID: 1764603517}
ownModel: {fileID: 842666264}
+ tradePartner: {fileID: 1237921497}
--- !u!114 &1396837743
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -22708,6 +22713,7 @@ GameObject:
- component: {fileID: 1947464187}
- component: {fileID: 1947464189}
- component: {fileID: 1947464188}
+ - component: {fileID: 1947464190}
m_Layer: 5
m_Name: Number
m_TagString: Untagged
@@ -22855,6 +22861,18 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1947464186}
m_CullTransparentMesh: 0
+--- !u!114 &1947464190
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1947464186}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1161ecc75d6ee4d44836c2efab2a0fd4, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
--- !u!1 &1964503989
GameObject:
m_ObjectHideFlags: 0
@@ -24649,6 +24667,7 @@ MonoBehaviour:
instructionText: {fileID: 1352417142}
layoutGroup: {fileID: 180905780}
ownModel: {fileID: 132445007}
+ tradePartner: {fileID: 1237921497}
--- !u!1 &2141857229
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/Shop/Components/InventoryMoneyDisplay.cs b/Assets/Scripts/Shop/Components/InventoryMoneyDisplay.cs
new file mode 100644
index 0000000..3a2db5f
--- /dev/null
+++ b/Assets/Scripts/Shop/Components/InventoryMoneyDisplay.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using TMPro;
+using UnityEngine;
+
+///
+/// This class' only purpose is to set the class of an item view prototype. Could be the item itself, or the infobox, or anything!
+///
+[RequireComponent(typeof(TextMeshProUGUI))]
+public class InventoryMoneyDisplay : MonoBehaviour,IShopModelObserver-
+{
+ private TextMeshProUGUI text;
+ private void Start()
+ {
+ text = GetComponent(); // Because of the meta tag, Unity will make sure this exists. No sanity checks
+ }
+
+ public void SetMoney(int balance)
+ {
+ if(text == null) text = GetComponent(); // Just in case this gets called before Start()
+ text.text = balance.ToString();
+ }
+
+ // This is why it's probably better to split this into two interfaces, but oh well
+ public void OnSelected(Item item)
+ {
+ //throw new NotImplementedException();
+ }
+
+ public void OnRemoved(Item item)
+ {
+ //throw new NotImplementedException();
+ }
+
+ public void OnAdded(Item item)
+ {
+ //throw new NotImplementedException();
+ }
+
+ public void OnTransaction(int balance)
+ {
+ SetMoney(balance);
+ }
+}
diff --git a/Assets/Scripts/Shop/Components/InventoryMoneyDisplay.cs.meta b/Assets/Scripts/Shop/Components/InventoryMoneyDisplay.cs.meta
new file mode 100644
index 0000000..592e482
--- /dev/null
+++ b/Assets/Scripts/Shop/Components/InventoryMoneyDisplay.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1161ecc75d6ee4d44836c2efab2a0fd4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Shop/Model/BuyModel.cs b/Assets/Scripts/Shop/Model/BuyModel.cs
index 69ffcf6..d46273a 100644
--- a/Assets/Scripts/Shop/Model/BuyModel.cs
+++ b/Assets/Scripts/Shop/Model/BuyModel.cs
@@ -1,11 +1,13 @@
using System;
using System.Configuration;
+using UnityEngine;
///
/// This is a concrete, empty model for the buy state of the shop for you to implement
///
public class BuyModel : ShopModel
{
+ private Inventory tradePartner;
public BuyModel(float pPriceModifier, int pItemCount, int pMoney) : base(pPriceModifier, pItemCount, pMoney)
{
@@ -27,9 +29,31 @@ public class BuyModel : ShopModel
public override void ConfirmSelectedItem()
{
- OnRemove(GetSelectedItem()); // If there's a view subscribed, this will probably remove the item from it
+ if (tradePartner == null)
+ {
+ Debug.Assert(false,"Could not make trade because the shop has no trade partner");
+ return;
+ }
+
+ // if (tradePartner.Money < GetSelectedItem().basePrice * priceModifier)
+ // {
+ // var up = new NotEnoughMoneyException();
+ // throw up; // If you find this, you can keep it!
+ // }
+ var item = GetSelectedItem();
+ tradePartner.ChangeBalance((int) (-item.basePrice * priceModifier));
+ OnRemove(item); // If there's a view subscribed, this will probably remove the item from it
inventory.RemoveItemByIndex(selectedItemIndex); // Before removing the item from the model's actual inventory
+ tradePartner.AddItem(item);
SelectItemByIndex(selectedItemIndex >= inventory.GetItemCount() ? --selectedItemIndex : selectedItemIndex);
}
+ public override void SetTradePartner(Inventory tradePartner)
+ {
+ this.tradePartner = tradePartner;
+ }
+}
+
+public class NotEnoughMoneyException : Exception
+{
}
diff --git a/Assets/Scripts/Shop/Model/Inventory.cs b/Assets/Scripts/Shop/Model/Inventory.cs
index df3235d..8de24ae 100644
--- a/Assets/Scripts/Shop/Model/Inventory.cs
+++ b/Assets/Scripts/Shop/Model/Inventory.cs
@@ -5,10 +5,11 @@ using System.Collections.Generic;
///
/// This class defines a basic inventory
///
-public class Inventory
+public class Inventory : IModelObservable
- // Borrowing the model observable here. Probably nothing will sub for items removed and such, but transactions are important! Lazy though, should split the observable
{
- public int Money { get; }//Getter for the money, the views need it to display the amount of money.
+ public int Money { get; private set; } //Getter for the money, the views need it to display the amount of money.
private List
- itemList = new List
- (); //Items in the inventory
+ private List> observers; // Mostly here for things that want to be up to date on transactions
//Set up the inventory with item count and money
public Inventory(int pItemCount, int pMoney) : this(pMoney)
@@ -19,6 +20,7 @@ public class Inventory
public Inventory(int pMoney)
{
Money = pMoney;
+ observers = new List>();
}
//------------------------------------------------------------------------------------------------------------------------
@@ -108,5 +110,37 @@ public class Inventory
itemList.Add(item);
}
}
+
+ public void ChangeBalance(int change)
+ {
+ if (Money + change < 0)
+ throw new NotEnoughMoneyException(); // We can't change the balance if it's too expensive
+ Money += change;
+ NotifyObservers();
+ }
//Think of other necessary functions for the inventory based on your design of the shop. Don't forget to unit test all the functions.
+ public IDisposable RegisterObserver(IShopModelObserver
- observer)
+ {
+ // Check whether observer is already registered. If not, add it
+ if (! observers.Contains(observer)) {
+ observers.Add(observer);
+ // Provide observer with existing data.
+ observer.OnTransaction(Money);
+ }
+ return new Unsubscriber
- (observers, observer);
+ }
+
+ public void RemoveObserver(IShopModelObserver
- observer)
+ {
+ if (observers.Contains(observer))
+ observers.Remove(observer);
+ }
+
+ private void NotifyObservers()
+ {
+ foreach (var observer in observers)
+ {
+ observer.OnTransaction(Money);
+ }
+ }
}
diff --git a/Assets/Scripts/Shop/Model/SellModel.cs b/Assets/Scripts/Shop/Model/SellModel.cs
index 99d2778..f5ca5de 100644
--- a/Assets/Scripts/Shop/Model/SellModel.cs
+++ b/Assets/Scripts/Shop/Model/SellModel.cs
@@ -1,11 +1,14 @@
using System;
using System.Configuration;
+using UnityEngine;
///
-/// This is a concrete, empty model for the sell state of the shop for you to implement
+/// This is a concrete, empty model for the sell state of the shop for you to implement. Technically this is the same as the buy model, as one inventory "buys" from the other.
+/// Except the shop doesn't pay because we don't want it to run out of money!
///
public class SellModel : ShopModel
{
+ private Inventory tradePartner;
public SellModel(float pPriceModifier, int pItemCount, int pMoney) : base(pPriceModifier, pItemCount, pMoney)
{
@@ -27,9 +30,16 @@ public class SellModel : ShopModel
public override void ConfirmSelectedItem()
{
- OnRemove(GetSelectedItem()); // If there's a view subscribed, this will probably remove the item from it
+ var item = GetSelectedItem();
+ inventory.ChangeBalance((int) (item.basePrice * priceModifier)); // We actually *add* the money to the player inventory...
+ OnRemove(item); // If there's a view subscribed, this will probably remove the item from it
inventory.RemoveItemByIndex(selectedItemIndex); // Before removing the item from the model's actual inventory
+ tradePartner?.AddItem(item); // Unlike the shop, we don't actually need a trade partner here. There might be shops that don't resell!
SelectItemByIndex(selectedItemIndex >= inventory.GetItemCount() ? --selectedItemIndex : selectedItemIndex);
}
+ public override void SetTradePartner(Inventory tradePartner)
+ {
+ this.tradePartner = tradePartner;
+ }
}
diff --git a/Assets/Scripts/Shop/Model/ShopModel.cs b/Assets/Scripts/Shop/Model/ShopModel.cs
index 4959b02..72607a5 100644
--- a/Assets/Scripts/Shop/Model/ShopModel.cs
+++ b/Assets/Scripts/Shop/Model/ShopModel.cs
@@ -93,6 +93,13 @@ public abstract class ShopModel : IModelObservable
-
//------------------------------------------------------------------------------------------------------------------------
//Concrete classes to implement
public abstract void ConfirmSelectedItem();
+
+ public void ChangeBalance(int change)
+ {
+ inventory.ChangeBalance(change);
+ }
+
+ public abstract void SetTradePartner(Inventory tradePartner);
// Observer pattern. Anything that wants to know what happens regarding item selection subscribes to this.
// This could mean all items of this model and its views do it, or all views do it.
diff --git a/Assets/Scripts/Shop/View/IShopModelObservable.cs b/Assets/Scripts/Shop/View/IShopModelObservable.cs
index fcbeefc..9e709bb 100644
--- a/Assets/Scripts/Shop/View/IShopModelObservable.cs
+++ b/Assets/Scripts/Shop/View/IShopModelObservable.cs
@@ -10,6 +10,7 @@ public interface IShopModelObserver
void OnSelected(T item);
void OnRemoved(T item);
void OnAdded(T item);
+ void OnTransaction(int balance); // Called when a transaction happens that changes the balance of the model, contains total new balance
}
// Unsubscriber, so the observer can self-unsubscribe from this observable without any coupling
diff --git a/Assets/Scripts/Shop/View/IShopModelObserver.cs b/Assets/Scripts/Shop/View/IShopModelObserver.cs
index d2cf93e..532f30a 100644
--- a/Assets/Scripts/Shop/View/IShopModelObserver.cs
+++ b/Assets/Scripts/Shop/View/IShopModelObserver.cs
@@ -4,12 +4,10 @@ using System.Collections.Generic;
///
/// This interface defines an observable. Since registering those is fairly generic, we also just keep a generic one around.
/// Works somewhat similar to IObservable, except that it's our own really.
-/// While we use generics here, theoretically hardcoding Item types would do the job
+/// While we use generics here, theoretically hardcoding Item types would do the job. Could make this 100% generic too...
///
public interface IModelObservable
{
IDisposable RegisterObserver(IShopModelObserver observer);
void RemoveObserver(IShopModelObserver observer);
- void OnRemove(T val);
- void OnSelect(T val);
}
diff --git a/Assets/Scripts/Shop/View/ShopView.cs b/Assets/Scripts/Shop/View/ShopView.cs
index 4a8eb91..1ea634f 100644
--- a/Assets/Scripts/Shop/View/ShopView.cs
+++ b/Assets/Scripts/Shop/View/ShopView.cs
@@ -28,6 +28,7 @@ public abstract class ShopView : MonoBehaviour
[SerializeField] protected LayoutGroup layoutGroup; // The layout group that represents this view visually
[SerializeField] private ModelComponent ownModel; // Reference to the model this view technically belongs to
+ [SerializeField] private InventoryComponent tradePartner; // Reference to the model this view technically belongs to
protected ShopModel model; // Model in MVC pattern
protected ShopModel other; // Other model in MVC pattern (our own inventory)
@@ -40,10 +41,12 @@ public abstract class ShopView : MonoBehaviour
{
//model = new BuyModel(2f, 16, 500); //Right now use magic values to set up the shop
Debug.Assert(ownModel != null,"No shop model assigned!",this);
+ Debug.Assert(tradePartner != null,"No trade partner assigned!",this);
model = ownModel.Model;
shopController = gameObject.AddComponent().Initialize(model);//Set the default controller to be the mouse controller
SetupItemIconView(); //Setup the grid view's properties
InitializeButtons(); //Connect the buttons to the controller
+
//model.Subscribe(this);
}
@@ -51,12 +54,30 @@ public abstract class ShopView : MonoBehaviour
{
// sanity check
if (model == null) model = ownModel.Model;
+ RegisterMoneyObserver(); // Make sure if this thing's got a money component somewhere we register it as an observer for the inventory we're looking at
+ model.SetTradePartner(tradePartner.Inventory);
PopulateItemIconView(); //Display items
}
private void OnDisable()
{
ClearIconView();
+ model.SetTradePartner(null);
+ UnregisterMoneyObserver();
+ }
+
+ // If view is generic, and shopview was just an implementation, this would be more flexible in theory. But for now it's enough to just manually assign "ourselves" as trade partner if we know the model works on just one inventory
+ private void RegisterMoneyObserver()
+ {
+ var moneyComp = GetComponentInChildren();
+ if (moneyComp != null) tradePartner.Inventory.RegisterObserver(moneyComp);
+ }
+
+ // Undo the above
+ private void UnregisterMoneyObserver()
+ {
+ var moneyComp = GetComponentInChildren();
+ if (moneyComp != null) tradePartner.Inventory.RemoveObserver(moneyComp);
}
//------------------------------------------------------------------------------------------------------------------------
diff --git a/Assets/Scripts/Shop/View/ShopViewList.cs b/Assets/Scripts/Shop/View/ShopViewList.cs
index 606a98f..b0df3e7 100644
--- a/Assets/Scripts/Shop/View/ShopViewList.cs
+++ b/Assets/Scripts/Shop/View/ShopViewList.cs
@@ -117,4 +117,9 @@ public class ShopViewList : ShopView, IShopModelObserver
-
{
throw new NotImplementedException();
}
+
+ public void OnTransaction(int moneyDelta)
+ {
+
+ }
}
diff --git a/Assets/Scripts/Shop/View/ViewItemContainer.cs b/Assets/Scripts/Shop/View/ViewItemContainer.cs
index 5a48e38..1c762e7 100644
--- a/Assets/Scripts/Shop/View/ViewItemContainer.cs
+++ b/Assets/Scripts/Shop/View/ViewItemContainer.cs
@@ -79,7 +79,12 @@ public abstract class ViewItemContainer : MonoBehaviour, IItemContainer, IShopMo
{
throw new NotImplementedException();
}
-
+
+ public void OnTransaction(int cost)
+ {
+ //throw new NotImplementedException(); Irrelevant for the view containers
+ }
+
// The reason we do this in OnDestroy() is so we don't have a memory leak when this gets removed externally somehow
private void OnDestroy()
{