Help with custom quest actions

Discuss modding questions and implementation details.
imsobadatnicknames
Posts: 371
Joined: Sun Jun 02, 2019 4:28 pm
Location: Colombia

Help with custom quest actions

Post by imsobadatnicknames »

Hey everybody, I'm trying to create a set of custom quest actions but I think I need help.
I couldn't find any quest actions that measured the PC's health, so I tried to make some myself. Have in mind that I'm a total novice to C# so I copied most of the code from the LevelCompelted action.
This one is supposed to trigger a task when the player's current health is at least a certain value

Code: Select all

using System.Text.RegularExpressions;
using FullSerializer;

namespace DaggerfallWorkshop.Game.Questing
{
    public class WhenHealthLevel : ActionTemplate
    {
        int minHealthValue;

        public override string Pattern
        {
            get { return @"pchealth is at least (?<minHealthValue>\d+)"; }
        }

        public WhenHealthLevel(Quest parentQuest)
            : base(parentQuest)
        {
            IsTriggerCondition = true;
        }

        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);
            if (!match.Success)
                return null;

            // Factory new action
            WhenHealthLevel action = new WhenHealthLevel(parentQuest);
            action.minHealthValue = Parser.ParseInt(match.Groups["minHealthValue"].Value);

            return action;
        }

        public override bool CheckTrigger(Task caller)
        {
            return GameManager.Instance.PlayerEntity.CurrentHealth >= minHealthValue;
        }

        #region Serialization

        [fsObject("v1")]
        public struct SaveData_v1
        {
            public int minHealthValue;
        }

        public override object GetSaveData()
        {
            SaveData_v1 data = new SaveData_v1();
            data.minHealthValue = minHealthValue;

            return data;
        }

        public override void RestoreSaveData(object dataIn)
        {
            if (dataIn == null)
                return;

            SaveData_v1 data = (SaveData_v1)dataIn;
            minHealthValue = data.minHealthValue;
        }

        #endregion
    }
}
This one is pretty much the same except it triggers a task if the player's current health is at least a certain percentage of their max health.

Code: Select all

using System.Text.RegularExpressions;
using FullSerializer;

namespace DaggerfallWorkshop.Game.Questing
{

    public class WhenHealthLevelPercent : ActionTemplate
    {
        int minHealthValue;

        public override string Pattern
        {
            get { return @"pchealthp is at least (?<minHealthValue>\d+)"; }
        }

        public WhenHealthLevelPercent(Quest parentQuest)
            : base(parentQuest)
        {
            IsTriggerCondition = true;
        }

        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);
            if (!match.Success)
                return null;

            // Factory new action
            WhenHealthLevelPercent action = new WhenHealthLevelPercent(parentQuest);
            action.minHealthValue = Parser.ParseInt(match.Groups["minHealthValue"].Value);

            return action;
        }

        public override bool CheckTrigger(Task caller)
        {
            return GameManager.Instance.PlayerEntity.CurrentHealthPercent >= minHealthValue;
        }

        #region Serialization

        [fsObject("v1")]
        public struct SaveData_v1
        {
            public int minHealthValue;
        }

        public override object GetSaveData()
        {
            SaveData_v1 data = new SaveData_v1();
            data.minHealthValue = minHealthValue;

            return data;
        }

        public override void RestoreSaveData(object dataIn)
        {
            if (dataIn == null)
                return;

            SaveData_v1 data = (SaveData_v1)dataIn;
            minHealthValue = data.minHealthValue;
        }

        #endregion
    }
}
I put them in the correct folder and at first I got some console errors in the Unity editor but eventually ironed them out, so
in theory they should be working alright now.

So I wrote a little quest to test them:

Code: Select all

Quest: ACTIONTEST
DisplayName: Action test
-- Message panels

QRC:

Message: 1010
<ce> You have at least 50% health

Message: 1020
<ce> You have at least 50 health

  

QBN:


--	Quest start-up:

_highhealth_ task:
    pchealthp is at least 50
    say 1010

_highhealth2_ task:
    pchealth is at least 50
    say 1020
	
However, when I try to test it using this quest nothing happens, and, checking the quest debugger, neither of the tasks triggers no matter how much health I have. Does anyone know what I might be doing wrong?
Released mods: https://www.nexusmods.com/users/5141135 ... files&BH=0
Daggerfall isn't the only ridiculously intrincate fantasy world simulator with the initials DF that I make mods for: http://www.bay12forums.com/smf/index.php?topic=177071.0

