I implemented a simple cooldown system in the actionstore script. It seems to work fine. But I wonder if I might have missed something, some critical bug? I gave the dockedItems a cooldown variable. I also added a cooldown variable on the actionitem. I then made a list of DockedItemSlot.
In the use method i check if the item is in cooldown, if so i return false. Then i add the dockedItemSlot to the list and set its cooldown to the cooldown variable in the actionItem script.
In the update method i decrease the value of the cooldown for all the items in the list and remove them from the list if cooldown reaches 0.
I also wonder if someone has any idea of how to make the cooldown visible in the slot the item is in. Ex an area that fades out like a clock arm moving around the slot.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using GameDevTV.Saving;
namespace GameDevTV.Inventories
{
/// <summary>
/// Provides the storage for an action bar. The bar has a finite number of
/// slots that can be filled and actions in the slots can be "used".
///
/// This component should be placed on the GameObject tagged "Player".
/// </summary>
public class ActionStore : MonoBehaviour, ISaveable
{
List<DockedItemSlot> coolDownItems = new List<DockedItemSlot>();
private void Update()
{
for (int i = coolDownItems.Count - 1; i >= 0; i--)
{
coolDownItems[i].cooldown -= Time.deltaTime;
if (coolDownItems[i].cooldown < 0)
{
coolDownItems.Remove(coolDownItems[i]);
}
}
}
// STATE
Dictionary<int, DockedItemSlot> dockedItems = new Dictionary<int, DockedItemSlot>();
private class DockedItemSlot
{
public ActionItem item;
public int number;
public float cooldown = 0;
}
// PUBLIC
/// <summary>
/// Broadcasts when the items in the slots are added/removed.
/// </summary>
public event Action storeUpdated;
/// <summary>
/// Get the action at the given index.
/// </summary>
public ActionItem GetAction(int index)
{
if (dockedItems.ContainsKey(index))
{
return dockedItems[index].item;
}
return null;
}
/// <summary>
/// Get the number of items left at the given index.
/// </summary>
/// <returns>
/// Will return 0 if no item is in the index or the item has
/// been fully consumed.
/// </returns>
public int GetNumber(int index)
{
if (dockedItems.ContainsKey(index))
{
return dockedItems[index].number;
}
return 0;
}
/// <summary>
/// Add an item to the given index.
/// </summary>
/// <param name="item">What item should be added.</param>
/// <param name="index">Where should the item be added.</param>
/// <param name="number">How many items to add.</param>
public void AddAction(InventoryItem item, int index, int number)
{
if (dockedItems.ContainsKey(index))
{
if (object.ReferenceEquals(item, dockedItems[index].item))
{
dockedItems[index].number += number;
}
}
else
{
var slot = new DockedItemSlot();
slot.item = item as ActionItem;
slot.number = number;
dockedItems[index] = slot;
}
if (storeUpdated != null)
{
storeUpdated();
}
}
/// <summary>
/// Use the item at the given slot. If the item is consumable one
/// instance will be destroyed until the item is removed completely.
/// </summary>
/// <param name="user">The character that wants to use this action.</param>
/// <returns>False if the action could not be executed.</returns>
public bool Use(int index, GameObject user)
{
if (dockedItems.ContainsKey(index))
{
for (int i = coolDownItems.Count - 1; i >= 0; i--)
{
if(coolDownItems[i].cooldown > 0 && object.ReferenceEquals(coolDownItems[i].item, dockedItems[index].item))
{
return false;
}
}
dockedItems[index].item.Use(user);
coolDownItems.Add(dockedItems[index]);
var coolDownItem = coolDownItems.Last();
coolDownItem.cooldown = dockedItems[index].item.coolDownTimer();
if (dockedItems[index].item.isConsumable())
{
RemoveItems(index, 1);
}
return true;
}
return false;
}
/// <summary>
/// Remove a given number of items from the given slot.
/// </summary>
public void RemoveItems(int index, int number)
{
if (dockedItems.ContainsKey(index))
{
dockedItems[index].number -= number;
if (dockedItems[index].number <= 0)
{
dockedItems.Remove(index);
}
if (storeUpdated != null)
{
storeUpdated();
}
}
}
/// <summary>
/// What is the maximum number of items allowed in this slot.
///
/// This takes into account whether the slot already contains an item
/// and whether it is the same type. Will only accept multiple if the
/// item is consumable.
/// </summary>
/// <returns>Will return int.MaxValue when there is not effective bound.</returns>
public int MaxAcceptable(InventoryItem item, int index)
{
var actionItem = item as ActionItem;
if (!actionItem) return 0;
// if we already have an item in this slot but its not of the same type as the item we want to
// add we cant add it.
if (dockedItems.ContainsKey(index) && !object.ReferenceEquals(item, dockedItems[index].item))
{
return 0;
}
// if its a consumable item you can take as many items as you want in the slot.
if (actionItem.isConsumable())
{
return int.MaxValue;
}
// if its not a consumable item and it already contains an item you cant take anything.
if (dockedItems.ContainsKey(index))
{
return 0;
}
//otherwise if its not consumable you can take just one item.
return 1;
}
/// PRIVATE
[System.Serializable]
private struct DockedItemRecord
{
public string itemID;
public int number;
}
object ISaveable.CaptureState()
{
var state = new Dictionary<int, DockedItemRecord>();
foreach (var pair in dockedItems)
{
var record = new DockedItemRecord();
record.itemID = pair.Value.item.GetItemID();
record.number = pair.Value.number;
state[pair.Key] = record;
}
return state;
}
void ISaveable.RestoreState(object state)
{
var stateDict = (Dictionary<int, DockedItemRecord>)state;
foreach (var pair in stateDict)
{
AddAction(InventoryItem.GetFromID(pair.Value.itemID), pair.Key, pair.Value.number);
}
}
}
}