How to change potion system?

Talk about the mods you'd like to see in Daggerfall Unity. Give mod creators some ideas!
Ommamar
Posts: 541
Joined: Thu Jul 18, 2019 3:08 am

How to change potion system?

Post by Ommamar »

There is a thread about this in general I started to gauge interest in a mod to expand this system which appears to exist. I have my own ideas but would appreciate any thoughts and opinions. So let me know how you would expand on the current system.

Zomgmeister
Posts: 21
Joined: Mon Aug 05, 2019 3:05 pm

Re: How to change potion system?

Post by Zomgmeister »

Frankly I hate potions and probably never used them in Daggerfall. Still have some thoughts.

The most obvious way to make it different is to copy the system of newer TES games. So, each ingredient has certain alchemical properties, and you need to mix at least two ingredients that have equal properties. Because we don't have the Alchemy skill, while we may have it in the future, I won't rely upon it.

So, without skill, the power of concoctions may depend on these factors:

1. Ingredients quantity. Let's say we are doing a Healing Potion. If we are mixing a Duck Bill with a Mole Ass, both of which, obviously, have healing properties, then we'll have a weak potion. But if we'll add third ingredient, Bear Sweat, also with healing power, then the potion will became stronger.

2. Ingredients quality. For example, Duck Bill and Mole Ass may have "Healing, minor" property, while Bear Sweat and Nymph Toe may have "Healing, major". Thus, if we mix Duck Bill with Mole Ass, we get minor Healing Potion. If we mix one of these with one of majors, we'll get moderate Healing Potion. And if we are mixing both majors, then obviously potion will be major.

3. Equipment. I don't think that we need it. Too much hassle and micromanagement.

Consequence of Grace
Posts: 112
Joined: Fri Oct 05, 2018 4:06 am

Re: How to change potion system?

Post by Consequence of Grace »

I am very interested in this. I have mapped out an Alchemist's Guild with various elements but I have no idea how to turn it into a mod.

l3lessed
Posts: 1403
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: How to change potion system?

Post by l3lessed »

Some things to consider digging into this. The original potion recipes are hardcoded into the potion item data in the original engine. So, the potion recipes are first grabbed from the item data in the original engine/data structure.

Code: Select all

    [fsObject("v1")]
    public class ItemData_v1
    {
            public int potionRecipe;
    }
Each potion recipe has a unique int value assigned to it that replaces/corresponds to the original item data recipe hidden inside the engine item data object itself. It then converts it into a usable unity int value.

Code: Select all

            // Convert classic recipes to DFU recipe key.
            if ((IsPotion || IsPotionRecipe) && typeDependentData < MagicAndEffects.PotionRecipe.classicRecipeKeys.Length)
                potionRecipeKey = MagicAndEffects.PotionRecipe.classicRecipeKeys[typeDependentData];
                
From there, the recipe can be called/referenced/used by calling the public PotionRecipeKey and then assigned to any potion for creation. Here is the public recipe int value, under DagerfallUnityItem.cs. Anytime a potion it created, it is done through this daggerfall item script and assigns it the corresponding name. We can see how they use the potion recipe here to create the specific potion itself as an item. I want to point out here, we can also see the individual potions and their recipes in the "PotionRecipes.txt"; Will come back to this at the end when we talk about creating custom potion systems.

Code: Select all

            public override string Potion()
            {   // %po
                string potionName = PotionRecipe.UnknownPowers;
                PotionRecipe potionRecipe = GameManager.Instance.EntityEffectBroker.GetPotionRecipe(parent.potionRecipeKey);
                if (potionRecipe != null)
                    potionName = potionRecipe.DisplayName;

                if (parent.IsPotionRecipe)
                    return potionName;                                          // "Potion recipe for %po"
                else if (parent.IsPotion)
                    return HardStrings.potionOf.Replace("%po", potionName);     // "Potion of %po" (255=Unknown Powers)

                throw new NotImplementedException();
            }
We can see how they use this to create potions on looting or in shops. Here is the code from the looting script.

Code: Select all

        /// <summary>
        /// Randomly add a potion recipe
        /// </summary>
        public static void RandomlyAddPotionRecipe(int chance, ItemCollection collection)
        {
            if (Dice100.SuccessRoll(chance))
            {
                int recipeIdx = Random.Range(0, PotionRecipe.classicRecipeKeys.Length);
                int recipeKey = PotionRecipe.classicRecipeKeys[recipeIdx];
                DaggerfallUnityItem potionRecipe = new DaggerfallUnityItem(ItemGroups.MiscItems, 4) { PotionRecipeKey = recipeKey };
                collection.AddItem(potionRecipe);
            }
        }
Can also see how they use it in the potion creation menu. THIS IS WHAT YOU ARE GOING TO NEED TO STUDY AND USE TO CREATE A CUSTOM POTION MOD:

Code: Select all

void Refresh()
        {
            // Update labels
            goldLabel.Text = GameManager.Instance.PlayerEntity.GetGoldAmount().ToString();

            // Add ingredient items to list and gather recipes - from inventory and wagon
            ingredients.Clear();
            List<DaggerfallUnityItem> recipeItems = new List<DaggerfallUnityItem>();
            foreach (ItemCollection playerItems in new ItemCollection[] { GameManager.Instance.PlayerEntity.Items, GameManager.Instance.PlayerEntity.WagonItems })
            {
                for (int i = 0; i < playerItems.Count; i++)
                {
                    DaggerfallUnityItem item = playerItems.GetItem(i);
                    if (item.IsIngredient && !item.IsEnchanted)
                        ingredients.Add(item);
                    else if (item.IsPotionRecipe)
                        recipeItems.Add(item);
                }
            }
            ingredientsListScroller.Items = ingredients;

            // Clear cauldron and assign to scroller
            cauldron.Clear();
            cauldronListScroller.Items = cauldron;

            // Populate picker from recipe items
            recipes.Clear();
            recipePicker.ListBox.ClearItems();
            foreach (DaggerfallUnityItem recipeItem in recipeItems)
            {
                PotionRecipe potionRecipe = GameManager.Instance.EntityEffectBroker.GetPotionRecipe(recipeItem.PotionRecipeKey);
                if (!recipes.Contains(potionRecipe))
                    recipes.Add(potionRecipe);
            }
            recipes.Sort((x, y) => (x.DisplayName.CompareTo(y.DisplayName)));
            foreach (PotionRecipe potionRecipe in recipes)
                recipePicker.ListBox.AddItem(potionRecipe.DisplayName);
Last edited by l3lessed on Mon Aug 24, 2020 8:06 pm, edited 2 times in total.
My Daggerfall Mod Github: l3lessed DFU Mod Github

My Beth Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 1403
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: How to change potion system?

Post by l3lessed »

Specifically, this block of code here will be of high use to you I think. This is looping through the players inventory, including their wagon, checking if the item is considered a ingredient for a potion and is not enchanted, and if so, adds it to the recipe item list object array.

Code: Select all

      
            // Add ingredient items to list and gather recipes - from inventory and wagon
            ingredients.Clear();
            List<DaggerfallUnityItem> recipeItems = new List<DaggerfallUnityItem>();
            foreach (ItemCollection playerItems in new ItemCollection[] { GameManager.Instance.PlayerEntity.Items, GameManager.Instance.PlayerEntity.WagonItems })
            {
                for (int i = 0; i < playerItems.Count; i++)
                {
                    DaggerfallUnityItem item = playerItems.GetItem(i);
                    if (item.IsIngredient && !item.IsEnchanted)
                        ingredients.Add(item);
                    else if (item.IsPotionRecipe)
                        recipeItems.Add(item);
                }
            }
            ingredientsListScroller.Items = ingredients;
            }
This then loops through the recipeitem list it just created from the players inventory, finds which potions can be made using which ingredients, and then list them in the creation menu for creation trigger to be activated.

Code: Select all

            // Populate picker from recipe items
            recipes.Clear();
            recipePicker.ListBox.ClearItems();
            foreach (DaggerfallUnityItem recipeItem in recipeItems)
            {
                PotionRecipe potionRecipe = GameManager.Instance.EntityEffectBroker.GetPotionRecipe(recipeItem.PotionRecipeKey);
                if (!recipes.Contains(potionRecipe))
                    recipes.Add(potionRecipe);
            }
            recipes.Sort((x, y) => (x.DisplayName.CompareTo(y.DisplayName)));
            foreach (PotionRecipe potionRecipe in recipes)
                recipePicker.ListBox.AddItem(potionRecipe.DisplayName);
        }
I recommend also digging into the DaggerfallPotionMakerWindow.cs script to get an idea how the objects work to create potions from individual recipes.

I'm not sure how much can be accessed and modded also. From first glance, it looks like you need to first grab the recipes and then see if you can change them by adding more requirements
My Daggerfall Mod Github: l3lessed DFU Mod Github

My Beth Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 1403
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: How to change potion system?

Post by l3lessed »

Also, once last note. The actual effects are of course managed and applied using the entityeffect objects built into DFU.

Here is the code for creating your own custom registered effects within the effect system. You must use this effect register and all its supporting objects in the EntityEffectBroker.cs script to get effects registered into the DFU engine, tied to an item or spell, and cast/managed once player activates said item/effect.

This is the object itself. You would reference this object using a create BaseEntityEffect object, and tell it within the bool trigger "true" to replace whatever default potion recipe and effect you are creating replacing.

Code: Select all

/// <summary>
        /// Register new effect template with broker.
        /// Also maps classic key and potion recipes defined by effect.
        /// </summary>
        /// <param name="effect">New effect to register.</param>
        /// <param name="allowReplacement">Allow replacement of existing effect with this key.</param>
        /// <returns>True if successful.</returns>
        public bool RegisterEffectTemplate(BaseEntityEffect effect, bool allowReplacement = false)
        {
            // Template cannot be null or have a null or empty key
            if (effect == null || string.IsNullOrEmpty(effect.Key))
            {
                Debug.LogError("RegisterEffect: Either template is null or has a null or empty key value.");
                return false;
            }

            // Check for existing template with this key
            if (magicEffectTemplates.ContainsKey(effect.Key))
            {
                if (allowReplacement)
                {
                    magicEffectTemplates.Remove(effect.Key);
                }
                else
                {
                    Debug.LogErrorFormat("RegisterEffect: Template key '{0}' already exists. Use allowReplacement=true to replace this effect.", effect.Key);
                    return false;
                }
            }

            // Register effect template
            if (effect.VariantCount > 1)
            {
                // Store one template per variant for multi-effects
                for (int i = 0; i < effect.VariantCount; i++)
                {
                    BaseEntityEffect variantEffect = CloneEffect(effect) as BaseEntityEffect;
                    variantEffect.CurrentVariant = i;
                    magicEffectTemplates.Add(variantEffect.Key, variantEffect);
                    IndexEffectRecipes(variantEffect, allowReplacement);
                    MapClassicKey(variantEffect, allowReplacement);
                }
            }
            else
            {
                // Just store singleton effect
                magicEffectTemplates.Add(effect.Key, effect);
                IndexEffectRecipes(effect, allowReplacement);
                MapClassicKey(effect, allowReplacement);
            }

            return true;
        }
On top of that, you also have the below objects. These objects combined with the effect manager are what you will use to pull the recipes, manipulate them, and reassign the new recipes and effects. As an example, you can get a recipe using the below " public PotionRecipe GetEffectPotionRecipe(IEntityEffect effect, int variant = 0)" object to pull a potions recipe.

Code: Select all

        
        /// <summary>
        /// Gets number of potion properties assigned to this effect.
        /// Effect must allow the potion maker crafting station and define potion recipes.
        /// Effect 
        /// </summary>
        /// <param name="effect">Input effect.</param>
        /// <returns>Number of recipes for this effect.</returns>
        public int GetEffectPotionRecipeCount(IEntityEffect effect)
        {...}
        
         /// <summary>
        /// Gets PotionRecipe from IEntityEffect.
        /// Effect must allow the potion maker crafting station and define potion recipes.
        /// </summary>
        /// <param name="effect">Input effect.</param>
        /// <param name="variant">Variant index, if more than one exists.</param>
        /// <returns>PotionRecipe if the effect has one, otherwise null.</returns>
        public PotionRecipe GetEffectPotionRecipe(IEntityEffect effect, int variant = 0)
        {...}
        
        /// <summary>
        /// Gets IEntityEffect from PotionRecipe.
        /// </summary>
        /// <param name="recipe">Input recipe.</param>
        /// <returns>IEntityEffect if this recipe is linked to an effect, otherwise null.</returns>
        public IEntityEffect GetPotionRecipeEffect(PotionRecipe recipe)
        {...}
        
         /// <summary>
        /// Gets PotionRecipe from effect that matches the recipeKey provided.
        /// </summary>
        /// <param name="recipeKey">Hashcode of a set of ingredients.</param>
        /// <returns>PotionRecipe if the key matches one from an effect, otherwise null.</returns>
        public PotionRecipe GetPotionRecipe(int recipeKey)
        {...}
        
         /// <summary>
        /// Get recipeKeys for all registered potion recipes.
        /// </summary>
        /// <returns>List of int recipeKeys</returns>
        public List<int> GetPotionRecipeKeys()
        {...}
        
         /// <summary>
        /// Logs a summary of how many recipes ingredients are used in so new recipes can choose to use little used ingredients.
        /// Intended for mod devs, used by invoking 'ingredUsage' console command.
        /// </summary>
        public void LogRecipeIngredientUsage()
        {...}
        
Lastly, you can reference and manually change the recipes using the text file they built exactly for this. PotionRecipes.txt. This is where I think you could also register new potions with custom recipes, just like we do when we want to create a custom item using the itemtemplate.txt file.

So, you have to decide how you even want your potion system first because of how many parts there are.

If you want to recreate a Skyrim based system, with ingredients having unique strengths for certain effects, you will have to rebuild most the system I believe. You will need to create some form of data file that stores each ingredients specific property data to reference since this is not built into the base engine in anyway. Then, when creating potions, you need to reference that data sheet and have it dynamically alter the effects/strengths of the potions. This doesn't include the issue of figuring out how to get around the original engine/item only having one very specific recipe. Skyrims system you can create a healing potion using a multitude of ingredients/recipes because it was built around the concept of ingredients having base properties. This system there is only one way/recipe to create any potion, so you would have to build a data base of differing possible health potions using the differing ingredients with healing properties. I can keep going, but going to stop here and let whoever wants to work on this take it up.

Have fun/ :twisted:
My Daggerfall Mod Github: l3lessed DFU Mod Github

My Beth Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

User avatar
Hazelnut
Posts: 3016
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: How to change potion system?

Post by Hazelnut »

I think the only mod that's done anything with potions is my Archaeologists guild which adds a new potion to the game - Teleport. It uses an existing effect though. Take a look at TeleportPotion.cs on my github.

Roleplay & Realism does change the CureDisease effect of Purification potions so they cure poison rather than grant invisibility. See CureDiseaseRR.cs on my github.

Hope having real examples helps in some way, in addition to the great info Blessed just posted for you. As he said, good luck. :)
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

Consequence of Grace
Posts: 112
Joined: Fri Oct 05, 2018 4:06 am

Re: How to change potion system?

Post by Consequence of Grace »

Crikey, thanks so much for all that.

It will take me quite some time to work all this out. So far, I just have the guild ranks and benefits of membership mapped out in Excel, and all the known ingredients and known vanilla potions and recipes. And I have the text of some books to put in the library.

Most of the ingredients do not have an effect that is used in potions, and about 1/4 to 1/3 of ingredients are not used at all. There are about eight or nine that have Resist Fire as a reagent effect and most of the time the ingredients bear little relationship to the potion effect.

I did plan on making a Teleport potion and have a list of all the spell effects and which ones have a corresponding potion in Vanilla. I then wanted to make new recipes for new potions that could be researched, to correspond to spell effects that have no potion equivalent.

I'll have to do this piecemeal. Maybe start with the reagents and potions, and then see how the Alchemist Guild can work.


Thank you so much.


:D

User avatar
Hazelnut
Posts: 3016
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: How to change potion system?

Post by Hazelnut »

Feel free to send me your guild design and I'll review it for any things that may trip you up. For the teleport potion I think I tried to use ingredients that were not used for any recipes. Pretty sure I did some analysis, but it could use a complete overhaul really. This page on the uesp may give a good starting point for ingredients and effects etc: https://en.uesp.net/wiki/Daggerfall:Ingredients

I'd be happy to disable the Archs Tele potion if your guild is installed and has one. Note that you can select the appearance of the potions using the texture record as I did with the Teleport one in my mod.

Good luck with this, sounds really good.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

Consequence of Grace
Posts: 112
Joined: Fri Oct 05, 2018 4:06 am

Re: How to change potion system?

Post by Consequence of Grace »

Thanks again.

I have all the ingredients in a spreadsheet, but it was still worth looking at that Wiki page!

One of the ranks at the Alchemists Guild will gain a bonus to Harvest Reagents, so it's interesting how many monsters never drop them. I was thinking of increasing the odds of a suitable drop when killing a monster, but it looks like I will have to add the chance of a drop, say 50% +5% per Guild Rank from when that perk kicks in.

"There are numerous enemies which never or almost never carry ingredients:"

Animals (Giant Bat, Giant Scorpion, Grizzly Bear, Rat, Sabertooth Tiger, Slaughterfish, Spider)
Atronachs
Daedroths
Dragonlings
Elemental Daedra (Fire, Frost)
Gargoyles
Ghosts
Lycanthropes (Wereboar, Werewolf)
Orcs (Normal, Sergeant, Warlord)
Skeleton Warriors
Warrior class NPCs
Wraiths

I mean, there are so many ingredients that should drop from most of those! If you kill a Giant Scorpion or a Wraith, you should have a decent chance of harvesting a Sting or Wraith Essence!

:shock:

Post Reply