User avatar
BadLuckBurt
Posts: 948
Joined: Sun Nov 05, 2017 8:30 pm

Re: Help with custom quest actions

Post by BadLuckBurt »

You're almost there but the class is missing an Update method, there is a demo quest action in the source files: Assets\Scripts\Game\Questing\Actions\Demo\JuggleAction.cs

You'll see it has an Update method where you can add the logic to make the quest action actually do what you want. In your case you'd probably get the player object, check their health against your minHealthValue and return the result. You may have to call SetComplete() when returning true to stop it from getting stuck in a loop.
DFU on UESP: https://en.uesp.net/w/index.php?title=T ... fall_Unity
DFU Nexus Mods: https://www.nexusmods.com/daggerfallunity
My github repositories with mostly DFU related stuff: https://github.com/BadLuckBurt

.

imsobadatnicknames
Posts: 371
Joined: Sun Jun 02, 2019 4:28 pm
Location: Colombia

Re: Help with custom quest actions

Post by imsobadatnicknames »

Okay! I will try that. I copied the action's structure from the LevelCompleted action, which doesn't seem to have an update method, but I'll try to look at the demo action and add an update method to mine and see if that helps!

EDIT: Looking at a couple more quest actions. None of the ones that trigger something if a player's level/skill/stat/repute is at least x value seem to have an update method. They all seem to do the comparison in "public override bool CheckTrigger(Task caller)"
Maybe the way I'm trying to get the player's health (return GameManager.Instance.PlayerEntity.CurrentHealth >= minHealthValue;) is what's wrong? I'm not really sure. I'm still gonna try adding an update method.
Released mods: https://www.nexusmods.com/users/5141135 ... files&BH=0
Daggerfall isn't the only ridiculously intrincate fantasy world simulator with the initials DF that I make mods for: http://www.bay12forums.com/smf/index.php?topic=177071.0

User avatar
BadLuckBurt
Posts: 948
Joined: Sun Nov 05, 2017 8:30 pm

Re: Help with custom quest actions

Post by BadLuckBurt »

imsobadatnicknames wrote: Thu Aug 18, 2022 4:18 am Okay! I will try that. I copied the action's structure from the LevelCompleted action, which doesn't seem to have an update method, but I'll try to look at the demo action and add an update method to mine and see if that helps!

EDIT: Looking at a couplemore quest actions. None of the ones that trigger something if a player's level/skill/stat/repute is at least x value seem to have an update method. They all seem to do the comparison in "public override bool CheckTrigger(Task caller)"
Yeah, you're right, my bad. I was looking at a more complicated quest action I've played around with in the past. Adding the CheckTrigger method should work too in this case and would be easier.
DFU on UESP: https://en.uesp.net/w/index.php?title=T ... fall_Unity
DFU Nexus Mods: https://www.nexusmods.com/daggerfallunity
My github repositories with mostly DFU related stuff: https://github.com/BadLuckBurt

.

imsobadatnicknames
Posts: 371
Joined: Sun Jun 02, 2019 4:28 pm
Location: Colombia

Re: Help with custom quest actions

Post by imsobadatnicknames »

Problem is, my action already has the CheckTrigger method and it doesn't seem to work still.

Code: Select all

       
        public override bool CheckTrigger(Task caller)
        {
            return GameManager.Instance.PlayerEntity.CurrentHealth >= minHealthValue;
        }
So I think the way I'm trying to access the player's health might be what's wrong but I'm not entirely sure
Released mods: https://www.nexusmods.com/users/5141135 ... files&BH=0
Daggerfall isn't the only ridiculously intrincate fantasy world simulator with the initials DF that I make mods for: http://www.bay12forums.com/smf/index.php?topic=177071.0

User avatar
BadLuckBurt
Posts: 948
Joined: Sun Nov 05, 2017 8:30 pm

Re: Help with custom quest actions

Post by BadLuckBurt »

I think that looks alright. You may have different expectations from that trigger though. I've looked at the other 'when xxx is bla' actions and they only seem to be used to 'arm' a task and then another task checks wether it's armed:

This might work:

Code: Select all

