Custom Abilities In Game

An extension I was thinking about was to allow the player to purchase upgraded abilities for a higher price.

I have already implemented a UI and I am able to spawn a custom ability type that references the default ability. This custom ability I am using the store the modifications. However I am unable to figure out the next step to update the damage amount in the base ability or the ability name for example.

If anyone has a suggestion I would appreciate it.

I’d need some more details about the route you took to where you’ve gotten so far… Post the relevant scripts and I’ll see what I can come up with.

Thanks Brian. Sorry of the wall of text. I noted what I have tried and what I am planning to try. If you have another route to suggest that would be great!

After further research online it may not be possible to do what I want to without a major refactor of the ability system. I’ve seen you post on other RPG course topic so I am sure you know how we set up the abilities as scriptable objects in the RPG Ability course. What I wrongly thought I could do was create another ability type “Scroll” that reference a regular ability (like the “Splash” we created during the course) as well as a power value. When activating the scroll I would “USE” the ability referencing the base (modified slightly to accept the power value) and pass in the power value.

The SO below is for the scroll. In the PlayerController I created a dictionary that links a base ability to a string code. So when the actionstore uses the scroll, the player controller will find the correct ability in the dictionary and call USE on it.

I believe the problem with this process is that I am unable to create unique versions of the scroll scriptable object. If I put the Scroll in the store, there is no way to update the power level without modifying the SO file which would modify it for all scrolls.

My next thought is to create a duplicate ability SplashCustom. This may allow me to provide a base ability while also giving the option to buy an upgraded version where the player chooses the power level for a higher cost. I was thinking I could have another dictionary that linked the custom version SO and currently purchased power level. Then when SplashCustom is USE is called, I can check the dictionary for what power level they bought. If they ever purchase a new power level I could just update the value.

    [CreateAssetMenu(fileName = "Scroll", menuName = "Abilitites/Scroll")]
    public class Scroll : ActionItem
    {
        [SerializeField] Ability ability;
        PlayerController playerController;
        public int power;

        public override void Use(GameObject user)
        {
            GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>().SetSpell("rf", power);
        }
    }

Hey Brian - I would still be happy to hear if you have other suggestions, but my the next option I was going to try seems like it is going to work. Instead of having a base ability and customer ability though, I decided to just modify the values of the base ability. While this does mean I can only have one ‘splash’ ability, the power level is now coming from the dictionary I setup. Thanks!

I’m not sure that approach would get you where you wanted to go. I have a solution of sorts, but it’s one that will require a bit of modification to the entire Inventory System … (It’s actually not that hard, I’ve done it for several projects).

First, you’ll need to implement ISaveable on InventoryItem, but when you create your CaptureState and RestoreState methods, you’ll make them virtual

public virtual object CaptureState()
{
     return null;
}

public virtual void RestoreState(object state)
{
     return;
}

This not only sets us up for modifications to ablities, but it also sets us up for things like (what I use them for) random or level based modifiers on equipment.

Each Inventory related CaptureState/RestoreState now has to capture an extra element for each inventory item…

Here’s an example from my Inventory.cs:

    [System.Serializable]
    public struct InventorySlotRecord
    {
        public string itemID;
        public int number;
        public int level;
        public object state;
    }

        public object CaptureState()
        {
            var slotStrings = new InventorySlotRecord[inventorySize];
            for (int i = 0; i < inventorySize; i++)
            {
                if (slots[i].item != null)
                {
                    slotStrings[i].itemID = slots[i].item.GetItemID();
                    slotStrings[i].number = slots[i].number;
                    slotStrings[i].level = slots[i].item.Level;
                    slotStrings[i].state = slots[i].item.CaptureState();
                }
            }
           return slotStrings;
        }
       public void RestoreState(object state)
        {
            var slotStrings = (InventorySlotRecord[])state;
            for (int i = 0; i < slotStrings.Length; i++)
            {

                var item = InventoryItem.GetFromId(slotStrings[i].itemID);
                if (item)
                {
                    if (!item.IsStackable())
                    {
                        var newItem = Instantiate(item);
                        item = newItem;
                        item.RestoreState(slotStrings[i].state);
                    }
                    item.Level = slotStrings[i].level;
                    slots[i].item = item;
                    slots[i].number = slotStrings[i].number; 
                }
            }

            InventoryUpdated?.Invoke();
        }

So what’s happening with this is that CaptureState() is using a modified InventorySlotRecord, which stores the item ID, the quantity, the level (in Beneath, the level determines how strong the default stats are) and a state, where bonus attributes are kept.

Any item that is not stackable is instantiated instead of simply referenced. The good news is that when you instantiate an item, it maintains the same ItemID, meaning that since each Basic Sword is it’s own unique instance, it all ties back to the original. All CaptureState has to do is copy the itemID and ask the item for it’s state, which encompass the things that make the item unique.

In RestoreState, if it’s not stackable, then an instance is created, and the information is restored by sending it the state.

Hey Brian. That’s great! Thanks for this. I didn’t even think to try for random items.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms