Overengineering


This month I focused on creating the consumable items and also the shortcut bar to hold them and use them during the game.

Since this was the third drag a drop operation (after the equipment screen and the crafting menu) I decided to try to make something more flexible that I could reuse in the future. In those two previous occasions, I wrote an script in each case to control the interaction. One of the reasons was that I wanted to implement several control modes, not only drag&drop but also "click to grab" and "click to socket into an slot".

I did plenty of copy paste but also plenty of modifications (for example, in the equipment system anything that is equiped on a creature gets removed from the inventory, while when crafting the ingredients are only removed when you actually craft), so now that I had to do it a third time and maybe would have to make it again in the future, I thought of isolating that behaviour and make a script that would fit all the cases using the two I already had as a template.

Then the overengineering started. I worked on this script for two days:

public interface IHasIcon
{
    Sprite GetIcon();
}

public class SelectOrDragIntoSlot<ContentType,SlotType>
    where SlotType : Slot<ContentType>
    where ContentType : IComparable, IHasIcon
{
    private enum AssignationState
    {
        None,
        SelectedSlot,
        SelectedItem,
    }

    public delegate void ContentSlotted(SlotType slot, ContentType content);
    public event ContentSlotted ContentSlottedEvent;
    public event ContentSlotted ContentRemovedFromSlotEvent;

    private ContentType selectedContent;
    private SlotType selectedSlot;
    private AssignationState state;
    private GameObject draggingItem;

   
    private void IngredientEventsUnsubscribe()
    {
        IngredientSlot.SlotClickedEvent -= SlotClick;
        IngredientSlot.SlotRightClickedEvent -= SlotRightClick;
        ScrapableDisplay.ScrapableClickedEvent -= ContentClick;

    }

    private void IngredientEventsSubscribe()
    {
        IngredientSlot.SlotClickedEvent -= SlotClick;
        IngredientSlot.SlotRightClickedEvent -= SlotRightClick;
        ScrapableDisplay.ScrapableClickedEvent -= ContentClick;
    }
   

    /// <summary>
    /// Try to put content into slot
    /// </summary>
    private void SetSlotContent(SlotType slot, ContentType content)
    {
        if ( slot.ValidateContent(content) )
            slot.SlotContent(content);
    }

    public void SlotClick(SlotType slotClicked)
    {
        if (state == AssignationState.SelectedItem)
        {
            SetSlotContent(slotClicked, selectedContent);
            Deselect();
        }
        else
        {
            SelectSlot(slotClicked);
        }
    }

    public void ContentClick(ContentType contentClicked)
    {
        if (state == AssignationState.SelectedSlot)
        {
            SetSlotContent(selectedSlot,contentClicked);
            Deselect();
        }
        else
        {
            SelectContent(contentClicked);
        }
    }

    public void SlotRightClick(SlotType slotDisplay)
    {
        slotDisplay.Unslot();

        Deselect();
    }

    private void SelectContent(ContentType newSelectedContent)
    {
        Deselect();
        selectedContent = newSelectedContent;

        draggingItem = CreateDragGraphic(newSelectedContent);
        state = AssignationState.SelectedItem;
    }

    private void SelectSlot(SlotType newSelectedSlot)
    {
        Deselect();
        selectedSlot = newSelectedSlot;

        ContentType slotContent = selectedSlot.GetContent();
        if (slotContent.Equals(default) == false)
            draggingItem = CreateDragGraphic(slotContent);

        state = AssignationState.SelectedSlot;
    }

    private void Deselect()
    {
        selectedContent = default;

        selectedSlot = default;

        GameObject.Destroy( draggingItem );

        state = AssignationState.None;
    }

    private GameObject CreateDragGraphic(ContentType item)
    {
        GameObject draggedItemGO = new GameObject("DraggedObject");

        draggedItemGO.AddComponent<RectTransform>();
        draggedItemGO.AddComponent<Image>();
        DraggedItem draggedItem = draggedItemGO.AddComponent<DraggedItem>();
        draggedItem.SetImage(item.GetIcon());

        return draggingItem.gameObject;
    }

    private void Update()
    {
        if ((Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) &&
             (state != AssignationState.None))
        {
            if ((EventSystem.current.currentSelectedGameObject == null) ||
                 (state == AssignationState.SelectedItem && EventSystem.current.currentSelectedGameObject.GetComponent<SlotType>() == null) ||
                 (state == AssignationState.SelectedSlot && (EventSystem.current.currentSelectedGameObject.GetComponent<ContentType>() == null && EventSystem.current.currentSelectedGameObject.GetComponent<InventoryPanel>() == null)))
            {
                Deselect();
            }
        }
    }
}

And I just though to myself "this can't be right". So I watched a youtube tutorial. I had already watched one before starting the first drag and drop system, but I grew it into a monstrosity. Tehy just used the drag and drop interfaces and I decided to do the same. It is true that it is not very flexible and I lost "the click to grab, click again to drop" but I gained sanity. I  think I may add those controls again if I find a simple way to implement them.

Here is the dragging and dropping and using items on creatures in action:

Get Pack

Leave a comment

Log in with itch.io to leave a comment.