Construction System

I have to admit something, Brian.

Whilst these courses initially were my solution to build something similar to RuneScape, and I can’t find anything better on the internet back then, I think the biggest ROI I ever got out of this, was getting to work with you. From everything we worked on together, you clearly know how to solve any problem we encounter, and you absolutely know how to make things work

From the bottom of my heart Brian, apart from the courses, you were the biggest prize and jackpot of all of this. Thank you so much for being tolerant with me this far, and thank you for teaching me (you and @bixarrio) everything I know so far in advanced Unity systems, and giving me the amazing opportunity to try out new ideas. You literally made the impossible, possible, for a 26 year old guy who is a big dreamer

You guys are amazing

I just wanted to say this :slight_smile: - anyway, no more emotions I guess… people seem to hate that :stuck_out_tongue_winking_eye:

2 Likes

heya @Brian_Trotter @bixarrio (mainly bixarrio for this one)

OK so… I’ve spent the past couple of hours digging deep into the EBS Code, trying to figure out where in the world does the code go to, when it’s executing the function to select a building part to construct… After about 3 hours of searching, I finally found this in a script called ‘UICircularBuildingMenu.cs’:

        public void SelectBuildingPart(string buildingName)
        {
            BuildingPlacer.Instance.SelectBuildingPart(BuildingManager.Instance.GetBuildingPartByName(buildingName));
            BuildingPlacer.Instance.ChangeBuildMode(BuildingPlacer.BuildMode.PLACE);
            CloseMenu();
        }

Basically if the content of this function is commented out, the player is unable to click on that option. He can hover over it, but can’t click on it, which is perfect for what I want

Alongside, the developer provided me the code that he used to implement that function above for the “RPGBuilder” Unity Asset, as follows (if that helps):

CircularButtonSettings m_selectedButtonSettings;
m_selectedButtonSettings = circularButtonSettings;

public void SelectBuilding(string name)
        {
            if (m_SelectedButtonSettings.BuildingEntity != null && 
                m_SelectedButtonSettings.BuildingEntity.RequiredItems != null)
            {
                if (!HasAllRequiredItems(m_SelectedButtonSettings.BuildingEntity.RequiredItems))
                {
                    return;
                }
            }

            BuildingPlacer.Instance.SelectBuildingPart(m_SelectedButtonSettings.BuildingEntity.GetComponent<BuildingPart>());
            BuildingPlacer.Instance.ChangeBuildMode(BuildingPlacer.BuildMode.PLACE);

            CloseMenu();
        }

I also copied a bit from ‘Recipe.cs’ (our Crafting System) to create a new script I called ‘buildingRecipe.cs’, as follows:

// Similar to the 'RPGBuilderBuildingEntity.cs', this
// is my version of my own Building Recipe

using System;
using System.Collections.Generic;
using RPG.Crafting;
using UnityEngine;

namespace EasyBuildSystem.Features.Runtime.Buildings.Recipe 
{
    [CreateAssetMenu(menuName = "Construction/Recipe", order = 0)]
    public class BuildingRecipe : ScriptableObject, ISerializationCallbackReceiver 
    {
        // Unique ID for recipes
        [SerializeField] string recipeID;
        // Building Ingredients
        [SerializeField] CraftingItem[] ingredients;
        // Resulting Item
        [SerializeField] CraftingItem resultingItem;
        // Level required
        [SerializeField] int levelRequired;
        // XP Reward
        [SerializeField] int XPReward;

        static Dictionary<string, BuildingRecipe> recipeLookupCache = default;

        public static BuildingRecipe GetFromID(string recipeID) 
        {
            if (recipeLookupCache == null) 
            {
                recipeLookupCache = new Dictionary<string, BuildingRecipe>();
                var recipeList = Resources.LoadAll<BuildingRecipe>("Recipes");

                foreach (var buildingRecipe in recipeList) 
                {
                    if (recipeLookupCache.ContainsKey(buildingRecipe.recipeID)) 
                    {
                        Debug.LogError($"Duplicate found: {recipeLookupCache[buildingRecipe.recipeID]} and {buildingRecipe}");
                        continue;
                    }
                    recipeLookupCache[buildingRecipe.recipeID] = buildingRecipe;
                }
            }

            if (string.IsNullOrWhiteSpace(recipeID) || !recipeLookupCache.ContainsKey(recipeID)) 
            {
                return null;
            }

            return recipeLookupCache[recipeID];

        }

        public string GetRecipeID() 
        {
            return recipeID;
        }