_highhealth_ task:
    pchealthp is at least 50

_highhealth2_ task:
    pchealth is at least 75

_pchealth50_ task:
	when _highhealth_
	say 1010

_pchealth75_ task:
	when _highhealth2_
	say 1020

If not I can take a look later today after I finish some other stuff.
DFU on UESP: https://en.uesp.net/w/index.php?title=T ... fall_Unity
DFU Nexus Mods: https://www.nexusmods.com/daggerfallunity
My github repositories with mostly DFU related stuff: https://github.com/BadLuckBurt

.

imsobadatnicknames
Posts: 371
Joined: Sun Jun 02, 2019 4:28 pm
Location: Colombia

Re: Help with custom quest actions

Post by imsobadatnicknames »

ok, I'll try that and tell you how it goes, thank you!

EDIT: I just tried it and it doesn't seem to be working, the _highhealth_ _highhealth2_ tasks never trigger so the other two don't either. I'm gonna go to bed and keep looking at this when I get off work tomorrow. Thanks for your help!
Released mods: https://www.nexusmods.com/users/5141135 ... files&BH=0
Daggerfall isn't the only ridiculously intrincate fantasy world simulator with the initials DF that I make mods for: http://www.bay12forums.com/smf/index.php?topic=177071.0

User avatar
BadLuckBurt
Posts: 948
Joined: Sun Nov 05, 2017 8:30 pm

Re: Help with custom quest actions

Post by BadLuckBurt »

imsobadatnicknames wrote: Thu Aug 18, 2022 4:50 am ok, I'll try that and tell you how it goes, thank you!

EDIT: I just tried it and it doesn't seem to be working, the _highhealth_ _highhealth2_ tasks never trigger so the other two don't either. I'm gonna go to bed and keep looking at this when I get off work tomorrow. Thanks for your help!
Thanks for reporting back. I added some debug log calls and I think we both expected different values than what we get from CurrentHealth and CurrentHealthPercent.

CurrentHealth returns the current number of hitpoints which in my case meant 35 :? so that never satisfied the condition.
CurrentHealthPercent returns the percentage but it's divided by 100 so you get a 1.0 at 100% and need to multiply first before making the comparison.

I pasted the code I used to test below, in the end it doesn't differ all that much from your original code so I think that should work too if you fix the comparison in your CheckTrigger.

Code: Select all

using UnityEngine;
using System.Text.RegularExpressions;
using FullSerializer;
using DaggerfallWorkshop.Game.Utility;

namespace DaggerfallWorkshop.Game.Questing
{
    public class WhenHealthLevel : ActionTemplate
    {
        float minHealthLevel;

        /// <summary>
        /// Signature is used to match action source and retrieve parameter values.
        /// You need to provide this so action can be tested and factoried as required.
        /// See Regex.Match docs for more: https://msdn.microsoft.com/en-us/library/twcw2f1c(v=vs.110).aspx
        /// Example match: "juggle 5 apples every 2 seconds drop 40%"
        /// </summary>
        public override string Pattern
        {
            get { return @"pchealth is at least (?<minHealthLevel>\d+)"; }
        }

        /// <summary>
        /// Constructor must set parent quest.
        /// </summary>
        /// <param name="parentQuest">Quest this action belongs to. Can be null for template.</param>
        public WhenHealthLevel(Quest parentQuest)
            : base(parentQuest)
        {
            IsTriggerCondition = true;
        }

        public override bool CheckTrigger(Task caller)
        {
            float currentHealth = GameManager.Instance.PlayerEntity.CurrentHealthPercent * 100;
            Debug.Log("BLB: currentHealth level = " + currentHealth.ToString());
            bool result = (currentHealth >= minHealthLevel);
            Debug.Log("BLB: Checking trigger - result = " + result.ToString());
            return result;
        }

        /// <summary>
        /// Create is called when action is factoried during parse time.
        /// Any setup required should be checked and instantiated from here.
        /// If anything prevents action from starting, please throw or log descriptive information.
        /// </summary>
        /// <param name="source">Source line.</param>
        /// <returns>New quest action from this template or null if not created.</returns>
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);
            if (!match.Success)
                return null;

