FolkeLogo

The Owlchemist - Mire of Malice

Genre: Single Player, Survival, Explorer


Platform & Control: Handheld Controller


Engine: Unity Engine


Development Time: 7 Weeks


Team Size: 3 Designers, 4 3D Artist, 2 2D Artis, 3 Programmers


Describtion of the game:

As a travelling Alchemist, the player will get quests from people in need, requiring certain potions. The ingredients to create these potions are spread out across the swamp-like world, a place that is also filled with heavy fog, darkness and enemies. From the safety of their cart, the player will explore outwards with a torch that slowly decays. The torch clears the heavy fog and keeps some enemies at bay. When the torch runs out, the fog is thicker and the player slower, and the enemies more dangerous.

My responsibility as a programmer:

Light 

I created a light component that required a light source. All the light components can be controlled by a system called “Light System”.

Jump to Light


Gathering 

The game revolved in gathering mysterious materials to combine them into potion which was managed by scriptableobject.

Jump to Gathering


Inventory 

The player can carry 4 different items in an inventory. All items in the game was of same type of scriptableobject.

Jump to Inventory


Crafting 

By using a HUB the player could craft potions to progress further into the game. The crafting required that the player had gathered materials.

Jump to Crafting


Combat 

Even though the mist is dangerous for the player, there are even more evil creatures lurking around. The player can use potion to survive.

Jump to Combat

Light

LightComponent


Every lightsource in the game are using a “LightComponent” that can be controlled by a light system. All the components can be reached by other components, making it very convenient to have communicate between various components.


All LightComponents have:


  • Fuel - How long will the light shine


  • Decay - How fast does the fuel getting drained


  • Intensity - How bright is the light


  • IsActive - Since enemies are afraid of the light, the component hold a bool telling if it is active or not


  • Fizzle - A particle system to make the light look alive

          		
public class LightComponent : BaseComponent
{
    public bool startLit = false;
    public int maxCharges = 3;
    public int currentCharges { get; set; }
    public Light lightSource;
    public GameObject flames;
    public ParticleSystem fizzleSystem;
    public float lightIntensity;
    public float maxFuel = 100f;
    public float currentFuelDecay { get; set; }
    public float fuelDecay = 10f;
    public float runningFuelDecay = 6f;
    public float increasedFuelDecay = 30f;
    public float maxIntensity = 2f;
    public float minIntensity = .5f;
    public AudioClip activetorchSoundLoop;
    public List<Light> nearbyLights = new List<Light>();
    public float currentFuel;
    AudioSource source;
    private ParticleSystem flameSystem;

    private void Awake()
    {
        lightSource.enabled = startLit;
        flames.SetActive(true);
        lightIntensity = lightSource.intensity;
        currentFuel = 0;
        source = GetComponent<AudioSource>();
        source.clip = activetorchSoundLoop;
        source.loop = true;
        source.Play();
        currentCharges = maxCharges;
        flameSystem = flames.GetComponent<ParticleSystem>();
        currentCharges = 0;
    }

    public void Reset()
    {
        currentFuel = maxFuel;
    }

    public bool IsLightEnabled()
    {
        return lightSource.enabled;
    }

    public void PlayFizzle()
    {
        fizzleSystem.Play();
    }

    public void SetLightEnabled(bool value)
    {
        lightSource.enabled = value;
        if (value)
        {
            flameSystem.Play();
            source.Play();
        }
        else
        {
            flameSystem.Stop();
            source.Stop();
        }
    }
}
        
	
          		
public class LightSystem : BaseSystem
{
    public Filter[] filters;
    public GridComponent gridComp;
    private LightComponent lightyComp;
    [System.Serializable]
    public struct Filter
    {
        public Filter(
            int id,
            GameObject go,
            InputComponent ic,
            LightComponent lc
            )
        {
            this.id = id;

            gameObject = go;
            lightComponent = lc;
            inputComponent = ic;
        }
        public int id;
        public GameObject gameObject;
        public LightComponent lightComponent;
        public InputComponent inputComponent;
    }