        public CraftingItem[] GetIngredients() 
        {
            return ingredients;
        }

        public void OnAfterDeserialize()
        {
            if (string.IsNullOrWhiteSpace(recipeID)) 
            {
                recipeID = Guid.NewGuid().ToString();
            }
        }

        public float GetRequiredLevel() 
        {
            return levelRequired;
        }

        public int GetXPReward()
        {
            return XPReward;
        }

        public void OnBeforeSerialize() 
        {
            // Empty function, needed for Interface to be quiet
        }
    }
}

Where “BuildingEntity.cs” is his equivalent of my “BuildingRecipe.cs” script, but each one of us took a slightly different direction with variable names I suppose

(I figured ‘CraftingItem.cs’ has sufficient information, so I copied a little bit from it, rather than developing a new ‘BuildingItem.cs’ script)

I seek help today for the final step, which is to blend these two scripts into one, so that if my player doesn’t have enough inventory resources in his inventory, he won’t be able to construct whatever the item he’s hovering over (and I’ll also throw in a level checker in the future, but that’s not for now… although I’m sure it’s literally only 3 lines of code at max)

Any other information you might seek? (I’m mainly asking because I can’t find the code in the Crafting System where the crafting operation of consuming stuff and all that happens, so I’m lowkey stuck and confessing that I forgot some stuff)

Seems simple enough. His HasAllRequiredItems function is what you’ll use to check if the player has the required ingredients. It should be the same kinda check we do for crafting

his ‘HasAllRequiredItems’ is a function he created for his RPG Integration. It’s not part of my system unfortunately… We’ll need to create something similar

If it helps, this is his ‘HasAllRequiredItems’ function:

public bool HasAllRequiredItems(RPGBuilderBuildingEntity.RequiredItemData[] requiredItems)
        {
            if (Character.Instance == null)
            {
                return false;
            }

            bool hasEnought = true;

            for (int i = 0; i < requiredItems.Length; i++)
            {
                if (!HasRequiredItem(requiredItems[i]))
                {
                    hasEnought = false;
                }
            }

            return hasEnought;
        }

Last time I tried referring to that integration, the saving system was a nightmare :sweat_smile:

so I guess we have to create something similar…

Yeah, so do it. You already have code that checks if the ingredients for a recipe is in the inventory.

if I remember where that went hiding, or what it was called, I would’ve quietly done it :sweat_smile: (no offence btw, but I honestly don’t remember where that code went hiding to…)

CraftingTable has a CanCraftRecipe() which is accessed by the CraftingSystem.

is it this one? It was in ‘CraftingTable.CraftRecipe()’

// Remove the ingredients from the player's inventory
            var playerInventory = Inventory.GetPlayerInventory();
            foreach (var ingredient in recipe.GetIngredients())
            {
                playerInventory.RemoveItem(ingredient.Item, ingredient.Amount);
            }

I found it in ‘ICraftingTable.CraftRecipe()’

Edit: ahh, nevermind… found the scanner in ‘CanCraftRecipe()’

How do we connect them though, the building recipe and the part to build?

No idea. I don’t know the build system, but the author gave you info on how he did it for the RPG Builder. It would be similar. Do what you need to do to get to that point and then use this method (or at least a copy of it for building) to check if the player has the required ingredients.

At first I thought that each ‘BuildingPart.cs’, the script attached to every buildable part, can be attached to a recipe through a serialized game object, but then idk why it felt like a bad idea…

OK I’m currently giving it a go, but I’m stuck… what was the recipe defined as again?

This is my copy of the ‘CanCraftRecipe’:

// Test: Check if the player can build a building, based on inventory items, or not
        bool CanBuildBuilding(BuildingRecipe recipe) 
        {
            // if recipe is null, return false
            if (recipe == null) return false;

            // if player inventory doesn't exist, return false
            var playerInventory = Inventory.GetPlayerInventory();
            if (playerInventory == null) return false;

            // Check the SkillStore for the level here

            foreach (var buildingCraftingItem in recipe.GetIngredients()) 
            {
                var hasIngredient = playerInventory.HasItem(buildingCraftingItem.Item, out int amountInInventory);

                // if the player does not have the ingredient, return false:
                if (!hasIngredient) return false;
                // if the ingredient quantity is not enough, return false:
                if (amountInInventory < buildingCraftingItem.Amount) return false;
            }

            return true;

        }

        public void SelectBuildingPart(string buildingName)
        {
            if (!CanBuildBuilding()) return;
            
            BuildingPlacer.Instance.SelectBuildingPart(BuildingManager.Instance.GetBuildingPartByName(buildingName));
            BuildingPlacer.Instance.ChangeBuildMode(BuildingPlacer.BuildMode.PLACE);
            CloseMenu();
        }