            // Factory new action and set default data as needed
            WhenHealthLevel action = new WhenHealthLevel(parentQuest);
            action.minHealthLevel = float.Parse(match.Groups["minHealthLevel"].Value);
            Debug.Log("BLB: Created action with minHealthLevel = " + action.minHealthLevel.ToString());
            return action;
        }

        /// <summary>
        /// Data to serialize action state can be placed in a struct.
        /// It's a good idea to version struct in case you need to migrate between very different state setups in future.
        /// For basic changes (e.g. just adding a new field with a sensible default) no migration should be needed.
        /// See FullSerializer versioning docs for more: https://github.com/jacobdufault/fullserializer/wiki/Versioning
        /// </summary>
        [fsObject("v1")]
        public struct MySaveData
        {
            public float minHealthLevel;
        }

        /// <summary>
        /// Gets save data for action serialization.
        /// </summary>
        /// <returns>Data packet with action state to save.</returns>
        public override object GetSaveData()
        {
            MySaveData data = new MySaveData();
            data.minHealthLevel = minHealthLevel;
            return data;
        }

        /// <summary>
        /// Restores deserialized state back to action.
        /// </summary>
        /// <param name="dataIn">Data packet with action state to load.</param>
        public override void RestoreSaveData(object dataIn)
        {
            MySaveData data = (MySaveData)dataIn;
            minHealthLevel = data.minHealthLevel;
        }

    }
}
Registering the above quest action and using it with this quest should give you the result you're looking for:

Code: Select all

Quest: BLB01004.TXT
DisplayName: Action test
-- Message panels

QRC:

QuestorOffer: [1000]
<ce>I offer you this quest to test my health level
<ce>Do you accept?

Message: 1010
<ce> You have at least 50% health

Message: 1020
<ce> Quest has started

  

QBN:

    say 1020
--	Quest start-up:

_highhealth_ task:
    pchealth is at least 50

_success_ task:
    when _highhealth_
    say 1010
DFU on UESP: https://en.uesp.net/w/index.php?title=T ... fall_Unity
DFU Nexus Mods: https://www.nexusmods.com/daggerfallunity
My github repositories with mostly DFU related stuff: https://github.com/BadLuckBurt

.

imsobadatnicknames
Posts: 371
Joined: Sun Jun 02, 2019 4:28 pm
Location: Colombia

Re: Help with custom quest actions

Post by imsobadatnicknames »

Thank you for your help! I'll multiply the value in the health percent action so that it's an actual percentage.
Also this is kind of embarrassing, but I think another reason why it wasn't working was because I forgot to register my quest actions. I'm still not sure how to do that tbh, it's my first time making something that needs to be registered.
Released mods: https://www.nexusmods.com/users/5141135 ... files&BH=0
Daggerfall isn't the only ridiculously intrincate fantasy world simulator with the initials DF that I make mods for: http://www.bay12forums.com/smf/index.php?topic=177071.0

User avatar
BadLuckBurt
Posts: 948
Joined: Sun Nov 05, 2017 8:30 pm

Re: Help with custom quest actions

Post by BadLuckBurt »

imsobadatnicknames wrote: Thu Aug 18, 2022 12:07 pm Thank you for your help! I'll multiply the value in the health percent action so that it's an actual percentage.
Also this is kind of embarrassing, but I think another reason why it wasn't working was because I forgot to register my quest actions. I'm still not sure how to do that tbh, it's my first time making something that needs to be registered.
Ah, yes, not registering the quest action will do that :) You can do that from the main class (that initializes your mod).

I have the code below in my main class to register the action:

Code: Select all

        [Invoke(StateManager.StateTypes.Start)]
        public static void InitAtStartState(InitParams initParams)
        {
            mod = initParams.Mod;
            var go = new GameObject(mod.Title);

            Debug.Log("Started setup of : " + mod.Title);
            GameManager.Instance.QuestMachine.RegisterAction(new WhenHealthLevel(null));

            if (!QuestListsManager.RegisterQuestList("BLBQuests"))
                throw new Exception("Quest list name is already in use, unable to register BLBQuests quest list.");

            mod.IsReady = true;
        }
DFU on UESP: https://en.uesp.net/w/index.php?title=T ... fall_Unity
DFU Nexus Mods: https://www.nexusmods.com/daggerfallunity
My github repositories with mostly DFU related stuff: https://github.com/BadLuckBurt

.

Post Reply