    public override void Initialize(Transform[] objects)
    {
        List<Filter> tmpFilters = new List<Filter>();
        int index = 0;
        for (int i = 0; i < objects.Length; i++)
        {
            LightComponent lc = objects[i].GetComponent<LightComponent>();
            InputComponent ic = objects[i].GetComponent<InputComponent>();
            if (lc && ic)
            {
                tmpFilters.Add(new Filter(index, objects[i].gameObject, ic, lc));
                lightyComp = lc;
            }
        }
        filters = tmpFilters.ToArray();
        player.uiComponent.torchIndicator.SetImageFillAmount(1, 0f);
        player.uiComponent.torchIndicator.SetImageFillAmount(2, 0f);
        player.uiComponent.torchIndicator.SetImageFillAmount(3, 0f);
        gridComp = GetComponentInChildren<GridComponent>();
    }
    public override void SetupInputComponent(InputComponent inputComponent)
    {
        player.eventComponent.OnRefillOneTorch += RechargeOneTorch;
    }
    public override void Tick(float deltaTime)
    {
        for (int i = 0; i < filters.Length; i++)
        {
            Filter filter = filters[i];
            LightComponent lightComp = filter.lightComponent;
            InputComponent inputComp = filter.inputComponent;
            // ----- logic -----
            if (lightComp.currentCharges != 0)
            {
                if (inputComp.rightShoulderDown || Input.GetKeyDown(KeyCode.R))
                {
                    if (lightComp.currentFuel > 0)
                    {
                        SetLightActive(lightComp, !lightComp.IsLightEnabled(), true);
                    }
                }
                if (lightComp.currentFuel > 0 && lightComp.IsLightEnabled())
                {
                    DecayLight(lightComp, deltaTime);
                }
            }
            else if (inputComp.rightShoulderDown || Input.GetKeyDown(KeyCode.R))
            {
                FailAttemptSetLightActive(lightComp, !lightComp.IsLightEnabled(), true);
            }
            if (inputComp.leftBumberDown || Input.GetKeyDown(KeyCode.T))
            {
                DevRechargeTorches();
            }
            foreach (Light light in player.lightComponent.nearbyLights)
            {
                if (!light.transform.parent.gameObject.activeSelf && player.lightComponent.nearbyLights.Contains(light))
                {
                    player.lightComponent.nearbyLights.Remove(light);
                    break;
                }
            }
        }
    }
    private void DecayLight(LightComponent lc, float deltaTime)
    {
        TorchIndicator indica = player.uiComponent.torchIndicator;
        float fuelDecay = player.combatComponent.isRunning ? lc.runningFuelDecay : lc.fuelDecay;
        lc.currentFuel -= fuelDecay * deltaTime;
        float fuelPercentage = lc.currentFuel / lc.maxFuel;
        lc.lightSource.intensity = Mathf.Lerp(lc.minIntensity, lc.maxIntensity, fuelPercentage);
        indica.SetImageFillAmount(lc.currentCharges, fuelPercentage);
        if (lc.lightSource.intensity <= lc.minIntensity)
        {
            SetLightActive(lc, false, false);
            if (lc.currentCharges > 0)
            {
                if (lc.currentCharges != 1)
                {
                    lc.currentFuel = lc.maxFuel;
                }
                lc.currentCharges--;
            }
        }
    }
    private void SetLightActive(LightComponent lc, bool value, bool triggerAnimation)
    {
        if (triggerAnimation)
        {
            if (player.movementComponent.movementAllowed)
            {
                player.animationComponent.OnPlayTorchToggleAnimation();
                player.movementComponent.SetMovementAllowed(false);
            }
        }
        else
        {
            lc.SetLightEnabled(value);
        }
    }
    private void FailAttemptSetLightActive(LightComponent lc, bool value, bool triggerAnimation)
    {
        if (triggerAnimation)
        {
            if (player.movementComponent.movementAllowed)
            {
                player.animationComponent.OnPlayTorchToggleAnimation();
                player.movementComponent.SetMovementAllowed(false);
            }
        }
    }
    private void RechargeOneTorch()
    {
        if (player.lightComponent.currentCharges < player.lightComponent.maxCharges)
        {
            if (player.lightComponent.currentCharges != 0)
            {
                player.uiComponent.torchIndicator.SetImageFillAmount(player.lightComponent.currentCharges, 1f);
            }
            player.lightComponent.currentCharges++;
            player.uiComponent.torchIndicator.PlayVfx(player.lightComponent.currentCharges);
            player.lightComponent.currentFuel = player.lightComponent.maxFuel;
            player.uiComponent.torchIndicator.SetImageFillAmount(player.lightComponent.currentCharges, 1f);
        }
        else
        {
            player.uiComponent.torchIndicator.SetImageFillAmount(player.lightComponent.currentCharges, 1f);
            player.lightComponent.currentFuel = player.lightComponent.maxFuel;
        }
    }
    private void DevRechargeTorches()
    {
        player.lightComponent.currentCharges = 3;
        player.lightComponent.currentFuel = player.lightComponent.maxFuel;
        player.uiComponent.torchIndicator.SetImageFillAmount(1, 1f);
        player.uiComponent.torchIndicator.SetImageFillAmount(2, 1f);
        player.uiComponent.torchIndicator.SetImageFillAmount(3, 1f);
    }
    private void OnDrawGizmos()
    {
        if (lightyComp == null) { return; }

        Gizmos.DrawWireSphere(lightyComp.transform.position, lightyComp.lightIntensity);
    }
}
        
	