but the fact that this script is put on the ‘UICircularBuildingMenu.cs’ is what’s weirding me out…

(I’ll continue this tomorrow, I’ll go get some rest otherwise I’ll get into trouble at work)

hey @bixarrio

I’m almost done with the implementation, but I’m stuck somewhere:

the developer has a required item counter, which looks as follows (he has 2 actually, a singular and plural one… I think my plural one should be fine, but will need some more checking):

public bool HasRequiredItem(RPGBuilderBuildingEntity.RequiredItemData requiredItem)
        {
            return RPGBuilderUtilities.getItemCount(requiredItem.Item) >= requiredItem.Amount;
        }

and this is what I’m trying to do (it’s missing the left-hand side):

        public bool HasRequiredItem(CraftingItem getIngredient) 
        {
            return Inventory.GetItemCount(getIngredient as InventoryItem) >= getIngredient.Amount;
        }

Do we have an equivalent item counter like his? Somehow, ‘RPGBuilderUtilities’ has no other references in any other script, I searched everywhere for one, can’t find it… as if that function came out by black magic or something :sweat_smile:


Edit: I changed the code to try and count the items, but… the compiler is giving me errors because I’m trying to convert a CraftingItem to an InventoryItem. Can I get some help about this?

and I still need to change the code for the Editor as well, to make it visible… :sweat_smile:

The InventoryExtensions.cs has a HasItem() that checks if the item is in the inventory and also returns how many of those items there are. It’s the same way we check ingredients in the crafting system

public bool HasRequiredItem(CraftingItem getIngredient) 
{
    return Inventory.HasItem(getIngredient as InventoryItem, out int amount) && amount >= getIngredient.Amount;
}

Is your CraftingItem an InventoryItem? It won’t cast if it’s not

It’s not, so I made it inherit from ‘InventoryItem.cs’, is that possible or will it cause problems?

I’m trying to use this, but I’m not sure of what issues it can yield. Can you give me ideas? :slight_smile: (no syntax errors though…)

public bool HasRequiredItem(CraftingItem getIngredient) 
        {
            return InventoryExtensions.HasItem(Inventory.GetPlayerInventory(), getIngredient, out int amount);
        }

It should be fine, as long as the base class has all the required values filled in

Well, you could have one in your inventory, but require two, and then it will return true, which is wrong. That’s why I added the && amount >= getIngredient.Amount (and you should, too)

and… it’s not. For some reason, if my ‘CraftingItem.cs’ inherits from ‘InventoryItem.cs’ script, my Crafting UI won’t even fire up to begin with…

naturally, as a result, the script I uploaded in my previous post won’t work either

What is a CraftingItem? Why does it exist?

Also, this is not how you use the extensions (although it will work)

public bool HasRequiredItem(CraftingItem getIngredient) 
{
    return Inventory.GetPlayerInventory().HasItem(getIngredient as InventoryItem, out int amount) && amount >= getIngredient.Amount;
}

it exists to define the item and quantity needed for a recipe, right? It’s basically a subclass

Edit: and… here is my second attempt at ‘HasRequiredItem()’:

        public bool HasRequiredItem(CraftingItem getIngredient) 
        {
            InventoryExtensions.HasItem(Inventory.GetPlayerInventory(), getIngredient.Item, out int amount);
            return amount >= getIngredient.Amount;
        }

Is this correct? I won’t be able to identify it for a while, as I need to work on the assets’ editor code as well

Ah yes, I see it in my repo. It’s not what you need to do, though. The CraftingItem holds the ingredient and how many of it you require. So, the code should be

public bool HasRequiredItem(CraftingItem getIngredient) 
{
    return Inventory.GetPlayerInventory().HasItem(getIngredient.Item, out int amount) && amount >= getIngredient.Amount;
}

OK I’ll inject this in my script for now, and as soon as I get a chance to work on the editor code, so I can install recipes to the parts, I will update you on how things went :slight_smile:

It’s not a subclass

It’s good but see my comment about extensions (it won’t make a difference, though)

public bool HasRequiredItem(CraftingItem getIngredient) 
{
    Inventory playerInventory = Inventory.GetPlayerInventory();
    if (!playerInventory.HasItem(getIngredient.Item, out int amount))
    {
        return false;
    }
    return amount >= getIngredient.Amount;
}

Privacy & Terms