using System.Collections.Generic; using UnityEngine; using System.Threading.Tasks; using TMPro; using UnityEngine.UI; using System.Linq; /// <summary> /// Primary handler for Shop functionality for the shop Encounters /// </summary> public class TownUI : MonoBehaviour { #region Variables public static TownUI Instance { get; private set; } [Header("UI Properties")] [SerializeField] private Canvas shopCanvas; [SerializeField] private List<GameObject> memberButtons; // References to all of the party member switch buttons on the left of the screen [SerializeField] private GameObject normalButton; // The small version of the button for enemy encounters [SerializeField] private GameObject itemBoxRef; // Prefab reference to an Item that is for sale [SerializeField] private GameObject gridObject; // Prefab reference to the grid that the item goes into [SerializeField] private TextMeshProUGUI shop_Text; // Reference to the field that displays the shop's name [SerializeField] private TextMeshProUGUI gold_Text; // Reference to the field that displays the character's gold [SerializeField] private TextMeshProUGUI memberInvenText; // Reference to the field that displays what member's inventory is being accessed [SerializeField] private TextMeshProUGUI shopOwnerText; // Reference to the field that displays text the shop owner is saying [SerializeField] private Button rightPageButton; // Button reference that will be deactivated when needed, logic analyzation [SerializeField] private Button leftPageButton; // Button reference that will be deactivated when needed, logic analyzation [SerializeField] private Button exitButton; // Button for exiting the shop and recalling the original shop UI [SerializeField] private List<Image> memberGoldActiveImages; // References to all of the activated member icons on the left of the member buttons [SerializeField] private Transform shopInven; // Reference to the container of the shop's inventory [SerializeField] private Transform playerInven; // Reference to the container of the player_S's inventory [SerializeField] private Transform buttonField; // Field for the general button selection [SerializeField] private TextMeshProUGUI soldOutText; // Reference to the sold out text that appears when no more stock is available for the shop [Header("Sleeper Components")] [SerializeField] private GameObject shopObject; // Container for the shop game object that holds the merchant's inventory [SerializeField] private GameObject invenObject; // Container for the player inventory game object that holds its own respective inventory [SerializeField] private GameObject buttonFieldObject; // Container for the button field game object that blocks the inventory object [Header("Confirmation Components")] [SerializeField] private GameObject confirmObject; // Confirmation game object when buying or selling multiple of the same thing [SerializeField] private TextMeshProUGUI currentAmountText; // Text field to indicate item Cur / item Max when confirming [SerializeField] private TextMeshProUGUI costField; // Text field to indicate the cost of the items multiplied by their values or cost [SerializeField] private Slider amountSlider; // The Slider component the player will manipulate [SerializeField] private GameObject restConfirmObject; // Confirmation game object for resting [SerializeField] private TextMeshProUGUI restCost; // Reference to the cost text field in the rest confirm object private Shop shop; // Shop from the Encounter container private Dictionary<int, Dictionary<Item, int>> pageDic; // Dictionary that retains what items are on what page private List<Item> discountedItems; // Reference discounted items private Dictionary<Item, int> playerItems; // Reference list for selling functionality private int currentPage = 0; // Represents what page is being shown to the player in the shop private List<GameObject> buttons; // Represents the buttons that indicate things to do in the town private Inventory currentInven; private GameObject playerObject; private Player player; private GameObject[] allyObjects; private Ally[] allies; private List<GameObject> shopGrids; private List<GameObject> playerGrids; private Entity currentPersonInven; private Task playerConfirmation; private bool restIndication; private int itemCount; private int itemMax; private int totalCost; private int currentItemVC; private bool? confDeter; private bool rested; private bool isSelling; private int RestCost; private bool discountsApplied; private bool alerted; private const byte MaxItemsPerPage = 12; private const byte MaxPartyMembers = 4; private readonly Color DisabledColor = new Color(.65f, .65f, .65f, 1f); #endregion /// <summary> /// Switches on or off the shop button when the inventory is open or closed to prevent odd interactions /// </summary> /// <param name="shop">The current TownUI</param> public static void CheckInventoryStatus(TownUI shop) { if (Globals.InventoryOpened) // Mark shop buttons as inaccessible { shop.buttons.ForEach(button => { button.GetComponent<Button>().interactable = false; button.GetComponent<Image>().color = new Color(.65f, .65f, .65f, 1f); }); } else // Mark shop buttons as accessible { for (int i = 0; i < shop.buttons.Count; ++i) { if (i is 0 && shop.rested) continue; else { shop.buttons[i].GetComponent<Button>().interactable = true; shop.buttons[i].GetComponent<Image>().color = Color.white; } } } } /// <summary> /// Shop information from the Shop object is inserted for indication of what items can be /// sold as well as the merchant information /// </summary> /// <param name="shop"></param> public void InsertShopInfo(Shop shop) => this.shop = shop; private void Awake() => Instance = this; private async void OnEnable() { await LoadShop(); // Sets the camera for the canvas as well as the overlay and layer IDs SetCanvasComponents(); playerObject = GameManager.Instance.GetPlayerObject(); player = playerObject.GetComponent<Player>(); allyObjects = GameManager.Instance.GetAllyObjects(); allies = new Ally[allyObjects.Length]; for (int i = 0; i < allyObjects.Length; i++) allies[i] = allyObjects[i].GetComponent<Ally>(); GeneralUI.Instance.UpdateGUIs(GameManager.Instance.GetAllyObjects()); currentPersonInven = player; // Cost of the rest RestCost = GenerateInnCost(); FillShopInformation(); GUIHandler(); AbilityBrain.ShopChecks(player, allies, this); SleepShop(); // Waits for the shop object to be fully created and within the UI system // before grabbing the information within the shop system async Task LoadShop() { while (shop == null) await Task.Yield(); await Task.CompletedTask; } void SetCanvasComponents() { Canvas canvasComp = GetComponentInChildren<Canvas>(); canvasComp.worldCamera = Camera.main; canvasComp.sortingLayerName = "UI"; } } /// <summary> /// Enables the shop layout by creating two buttons to enter the shop or to leaves the encounter /// </summary> public void EnableShopLayout() { // Clears the transform to be sure no leftover children are left ResetUI(); buttons = new List<GameObject>(); const byte ButtonAmount = 3; for (int i = 1; i <= ButtonAmount; i++) { // Two buttons are created for entering the Shop and leaving the Encounter GameObject buttonRef = Instantiate(normalButton, buttonField); TextMeshProUGUI retainer = buttonRef.GetComponentInChildren<TextMeshProUGUI>(); buttons.Add(buttonRef); switch (i) { case 1: retainer.text = "Rest at Inn"; buttonRef.GetComponent<Button>().onClick.AddListener(() => { InitializeRestConfirmationObject(); }); if (rested) { buttonRef.GetComponent<Button>().interactable = false; buttonRef.GetComponent<Image>().color = DisabledColor; } break; case 2: retainer.text = "Shop"; buttonRef.GetComponent<Button>().onClick.AddListener(() => { ActivateShop(); }); break; case 3: retainer.text = "Leave"; buttonRef.GetComponent<Button>().onClick.AddListener(() => { GameManager.Instance.EndEncounter(player, allies); }); break; } } } private void BountyHunterFunction() { if (GameManager.Instance.GetEncounterValue() is not 1) { if (player.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[70])) { Ability abRef = player.GetInventory().Abilities.Find(ability => ability.AbilityID is 70); if (abRef.ReferenceObject is null) { GameObject bountyBoard = Resources.Load<GameObject>("Prefabs/Special Prefabs/Bounty_Board"); GameObject instancedBoard = Instantiate(bountyBoard, transform.GetChild(0)); instancedBoard.GetComponent<Bounty>().InjectBounties(); return; } TakenBounty bountyLogic = (TakenBounty)abRef.ReferenceObject; if (bountyLogic.HasBeenKilled) { if (abRef.ReferenceObject is not null) { int goldAmount = bountyLogic.GoldReward; player.GetInventory().AddGold(goldAmount); } GameObject bountyBoard = Resources.Load<GameObject>("Prefabs/Special Prefabs/Bounty_Board"); GameObject instancedBoard = Instantiate(bountyBoard, transform.GetChild(0)); instancedBoard.GetComponent<Bounty>().InjectBounties(); } } } } /// <summary> /// Fills in the shop's merchant information as well as the inventory information from the Shop object /// </summary> private void FillShopInformation() { string user = currentPersonInven.GetName(); shop_Text.text = shop.ShopName; gold_Text.text = "GOLD:" + player.GetInventory().GetGold().ToString("000000"); memberInvenText.text = $"{user}'s Inventory"; shopOwnerText.text = "Welcome to my Shop! Please look around!"; // Gathers all items that are available within the shop Dictionary<Item, int> listOfStock = StockShop(); // Seperates all of the available items onto different pages by groups of 12 pageDic = new Dictionary<int, Dictionary<Item, int>>(); int counter = -1; int itemCounter = 0; foreach (KeyValuePair<Item, int> item in listOfStock) { if (itemCounter % MaxItemsPerPage == 0) pageDic.Add(++counter, new Dictionary<Item, int>()); pageDic[counter].Add(item.Key, item.Value); itemCounter++; } if (listOfStock.Count is 0) soldOutText.gameObject.SetActive(true); else soldOutText.gameObject.SetActive(false); } /// <summary> /// Stocks up the shop with the items and such within the Shop component itself /// </summary> /// <returns>The Complete stock of the shop</returns> private Dictionary<Item, int> StockShop() { Dictionary<Item, int> listOfStock = new Dictionary <Item, int>(); foreach (KeyValuePair<Item, int> item in shop.ItemsOnSale) { if (listOfStock.ContainsKey(item.Key)) { listOfStock[item.Key] += item.Value; continue; } listOfStock.Add(item.Key, item.Value); } foreach (KeyValuePair<Consumable, int> con in shop.ConsumablesOnSale) { if (listOfStock.ContainsKey(con.Key)) { listOfStock[con.Key] += con.Value; continue; } listOfStock.Add(con.Key, con.Value); } foreach (KeyValuePair<Relic, int> relic in shop.RelicsOnSale) { if (listOfStock.ContainsKey(relic.Key)) { listOfStock[relic.Key] += relic.Value; continue; } listOfStock.Add(relic.Key, relic.Value); } foreach (KeyValuePair<Weapon, int> weapon in shop.WeaponOnSale) { if (listOfStock.ContainsKey(weapon.Key)) { listOfStock[weapon.Key] += weapon.Value; continue; } listOfStock.Add(weapon.Key, weapon.Value); } foreach (KeyValuePair<Apparel, int> apparel in shop.ApparelOnSale) { if (listOfStock.ContainsKey(apparel.Key)) { listOfStock[apparel.Key] += apparel.Value; continue; } listOfStock.Add(apparel.Key, apparel.Value); } return listOfStock; } /// <summary> /// Handles creation and listeners of the GUI Buttons to determine what allies inventory to access /// </summary> private void GUIHandler() { for (int i = 0; i < MaxPartyMembers; i++) { Button b = memberButtons[i].GetComponent<Button>(); b.interactable = true; if (i is 0) { memberButtons[i].GetComponentsInChildren<Image>()[1].sprite = Resources.Load<Sprite>("Sprites/PlayerGUI"); memberGoldActiveImages[i].enabled = true; int tempVar = i; b.onClick.AddListener(() => { SwitchInventories(tempVar, player.GetInventory(), player); }); continue; } // 0 is the Image within the primary object 1 is the Image within the child object try { Sprite allySprite = allies[i - 1].GetSprite(); memberGoldActiveImages[i].enabled = false; memberButtons[i].GetComponentsInChildren<Image>()[0].enabled = true; memberButtons[i].GetComponentsInChildren<Image>()[1].enabled = true; memberButtons[i].GetComponentsInChildren<Image>()[1].sprite = allySprite; int tempVar = i; b.onClick.AddListener(() => { SwitchInventories(tempVar, allies[tempVar - 1].GetInventory(), allies[tempVar - 1]); }); } catch { memberGoldActiveImages[i].enabled = false; memberButtons[i].GetComponent<Button>().interactable = false; memberButtons[i].GetComponentsInChildren<Image>()[0].enabled = false; memberButtons[i].GetComponentsInChildren<Image>()[1].enabled = false; } } } /// <summary> /// Fills up the player's inventory bar for selection to sell to the vendor, scrolls properly /// </summary> private void FillInventoryBar(Entity activeEntity, Dictionary<Item, int> items) { playerGrids = new List<GameObject>(); // Retrieves all the player's inventory items playerItems = new Dictionary<Item, int>(items); currentInven = activeEntity.GetInventory(); currentPersonInven = activeEntity; for (int i = 0; i < currentInven.GetInvenLimit(); i++) { GameObject grid = Instantiate(gridObject, playerInven); grid.GetComponent<DropBox>().DetectItemUISize(DropBox.DropLocations.ShopInventory); playerGrids.Add(grid); } int counter = 0; foreach (KeyValuePair<Item, int> item in playerItems) { // Skips unsellable items if (item.Key.CantBeSold) continue; GameObject itemCon = Instantiate(itemBoxRef, playerGrids[counter++].transform); itemCon.GetComponent<DraggableItem>().Type = DraggableItem.DraggableItemType.InventoryInShop; ExtractItemInfo(item.Key, item.Value, itemCon, true); AspectHelperManager ahm = itemCon.GetComponent<AspectHelperManager>(); ahm.InjectTransform(shopCanvas.transform); ahm.InjectAspect(item.Key); ahm.InjectOffset(new Vector2(4.25f, -1.55f)); } } /// <summary> /// Updates the shop information after the player buys from the vendor or sells to the vender /// </summary> private void UpdateShopInformation() { // Destroys all of the Shop Items that are currently on the page before resetting the item information foreach (Transform child in shopInven) Destroy(child.gameObject); FillShopInformation(); ResetPlayerBar(currentPersonInven, currentInven.GetAllItems()); EnablePage(); } /// <summary> /// The page indicated by the current page marker is activated and shows all items represented by the /// dictionary marker correlated to the current page /// </summary> private void EnablePage() { if (soldOutText.gameObject.activeSelf) return; shopGrids = new List<GameObject>(); foreach (KeyValuePair<Item, int> pair in pageDic[currentPage]) { GameObject grid = Instantiate(gridObject, shopInven); grid.GetComponent<DropBox>().DetectItemUISize(DropBox.DropLocations.ShopStock); shopGrids.Add(grid); GameObject itemCon = Instantiate(itemBoxRef, grid.transform); itemCon.GetComponent<DraggableItem>().Type = DraggableItem.DraggableItemType.ShopStock; ExtractItemInfo(pair.Key, pair.Value, itemCon); AspectHelperManager ahm = itemCon.GetComponent<AspectHelperManager>(); ahm.InjectTransform(shopCanvas.transform); ahm.InjectAspect(pair.Key); ahm.InjectOffset(new Vector2(-4.25f, -1.55f)); } } /// <summary> /// Changes the pages of the shop by either incrementing or decrementing the current page /// If the current page goes below the restrictive page values than resets to either the lowest /// page or highest page respectively /// </summary> /// <param name="value">Indicator for if the page is being incremented or decremented</param> public void ChangePages(int value) { // Destroys all of the Shop Items that are currently on the page before changing page foreach (Transform child in shopInven) Destroy(child.gameObject); // If the value from the button was 1 if (value > 0) { if (currentPage + 1 > pageDic.Count - 1) currentPage = 0; else currentPage += 1; } // If the value from the button was -1 else { if (currentPage - 1 < 0) currentPage = pageDic.Count - 1; else currentPage -= 1; } EnablePage(); } /// <summary> /// Translates the Item object to the ItemGUI UI object /// </summary> /// <param name="item">Item to translate</param> /// <param name="itemCon">The ItemGUI container</param> /// <param name="playerBar">If the object is in the player inventory bar transform</param> private void ExtractItemInfo(Item item, int amount, GameObject itemCon, bool playerBar = false) { ItemGUI comp = itemCon.GetComponent<ItemGUI>(); comp.InjectItemInformation(item, amount); if (playerBar) comp.IndicateNoNameLargeSpriteWithValue(); else comp.IndicateNoNameLargeSpriteWithCost(); } /// <summary> /// Sells the selected item to the vendor for the item's selling value /// </summary> /// <param name="itemName">Name of the item to be sold</param> public async void SellToVendor(string itemName) { Dictionary<Item, int> items = playerItems; // TODO --> Possibility of implementation for vendor to sell back items? confDeter = null; foreach (KeyValuePair<Item, int> item in items) { // Once the item is found, adds the item value to the player's gold than removes it // Then updates shop information for the player's gold amount if (itemName.Equals(item.Key.ItemName)) { if (item.Value > 1) { ConfirmationOfAmount(item.Key, item.Value, true); while (playerConfirmation is null) await Task.Yield(); if (confDeter is false) return; } int cost = confDeter is null ? item.Key.ItemValue * item.Value : totalCost; player.GetInventory().AddGold(cost); int amount = confDeter is null ? 1 : itemCount; SwitchPlayerToShop(item, amount); if (playerItems[item.Key] - amount == 0) playerItems.Remove(item.Key); UpdateShopInformation(); break; } } } /// <summary> /// Switches designated item and amount to shop inventory from the player inventory, /// also switches off the inventory active nature of items that are active and sold /// </summary> /// <param name="item">The item to add to the shop and remove from the player</param> /// <param name="amount">The amount of the object desired to sell to shop</param> private void SwitchPlayerToShop(KeyValuePair<Item, int> item, int amount) { if (item.Key is Weapon) { if (item.Key == currentInven.GetPrimaryActiveWeapon()) currentInven.SetPrimaryActiveWeapon(null); else if (item.Key == currentInven.GetSecondaryActiveWeapon()) currentInven.SetSecondaryActiveWeapon(null); currentInven.Remove(item.Key as Weapon, amount); shop.Add(item.Key as Weapon, amount); } else if (item.Key is Consumable) { currentInven.Remove(item.Key as Consumable, amount); shop.Add(item.Key as Consumable, amount); } else if (item.Key is Apparel app) { if (currentInven.GetActiveApparelLocations().ContainsValue(app)) currentInven.SetActiveApparelAtLocation(null, app.ApparelLocation); currentInven.Remove(item.Key as Apparel, amount); shop.Add(item.Key as Apparel, amount); } else if (item.Key is Relic) { if (item.Key == currentInven.GetActiveRelic()) currentInven.SetActiveRelic(null); currentInven.Remove(item.Key as Relic, amount); shop.Add(item.Key as Relic, amount); } else { currentInven.Remove(item.Key, amount); shop.Add(item.Key, amount); } } /// <summary> /// Sells the selected item to the player for the item's cost and takes the necessary gold /// If the gold is unavailable the item will not be given /// </summary> /// <param name="itemName">Name of the item to be sold</param> public async void SellToPlayer(string itemName) { if (currentInven.GetInvenCount() + 1 > currentInven.GetInvenLimit()) return; Dictionary<Item, int> items = shop.GetAll(); confDeter = null; foreach (KeyValuePair<Item, int> item in items) { // Checks for the correct item and also makes sure that the player's gold is able to purchase the item if (itemName.Equals(item.Key.ItemName)) { if (item.Key.ItemCost > player.GetInventory().GetGold()) return; if (item.Value > 1) { ConfirmationOfAmount(item.Key, item.Value, false); while (playerConfirmation is null) await Task.Yield(); if (confDeter is false) return; } int cost = confDeter is null ? item.Key.ItemCost : totalCost; player.GetInventory().RemoveGold(cost); int amount = confDeter is null ? 1 : itemCount; SwitchShopToPlayer(item, amount); if (!playerItems.ContainsKey(item.Key)) playerItems.Add(item.Key, amount); else playerItems[item.Key] += amount; UpdateShopInformation(); break; } } // False Idol Relic Flag if (Globals.RelicFlags[1]) { if (!alerted) { foreach (KeyValuePair<Item, int> item in items) item.Key.ImplementPriceSurge(.25f); alerted = true; } } } /// <summary> /// Adds the item to the player's inventory and removes it from the shop's inventyory /// </summary> /// <param name="item">The item to switch to the player's inventory and remove from the shop</param> /// <param name="amount">The amount of the item to remove from the shop and give to the player</param> private void SwitchShopToPlayer(KeyValuePair<Item, int> item, int amount) { if (item.Key is Weapon) { currentInven.Add(item.Key as Weapon, amount); shop.Remove(item.Key as Weapon, amount); } else if (item.Key is Consumable) { currentInven.Add(item.Key as Consumable, amount); shop.Remove(item.Key as Consumable, amount); } else if (item.Key is Apparel) { currentInven.Add(item.Key as Apparel, amount); shop.Remove(item.Key as Apparel, amount); } else if (item.Key is Relic) { currentInven.Add(item.Key as Relic, 1); shop.Remove(item.Key as Relic, amount); } else { currentInven.Add(item.Key, amount); shop.Remove(item.Key, amount); } } /// <summary> /// Calls the confirmation canvas object for the player to take an amount of the object they want to purchase or sell if more than 1 /// </summary> /// <param name="item">The item being purchased or sold</param> /// <param name="amount">The max amount of the item available</param> /// <param name="merchantState">True, If the item is being sold to the vendor or False, if to the player</param> private void ConfirmationOfAmount(Item item, int amount, bool merchantState) { playerConfirmation = null; confirmObject.SetActive(true); itemMax = amount; itemCount = 1; isSelling = merchantState; // Sets the cost/value variable to the items cost for buying from the merchant if (!merchantState) { confirmObject.transform.Find("Question_Text").GetComponent<TextMeshProUGUI>().text = "How many would you like to buy?"; currentItemVC = item.ItemCost; totalCost = currentItemVC * itemCount; costField.text = $"Cost: {totalCost}"; } // Sets the cost/value variable to the items value for selling to the merchant else { confirmObject.transform.Find("Question_Text").GetComponent<TextMeshProUGUI>().text = "How many would you like to sell?"; currentItemVC = item.ItemValue; totalCost = currentItemVC * itemCount; costField.text = $"Value: {totalCost}"; } // Default values amountSlider.maxValue = amount; amountSlider.value = itemCount; currentAmountText.text = $"{itemCount} / {itemMax}"; } /// <summary> /// Slider adjustment method that adjusts the GUI objects in the confirmation canvas object /// </summary> public void AdjustValues() { itemCount = (int)amountSlider.value; totalCost = itemCount * currentItemVC; if (isSelling) costField.text = $"Value: {totalCost}"; else costField.text = $"Cost: {totalCost}"; currentAmountText.text = $"{itemCount} / {itemMax}"; } /// <summary> /// Cancels the potential transaction, doesn't give the items or take the gold from the designated people /// </summary> public void Cancel() { confirmObject.SetActive(false); confDeter = false; playerConfirmation = Task.CompletedTask; } /// <summary> /// Confirms the transaction based off of the users value on the slider, if the gold cost exceeds player's gold than simply cancels /// </summary> public void Confirmation() { confirmObject.SetActive(false); bool signal = true; // Guard check for if the player does not have the gold for the items if (totalCost > player.GetInventory().GetGold() && !isSelling) signal = false; confDeter = signal; playerConfirmation = Task.CompletedTask; } /// <summary> /// Activates the shop by calling the player's inventory sell screen, the shop merchant screen, /// and resets the page calls /// </summary> public void ActivateShop() { if (Globals.InventoryOpened) return; else { Globals.CloseInventory(); GeneralUI.Instance.DeactivateInventoryButton(); } AwakeShop(); ResetUI(); ActivateAnyDiscounts(); currentPage = 0; EnablePage(); FillInventoryBar(player, player.GetInventory().GetAllItems()); buttonFieldObject.SetActive(false); } /// <summary> /// Activates any discounts based on a random value and the player's abilities /// </summary> private void ActivateAnyDiscounts() { discountedItems ??= new List<Item>(); // Makes sure that the if (!discountsApplied) { Dictionary<int, Ability> abList = AbilityBrain.GetAbilityList(); // Random discount chance int discountTimes = Random.Range(0, 3); // Extortion if (player.GetInventory().Abilities.Contains(abList[112]) || player.GetInventory().limitedAbilities.Contains(abList[112])) discountTimes++; foreach (Ally ally in allies) { if (ally.GetInventory().Abilities.Contains(abList[112]) || ally.GetInventory().limitedAbilities.Contains(abList[112])) discountTimes++; } // Cycles through and gathers random items in the store to discount for each discount point the party has including potential discounts randomly generated for (int i = 0; i < discountTimes; ++i) { int randPage = Random.Range(0, pageDic.Count); Dictionary<Item, int> itemPage = pageDic[randPage]; int itemPos = Random.Range(0, itemPage.Count); Item chosenItem = null; for (int cycle = 0; cycle <= itemPos; ++cycle) { chosenItem = itemPage.ElementAt(cycle).Key; if (cycle == itemPos) break; } if (!discountedItems.Contains(chosenItem)) { chosenItem.ImplementDiscountValue(.75f); discountedItems.Add(chosenItem); } else --i; } discountsApplied = true; // False Idol Relic Flag if (Globals.RelicFlags[1]) { foreach (KeyValuePair<int, Dictionary<Item, int>> shopPage in pageDic) { foreach (KeyValuePair<Item, int> shopStock in shopPage.Value) shopStock.Key.ImplementDiscountValue(.99f); } } } } /// <summary> /// Deactivates the shop by clearing the stock to prevent duplication and re-enables the shop layout /// for the player to choose to re-enter the shop or to leave, also sets the images for the shop to sleep /// </summary> public void DeactivateShop() { if (!Globals.InventoryOpened) GeneralUI.Instance.ReactivateInventoryButton(); ClearStock(); EnableShopLayout(); SleepShop(); buttonFieldObject.SetActive(true); } /// <summary> /// Resets the player bar when an item is sold or bought to correctly show the state in the transform /// </summary> private void ResetPlayerBar(Entity activeEntity, Dictionary<Item, int> items) { // Destroys all the old objects before reapplying the objects foreach (Transform c in playerInven) Destroy(c.gameObject); FillInventoryBar(activeEntity, items); } /// <summary> /// Clears all of the objects in the shop inventory transform and the player inventory bar transform /// </summary> private void ClearStock() { foreach (Transform child in shopInven) Destroy(child.gameObject); foreach (Transform child in playerInven) Destroy(child.gameObject); } /// <summary> /// Sets the objects that hold the shop and inventory asleep /// </summary> private void SleepShop() { shopOwnerText.enabled = false; for (int i = 0; i < memberGoldActiveImages.Count; i++) { if (i is 0) memberGoldActiveImages[i].enabled = true; else memberGoldActiveImages[i].enabled = false; } shopObject.SetActive(false); invenObject.SetActive(false); BountyHunterFunction(); } /// <summary> /// Sets the objects that hold the shop and player inventory awake /// </summary> private void AwakeShop() { shopOwnerText.enabled = true; shopObject.SetActive(true); invenObject.SetActive(true); } /// <summary> /// Clears the parent field for the player's choice buttons of any potential extra children /// </summary> public void ResetUI() { foreach (Transform child in buttonField) Destroy(child.gameObject); } /// <summary> /// Switches inventories between ally and player member inventories /// </summary> /// <param name="index">The position reference in the list to enable the member image to indicate which inventory is active</param> /// <param name="inventory">The inventory to retrieve all items from</param> /// <param name="newActiveEntity">The entity to set as the current ally</param> private void SwitchInventories(int index, Inventory inventory, Entity newActiveEntity) { string invenName = newActiveEntity.GetName(); memberInvenText.text = $"{invenName}'s Inventory"; // Simply returns if the inventory clicked on is the same as the current one being shown if (inventory == currentInven) return; ResetPlayerBar(newActiveEntity, inventory.GetAllItems()); GUIHandler(); for (int i = 0; i < MaxPartyMembers; i++) { if (index == i) memberGoldActiveImages[i].enabled = true; else memberGoldActiveImages[i].enabled = false; } } public void ModifyShopQualities(string qualifier) { // Starts at 1f and the discounts deduct float discountValue = 1f; switch (qualifier) { case "Slick Barterer": discountValue -= .05f; break; default: Debug.LogWarning($"Ability qualifier for shop not known. Please check spelling within code to make " + $"sure correct strings are implemented --> Qualifier ({qualifier})"); break; } for (int i = 0; i < pageDic.Count; i++) { foreach (KeyValuePair<Item, int> pair in pageDic[i]) pair.Key.ImplementDiscountValue(discountValue); } } public void Rest(int RestCost) { // Make sure the player has enough gold for the rest if (player.GetInventory().GetGold() < RestCost) return; // Fully revitalize the player player.IncreaseHealth(player.RetrieveMaxHealth()); player.IncreaseMana(player.RetrieveMaxMana()); player.StatusChanged(); if (allies.Length is not 0) { // Fully revitalize each ally foreach (Ally ally in allies) { ally.IncreaseHealth(ally.RetrieveMaxHealth()); ally.IncreaseMana(ally.RetrieveMaxMana()); ally.StatusChanged(); } } // Remove the necessary gold from the player's inventory player.GetInventory().RemoveGold(RestCost); gold_Text.text = "GOLD:" + player.GetInventory().GetGold().ToString("000000"); rested = true; // Deactivate the rest button since everybody is fully healed and refilled buttons[0].GetComponent<Button>().interactable = false; buttons[0].GetComponent<Image>().color = DisabledColor; } private async void InitializeRestConfirmationObject() { restCost.text = RestCost.ToString(); restConfirmObject.SetActive(true); playerConfirmation = null; restIndication = false; while (true) { if (playerConfirmation is null) await Task.Yield(); else break; } restConfirmObject.SetActive(false); if (restIndication) Rest(RestCost); else return; } public void ConfirmRest() { restIndication = true; playerConfirmation = Task.CompletedTask; } public void ConfirmNoRest() => playerConfirmation = Task.CompletedTask; private int GenerateInnCost() { int encounterValue = GameManager.Instance.GetEncounterValue(); if (encounterValue < 10) return Random.Range(15, 50); else if (encounterValue < 20) return Random.Range(100, 250); else return Random.Range(150, 500); } public void AdjustInnRate(object modifier, bool isAdded) { if (modifier is int intMod) { if (isAdded) RestCost += intMod; else RestCost = Mathf.Clamp(RestCost - intMod, 1, int.MaxValue); } else if (modifier is float floMod) { if (isAdded) RestCost += (int)Mathf.Ceil(RestCost * floMod); else RestCost = Mathf.Clamp(RestCost - (int)Mathf.Ceil(RestCost * floMod), 1, int.MaxValue); } } }