Gathering

The gathering is very intertwined with the inventory and crafting. Starting off, I had to find an easy way for designers to brand the items in the game. By creating a ScriptableObject called “BaseResource” the designer could now brand every item in the game and every time a new item was created, it could be marked and ready to go.


The BaseResource let the designer create freely any new item. If an item was marked with the BaseResource, it could be picked-up, crafted, dropped, etc.

When creating a new BaseResource, the designer could alter:

  • InteractionTime - How long will it take to pick it up from the ground

  • DestroyonPickup - Some items got consumed when interacted and got destroyed

  • GuiSprite - The GUI sprite shown in the inventory


The player had a GatheringComponent attached to it that checked for nearby collectibles. Every collectible had a CollectibleComponent where there was an enum that could be set, telling the inventory what type of item it was.

          		
[CreateAssetMenu(menuName = "New Resource")]
public class BaseResource : ScriptableObject
{
    public float interactionTime = 1.0f;
    public bool destroyObjectOnPickup = true;
    public Sprite GuiSprite;
    public string GetName()
    {
        return name;
    }

    public virtual bool PerformCollectibleEvent(PlayerFilter player)
    {
        return false;
    }
}
        
	
          		
public class GatheringComponent : BaseComponent
{
    public float viewRadius;
    [Range(0, 360)]
    public float viewAngle;
    public LayerMask targetMask;
    public LayerMask obstacleMask;
    public bool hasPurificationPotion = false;
    public List<CollectibleComponent> nearbyCollectibles = new List<CollectibleComponent>();

    public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal)
    {
        if (!angleIsGlobal)
        {
            angleInDegrees += transform.eulerAngles.y;
        }
        return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
    }
}
        
	
          		
public class CollectibleComponent : InteractableComponent
{
    public BaseResource baseCollectible;
    public int Quantity = 1;
    public InteractableComponent interactable { get; private set; }
    public CollectibleType TypeofCollectible = CollectibleType.INGREDIENT;
    [HideInInspector] public Transform startTransform;

    [Header("Prototype Extras")]
    public bool isGatherable = true;
    public enum CollectibleType
    {
        INGREDIENT,
        BATTLEPOTION,
        UTILITY,
        LETTER
    }
    private void Awake()
    {
        startTransform = this.transform;
    }
    public void ResetTransform()
    {
        if(gameObject.activeInHierarchy == false)
        {
            gameObject.SetActive(true);
        }
        transform.position = startTransform.position;
        transform.rotation = startTransform.rotation;
        transform.localScale = startTransform.localScale;
    }
}
        
	

