using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using UnityEngine; using UnityEngine.UI; using TMPro; /// /// This class connects a grid view for buy state of the shop to a controller to manipulate the BuyModel via a ShopController /// interface, it contains specific methods to setup and update a grid view, with the data from a BuyModel. If you want to display /// informationoutside of the BuyModel, for example, the money amount from the player's inventory, then you need to either keep a /// reference to all the related models, or make this class an observer/event subscriber of the related models. /// public abstract class ShopView : MonoBehaviour { public ShopModel ShopModel => model; //A getter to access shopModel. Will access inventory of trade partner in sell mode! [SerializeField] protected GameObject itemPrefab; //A prefab to display an item in the view [SerializeField] protected Button buyButton; [SerializeField] protected TextMeshProUGUI instructionText; [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] protected 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) protected ShopController shopController; //Controller in MVC pattern private ItemType itemFilter = ItemType.All; // View can filter items, and this is the filter we want to use for that // Set up the view's necessary stuff before the view is enabled protected virtual void Awake() { //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); } private void Start() { 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 } private void OnEnable() { // sanity check if (model == null) model = ownModel.Model; model.SetTradePartner(tradePartner.Inventory); PopulateItemIconView(); //Display items } private void OnDisable() { ClearIconView(); //model.SetTradePartner(null); UnregisterMoneyObserver(); } // The view should decide which inventory is used to display money. Who knows what might be needed! protected abstract void RegisterMoneyObserver(); // Undo the above private void UnregisterMoneyObserver() { var moneyComp = GetComponentInChildren(); if (moneyComp != null) tradePartner.Inventory.RemoveObserver(moneyComp); } //------------------------------------------------------------------------------------------------------------------------ // SetupItemIconView() //------------------------------------------------------------------------------------------------------------------------ //Setup the grid view according to the ViewConfig object's requirements, right now it just sets the constraint mode and column count, //you can make cosmetic adjustments to the GridLayoutGroup by adding more configurations to ViewConfig and use them adjusting properties //like cellSize, spacing, padding, etc. protected abstract void SetupItemIconView(); //------------------------------------------------------------------------------------------------------------------------ // RepopulateItems() //------------------------------------------------------------------------------------------------------------------------ //Clears the grid view and repopulates it with new icons (updates the visible icons) private void RepopulateItemIconView() { ClearIconView(); PopulateItemIconView(); } //------------------------------------------------------------------------------------------------------------------------ // PopulateItems() //------------------------------------------------------------------------------------------------------------------------ //Adds one icon for each item in the shop private void PopulateItemIconView() { foreach (Item item in model.inventory.GetItems()) { if(itemFilter == ItemType.All || item.GetItemType() == itemFilter) AddItemToView(item); } } //------------------------------------------------------------------------------------------------------------------------ // ClearIconView() //------------------------------------------------------------------------------------------------------------------------ //Removes all existing icons in the gridview protected abstract void ClearIconView(); //------------------------------------------------------------------------------------------------------------------------ // AddItemToView() //------------------------------------------------------------------------------------------------------------------------ //Adds a new item container to the view, each view can have its way of displaying items protected abstract void AddItemToView(Item item); protected abstract void RemoveItemFromView(Item item); //------------------------------------------------------------------------------------------------------------------------ // InitializeButtons() //------------------------------------------------------------------------------------------------------------------------ //This method adds a listener to the 'Buy' button. They are forwarded to the controller. Since this is the confirm button of //the buy view, it will just call the controller interface's ConfirmSelectedItem function, the controller will handle the rest. private void InitializeButtons() { buyButton.onClick.AddListener( delegate { shopController.ConfirmSelectedItem(); } ); } // Set the item filter and automatically repopulate. Could use bit flags probably... public void SetItemFilter(ItemType filter) { this.itemFilter = filter; RepopulateItemIconView(); } private void Update() { //RepopulateItemIconView();//Repopulate the view each frame, this is very inefficient and won't work in many scenarios and SHOULD NOT be in //the final implementation, the view should be modified by the models via an observer or event queue pattern //Switch between mouse and keyboard controllers if (Input.GetKeyUp(KeyCode.K)) { if (shopController is MouseController) { SwitchToKeyboardControl(); } } else if (Input.GetMouseButtonUp(0)) { if (shopController is GridViewKeyboardController) { SwitchToMouseControl(); } } //Let the current controller handle input shopController.HandleInput(); } //------------------------------------------------------------------------------------------------------------------------ // SwitchToKeyboardControl() //------------------------------------------------------------------------------------------------------------------------ protected abstract void SwitchToKeyboardControl(); //------------------------------------------------------------------------------------------------------------------------ // SwitchToMouseControl() //------------------------------------------------------------------------------------------------------------------------ protected abstract void SwitchToMouseControl(); // These three are from the observable interface, and it was decided to leave this stuff to the implementation for now. Keeping here for reference // public virtual void OnSelected(Item item) // { // Debug.Log("View selects item: " + item.name,this); // RepopulateItemIconView(); // } // // public virtual void OnRemoved(Item item) // { // RemoveItemFromView(item); // } // // public virtual void OnAdded(Item item) // { // throw new NotImplementedException(); // } }