Inventory


When the player picks up stuff it goes into the inventory. The inventory can hold up to 4 different items at once. When the player enters their cart the items are transferred into a separate inventory that are used when crafting.


The inventory gui is handled separate from the rest of the inventory logics.


Using the D-pad, the player can drop items from the inventory.


The dropped item will spawn beneath the player.

          		
public class InventorySystem : BaseSystem
{
    public Filter[] filters;
    public GameObject tempUIfix;
    [System.Serializable]
    public struct Filter
    {
        public Filter(
            int id,
            GameObject go,
            InventoryComponent ic,
            InputComponent inc,
            InteractorComponent iac,
            CartComponent cart
            )
        {
            this.id = id;
            gameObject = go;
            inventoryComponent = ic;
            inputComponent = inc;
            interactorComponent = iac;
            cartComponent = cart;
        }
        public int id;

        public GameObject gameObject;
        public InventoryComponent inventoryComponent;
        public InputComponent inputComponent;
        public InteractorComponent interactorComponent;
        public CartComponent cartComponent;
    }

    public override void Initialize(Transform[] objects)
    {
        List<Filter> tmpFilters = new List<Filter>();
        int index = 0;
        for (int i = 0; i < objects.Length; i++)
        {
            InventoryComponent ic = objects[i].GetComponent<InventoryComponent>();
            InteractorComponent iac = objects[i].GetComponent<InteractorComponent>();
            InputComponent inc = objects[i].GetComponent<InputComponent>();
            CartComponent cart = objects[i].GetComponent<CartComponent>();
            if (ic && iac && inc)
            {
                tmpFilters.Add(new Filter(index, objects[i].gameObject, ic, inc, iac, cart));
            }
        }
        filters = tmpFilters.ToArray();
    }
    
    public override void Tick(float deltaTime)
    {
        for (int i = 0; i < filters.Length; i++)
        {
            Filter filter = filters[i];
            InventoryComponent inventoryComp = filter.inventoryComponent;
            InputComponent inputComp = filter.inputComponent;
            InteractorComponent interactComp = filter.interactorComponent;
            CartComponent cartComp = filter.cartComponent;
            // ----- logic -----
            if (inputComp.GetKeyDown(KeyCode.E) || inputComp.GetButtonDown("Fire1"))
            {
                AttemptWorldInteract(interactComp, player.inventoryComponent, inputComp.worldInteractMask);
                interactComp.SetInteractMode(InteractMode.Object);
            }
            if (inputComp.upDPad)
            {
                DropItem(0);
            }
            else if (inputComp.rightDPad)
            {
                DropItem(1);
            }
            else if (inputComp.downDPad)
            {
                DropItem(2);
            }
            else if (inputComp.leftDPad)
            {
                DropItem(3);
            }
        }
    }

    public void SetNewPositionandActivateObjectatDrop(Vector3 newPosition, GameObject droppedItem)
    {
        droppedItem.SetActive(true);
        if (droppedItem.GetComponent<CollectibleComponent>().TypeofCollectible != CollectibleComponent.CollectibleType.BATTLEPOTION)
        {
            droppedItem.transform.position = player.go.transform.position + player.go.transform.forward;//new Vector3(newPosition.x, newPosition.y, newPosition.z);
        }
        RaycastHit rayhit;
        Ray ray = new Ray(player.go.transform.position + player.go.transform.forward, Vector3.down);

        if (Physics.Raycast(ray, out rayhit, 1000, player.interactorComponent.groundMask))
        {
            if (droppedItem.GetComponent<CombatPotion>())
            {
                if (droppedItem.GetComponent<CombatPotion>().BattleType != CombatPotion.BattlePoition.QUEST)
                {
                    droppedItem.transform.position = player.go.transform.position;
                    droppedItem.transform.position = new Vector3(newPosition.x, rayhit.point.y /*+(droppedItem.GetComponent<MeshRenderer>().bounds.center.y / 2)*/, newPosition.z);
                }
            }
            else
            {
                droppedItem.transform.position = player.go.transform.position;
                droppedItem.transform.position = new Vector3(newPosition.x, rayhit.point.y /*+(droppedItem.GetComponent<MeshRenderer>().bounds.center.y / 2)*/, newPosition.z);
            }
        }
        if (droppedItem.GetComponent<CollectibleComponent>().TypeofCollectible == CollectibleComponent.CollectibleType.BATTLEPOTION)
        {
            droppedItem.GetComponent<CollectibleComponent>().isGatherable = false;
            CombatPotion combatPotion = droppedItem.GetComponent<CombatPotion>();
            HealthComponent playerHealth = player.go.GetComponent<HealthComponent>();
            if (combatPotion.BattleType == CombatPotion.BattlePoition.HEALTH)
            {
                playerHealth.RestoreGranularDamageOverTime(100, 6f, true);
            }
            if (combatPotion.BattleType == CombatPotion.BattlePoition.LIGHT)
            {
                playerHealth.RestoreGranularDamageOverTime(10, 3f, true);
            }
            if (combatPotion.BattleType == CombatPotion.BattlePoition.QUEST)
            {
                playerHealth.RestoreGranularDamageOverTime(10, 5f, true);
                if (Vector3.Distance(combatPotion.questObjective.transform.position, player.go.transform.position) < 5)
                {
                    combatPotion.questComplete = true;
                    player.go.GetComponent<PlayerEventComponent>().OnSisterCleansed();        
                }
                else
                {
                    droppedItem.transform.position = new Vector3(newPosition.x, newPosition.y, newPosition.z);
                    RaycastHit rayhited;
                    Ray rayed = new Ray(player.go.transform.position + player.go.transform.forward, Vector3.down);
                    if (Physics.Raycast(rayed, out rayhited, 1000, player.interactorComponent.groundMask))
                    {
                        droppedItem.transform.position = player.go.transform.position;
                        droppedItem.transform.position = new Vector3(newPosition.x, rayhited.point.y, newPosition.z);
                    }
                }
            }
        }
    }
    
    private void DropItem(int index)
    {
        if (player.inventoryComponent.InventoryBag[index].itemType.baseCollectible != player.inventoryComponent.tempColl.itemType.baseCollectible)
        {
            GatheringSystem gathering = GetComponent<GatheringSystem>();
            for (int i = 0; i < gathering.pickedUpItems.Count; i++)
            {
                if (gathering.pickedUpItem[i].baseCollectible == player.inventoryComponent.InventoryBag[index].itemType.baseCollectible)
                {
                    if (gathering.pickedUpItem[i].GetComponent<CombatPotion>())
                    {
                        if (!gathering.pickedUpItem[i].GetComponent<CombatPotion>().isCurrentlyActivated)
                        {
                            SetNewPositionandActivateObjectatDrop(player.go.transform.position + player.go.transform.forward, gathering.pickedUpItem[i].gameObject);
                            if (!gathering.pickedUpItem[i].GetComponent<CombatPotion>())
                            {
                                gathering.pickedUpItem.RemoveAt(i);
                            }
                            player.inventoryComponent.invUI.RemoveFromInventoryUI(player.inventoryComponent.InventoryBag[index].itemType);
                            player.inventoryComponent.RemoveItemFromInventory(player.inventoryComponent.InventoryBag[index].itemType, 1);
                            player.animationComponent.OnPlayThrowAnimation();
                            break;
                        }
                    }
                    else
                    {
                        SetNewPositionandActivateObjectatDrop(player.go.transform.position + player.go.transform.forward, gathering.pickedUpItem[i].gameObject);

                        if (!gathering.pickedUpItem[i].GetComponent<CombatPotion>())
                        {
                            gathering.pickedUpItem.RemoveAt(i);
                        }
                        player.inventoryComponent.invUI.RemoveFromInventoryUI(player.inventoryComponent.InventoryBag[index].itemType);
                        player.inventoryComponent.RemoveItemFromInventory(player.inventoryComponent.InventoryBag[index].itemType, 1);
                        break;
                    }
                }
            }
        }
        player.uiComponent.groceryList.UpdateList();
    }
    private void AttemptWorldInteract(InteractorComponent interactorComp, InventoryComponent inventoryComp, LayerMask mask)
    {
        Transform t = interactorComp.GetOwnerGO().transform;
        Vector3 position = t.position + t.forward;
        RaycastHit[] hits;
        hits = Physics.BoxCastAll(position, Vector3.one, t.forward, Quaternion.identity, 1f, mask);

        for (int i = 0; i < hits.Length; i++)
        {
            CartComponent tc = hits[i].transform.GetComponentInParent<CartComponent>();
            if (tc)
            {
                interactorComp.currentInteractable = tc;
                tc.isTraveling = false;
                interactorComp.SetInteractMode(InteractMode.Object);
                break;
            }
        }
    }
        
	
          		
public class UseInventorySystem : MonoBehaviour
{
    private GameObject player;
    public Image[] sprites;
    public List<Text> amountTexts = new List<Text>();
    public Sprite StandardSprite;
    private InventorySlot[] slots;

    private void Start()
    {
        List<Image> tempSprites = new List<Image>();
        player = GameObject.FindObjectOfType<MovementComponent>().gameObject;
        slots = GetComponentsInChildren<InventorySlot>();

        for (int i = 0; i < slots.Length; i++)
        {
            tempSprites.Add(slots[i].GetComponent<Image>());
        }
        sprites = tempSprites.ToArray();

        for (int i = 0; i < slots.Length; i++)
        {
            slots[i].image.sprite = StandardSprite;
        }
    }
    public void UpdateInventory(CollectibleComponent br)
    {
        for (int i = 0; i < slots.Length; i++)
        {
            if (slots[i].image.sprite == br.baseCollectible.GuiSprite)
            {
                slots[i].amount += br.Quantity;
                slots[i].amountTXT.text = slots[i].amount.ToString();
                return;
            }
        }
        for (int i = 0; i < slots.Length; i++)
        {
            if (slots[i].image.sprite == StandardSprite)
            {
                slots[i].image.sprite = br.baseCollectible.GuiSprite;
                slots[i].amount += br.Quantity;
                slots[i].amountTXT.text = slots[i].amount.ToString();
                return;
            }
        }
    }
    public void RemoveFromInventoryUI(CollectibleComponent br)
    {
        int slotIndex = 10;
        for (int i = 0; i < slots.Length; i++)
        {
            if (slots[i].image.sprite == br.baseCollectible.GuiSprite)
            {
                slotIndex = i;
            }
        }
        if(slotIndex < 6
        {
            if (slots[slotIndex].image.sprite != StandardSprite && slotIndex < slots.Length)
            {
                slots[slotIndex].amount -= 1;
                if (slots[slotIndex].amount < 1)
                {
                    sprites[slotIndex].sprite = StandardSprite;
                }
                slots[slotIndex].amountTXT.text = slots[slotIndex].amount.ToString();
                if (slots[slotIndex].amount < 1)
                {
                    slots[slotIndex].amountTXT.text = "";
                }
            }
        }
    }
}
        
	

Crafting

Crafting is something I’ve been wanting to apply to a game for a long time. I had this idea of making a dynamic list for the designers to design whatever recipe they want using any ingredient.


The designers could create any recipe they wanted and choose what the final item was going to be. They could easily extend the list and add any ingredient they wanted. All revolving around BaseResource (same as in Gathering).

          		
public class CraftingSystem : BaseSystem
    {
    [HideInInspector]
    public Filter[] filters;
    public Button CraftThis;
    CartComponent cartComponent;
    [System.Serializable]
    public struct Filter
    {
        public Filter(int id,GameObject go,InventoryComponent ic)
        {
            this.id = id;
            gameObject = go;
            inventoryComponent = ic;
        }
        public int id;
        public GameObject gameObject;
        public InventoryComponent inventoryComponent;
    }

    public override void Initialize(Transform[] objects)
    {
        List<Filter> tmpFilters = new List<Filter>();
        int index = 0;
        for (int i = 0; i < objects.Length; i++)
        {
            InventoryComponent ic = objects[i].GetComponent<InventoryComponent>();
            if (ic)
            {
                tmpFilters.Add(new Filter(index, objects[i].gameObject, ic));
            }
        }
        filters = tmpFilters.ToArray();
        cartComponent = GetComponentInChildren<CartComponent>();
    }
    public bool craftPotion1 { get; set; }
    public override void Tick(float deltaTime)
    {
        for (int i = 0; i < filters.Length; i++)
        {
            Filter filter = filters[i];
            InventoryComponent inventoryComp = filter.inventoryComponent;
        }
    }
    [System.Serializable]
    public struct Ingredient
    {
        public BaseResource item;
        public int amount;
    }
    [System.Serializable]
    public class ItemRecipe
    {
        public Ingredient[] ingredients;
        public CollectibleComponent finalItem;
        public string Name() { return finalItem.baseCollectible.name; }
    }
    public List<ItemRecipe> recipeList = new List<ItemRecipe>();
    public List<ItemRecipe> GetRecipes()
    {
        return recipeList;
    }
    
    void CreateItem(CollectibleComponent finalItem)
    {
        CollectibleComponent newItem;
        newItem = finalItem;
        if (finalItem.TypeofCollectible == CollectibleComponent.CollectibleType.BATTLEPOTION)
        {
            player.inventoryComponent.AddtoInventroy(newItem, player.inventoryComponent, newItem.Quantity);
        }
        else
        {
            cartComponent.GetComponent<InventoryComponent>().AddtoInventroy(newItem, cartComponent.GetComponent<InventoryComponent>(), newItem.Quantity);
        }
    }
    public bool CanCraft(ItemRecipe currentRecipe)
    {
        for (int i = 0; i < recipeList.Count; i++)
        {
            if (recipeList[i].finalItem == currentRecipe.finalItem)
            {  
                return true;
            }
        }
        return false;
    }
    public bool CraftGotMaterials(int currentRecipeIndex)
    {
        int TempCheck = 0;
        InventoryComponent pInventory = cartComponent.GetComponent<InventoryComponent>();

        List<CollectibleComponent> itemsInInventory = new List<CollectibleComponent>();

        for (int i = 0; i < recipeList[currentRecipeIndex].ingredients.Length; i++)
        {
            if (pInventory.DoesItemExist(recipeList[currentRecipeIndex].ingredients[i].item))
            {
                int inventoryAmount = pInventory.GetItemFromInventory(recipeList[currentRecipeIndex].ingredients[i].item).amountOfItem;

                if (recipeList[currentRecipeIndex].ingredients[i].amount <= inventoryAmount)
                {
                    TempCheck++;
                }

            }
        }

        if (TempCheck >= recipeList[currentRecipeIndex].ingredients.Length)
        {
            return true;
        }
        else
        {
            return false;
        }

    }
    public void CraftFromRecipe(int currentRecipeIndex)
    {
        int TempCheck = 0;
        InventoryComponent pInventory = cartComponent.GetComponent<InventoryComponent>();
        List<CollectibleComponent> itemsInInventory = new List<CollectibleComponent>();
        for (int i = 0; i < recipeList[currentRecipeIndex].ingredients.Length; i++)
        {
            if (pInventory.DoesItemExist(recipeList[currentRecipeIndex].ingredients[i].item))
            {
                int inventoryAmount = pInventory.GetItemFromInventory(recipeList[currentRecipeIndex].ingredients[i].item).amountOfItem;
                if (recipeList[currentRecipeIndex].ingredients[i].amount <= inventoryAmount)
                {
                    TempCheck++;
                }
            }
        }

        if (TempCheck >= recipeList[currentRecipeIndex].ingredients.Length)
        {
            for (int i = 0; i < recipeList[currentRecipeIndex].ingredients.Length; i++)
            {
                pInventory.GetItemFromInventory(recipeList[currentRecipeIndex].ingredients[i].item, recipeList[currentRecipeIndex].ingredients[i].amount, true);
            }
            CreateItem(recipeList[currentRecipeIndex].finalItem);
        }
    }
        
	

Combat

Among the potions the player can craft, there are utility potions and also battle potion. In the picture to the right, you see a “Sunshine potion” that makes enemies run away in fear. It doesn’t require too much materials and is very effective when to many enemies starting to corner you.

The dropping of potions is highly intertwined with the inventory system and crafting system. The code is not the prettiest because it was made in a hurry due to time.

          		
    public enum BattlePoition
    {
        LIGHT,
        QUEST,
        HEALTH
    }
    private Player player;
    private float intensity;
    private bool isCurrentlyActivated = false;
    private bool questComplete = false;
    private bool potionDelay = true;
    private float potionLightDelay = 0.7f;
    private Vector3 originalPos;
    private BattlePotion potionType = BattlePotion.LIGHT;
    
    public GameObject healthIndicator;
    public GameObject questObjective;
    public GameObject ChildVFXQuestComplete;
    public GameObject ChildVFXQuestMissed;
    public GameObject wellSmoke;
    public GameObject storyActivated;
    
    private void Start()
    {
        if (potionType == BattlePoition.LIGHT)
        {
            intensity = GetComponentInChildren<Light>().intensity;
            GetComponentInChildren<Light>().enabled = false;
        }
        player = this.transform.parent.GetComponent<Player>();
        
    }
    
    private void Update()
    {
        switch(potionType)
        {
        case BattlePoition.LIGHT:
            LightPotion();
            break;
        case BattlePoition.QUEST:
            QuestPotion();
            break;
        case BattlePoition.HEALTH:
            HealthPotion();
            break;
        }
    }
    
    void LightPotion()
    {
        if (!potionDelay)
        {
            isCurrentlyActivated = true;
            Light light = GetComponentInChildren<Light>();
            light.intensity--;

            if (light.intensity <= 2)
            {
                gameObject.SetActive(false);
                light.intensity = intensity;
                isCurrentlyActivated = false;
                potionDelay = true;
                light.enabled = false;
            }
        }
        else
        {
            potionLightDelay -= Time.deltaTime;
            if (potionLightDelay < 0)
                {
                potionDelay = false;
                potionLightDelay = 0.7f;
                light.enabled = true;
                }
            }
        }
    }
    void QuestPotion()
    {
        isCurrentlyActivated = true;

        if (Vector3.Distance(questObjective.transform.position, transform.position) < 5 && questComplete == false)
        {
            if (storyActivated.GetComponent<SisterDelete>())
            {
                player.GetComponent<PlayerEventComponent>().OnSisterCleansed();
            }
            if (storyActivated.GetComponent<StoryEndGame>())
            {
                player.GetComponent<PlayerEventComponent>().StartEnd();
            }
            questComplete = true;
        }
        if (questComplete)
        {
            ChildVFXQuestComplete.SetActive(true);
            if (ChildVFXQuestComplete.GetComponent<ParticleSystem>().isStopped) 
            {
                isCurrentlyActivated = false;
                ChildVFXQuestComplete.SetActive(false);
                gameObject.SetActive(false);
                if (wellSmoke != null)
                {
                    wellSmoke.SetActive(false);
                }
            }
        }
        else
        {
            ChildVFXQuestMissed.SetActive(true);
            if (ChildVFXQuestMissed.GetComponent<ParticleSystem>().isStopped) 
            {
                isCurrentlyActivated = false;
                ChildVFXQuestMissed.SetActive(false);
                gameObject.SetActive(false);
            }
        }
    }
    
    void HealthPotion()
    {
        isCurrentlyActivated = true;
        if (transform.GetChild(0).GetChild(0).GetComponent<ParticleSystem>().isStopped)
        {
            isCurrentlyActivated = false;
            gameObject.SetActive(false);
        }
    }