Trying To Make Custom Quest Action, Very Confused

For all talk about quest development - creation, testing, and quest system.
Post Reply
User avatar
Magicono43
Posts: 1139
Joined: Tue Nov 06, 2018 7:06 am

Trying To Make Custom Quest Action, Very Confused

Post by Magicono43 »

So the past few days I've been working on Custom quest actions/conditions that I plan on making a mod for eventually. However on this second condition I am getting very lost on why the parameter values are not working as I was expecting.

So this condition is question called "WhenPCRests" is simply meant to track through currently existing events when and for how long the player has Rested. But the system I was using to track the hours is not working as I'd expect. That being the tracked value is only persisting and accurate AFTER the condition has been set, but before it is set everytime the incrementing happens the tracking values return to 0, and I have not much clue why.

I copied nearly the entire structure from the other quest condition "WhenPCEntersExits", which appears to work in a similar manner, but does not have a tracked number values.

Code: Select all

using DaggerfallWorkshop.Game.Entity;
using DaggerfallWorkshop.Game.Questing;
using System.Text.RegularExpressions;
using FullSerializer;
using DaggerfallWorkshop.Game.Serialization;
using DaggerfallWorkshop.Game.Utility;
using DaggerfallWorkshop.Game.UserInterfaceWindows;

namespace QuestMakersToolbox
{
    /// <summary>
    /// Triggers when the player rests (only counts proper sleeping hours, NOT loitering.)
    /// If providing no specific hours, default behavior is to trigger condition as soon as one rest "tick" occurs, or any hour of proper rest basically.
    /// When an hour value followed by 'consecutive hours' will only trigger condition when player has slept those number of hours consecutively in one rest cycle, capped at 99.
    /// When an hour value followed by 'hours' will trigger condition when player has slept those TOTAL number of hours, can span multiple rest cycles.
    /// </summary>
    public class WhenPCRests : ActionTemplate
    {
        int totalRestHoursNeeded;
        int hoursConsecutive;
        int hourTracker;
        int consecutiveTracker;

        // Example match: "trigger if pc rests 8 consecutive hours"
        // Example match: "trigger if pc rests 20 hours"
        // Example match: "trigger if pc rests"
        public override string Pattern
        {
            get
            {
                return @"trigger if pc rests (?<consHours>\d+) consecutive hours|" +
                       @"trigger if pc rests (?<restHours>\d+) hours|" +
                       @"trigger if pc rests";
            }
        }

        public WhenPCRests(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
            WhenPCRests action = new WhenPCRests(parentQuest);
            action.totalRestHoursNeeded = Parser.ParseInt(match.Groups["restHours"].Value);
            action.hoursConsecutive = Parser.ParseInt(match.Groups["consHours"].Value);
            action.hourTracker = 0;
            action.consecutiveTracker = 0;

            if (action.totalRestHoursNeeded < 0)
                action.totalRestHoursNeeded = 0;

            if (action.hoursConsecutive < 0)
                action.hoursConsecutive = 0;
            else if (action.hoursConsecutive > 99)
                action.hoursConsecutive = 99;

            // Register events when creating action
            action.RegisterEvents();

            return action;
        }

        public override bool CheckTrigger(Task caller)
        {
            // Check if current rested hours tracked value is within range to trigger condition
            if (totalRestHoursNeeded <= 0 && hoursConsecutive <= 0 && hourTracker > 0)
                return true;

            if (hoursConsecutive > 0 && consecutiveTracker >= hoursConsecutive)
                return true;

            if (totalRestHoursNeeded > 0 && hourTracker >= totalRestHoursNeeded)
                return true;

            return false;
        }

        public override void RearmAction()
        {
            base.RearmAction();

            hourTracker = 0;
            consecutiveTracker = 0;
        }

        public override void Dispose()
        {
            base.Dispose();

            // Unregister events when quest ends
            UnregisterEvents();
        }

        #region Private Methods

        private void RegisterEvents()
        {
            DaggerfallRestWindow.OnSleepTick += IncreaseHourTracker_OnSleepTick;
            DaggerfallRestWindow.OnSleepEnd += ResetConsecutiveTracker_OnSleepEnd;

            SaveLoadManager.OnStartLoad += SaveLoadManager_OnStartLoad;
            StartGameBehaviour.OnNewGame += StartGameBehaviour_OnNewGame;
        }

        private void UnregisterEvents()
        {
            DaggerfallRestWindow.OnSleepTick -= IncreaseHourTracker_OnSleepTick;
            DaggerfallRestWindow.OnSleepEnd -= ResetConsecutiveTracker_OnSleepEnd;

            SaveLoadManager.OnStartLoad -= SaveLoadManager_OnStartLoad;
            StartGameBehaviour.OnNewGame -= StartGameBehaviour_OnNewGame;
        }

        #endregion

        #region Events Handlers

        private void IncreaseHourTracker_OnSleepTick()
        {
            hourTracker++;
            consecutiveTracker++;
        }

        private void ResetConsecutiveTracker_OnSleepEnd()
        {
            consecutiveTracker = 0; // Could add additional logic to this eventually, like to specify different amounts of completed "rest cycles" or something, but for now it's probably fine.
        }

        private void StartGameBehaviour_OnNewGame()
        {
            UnregisterEvents();
        }

        private void SaveLoadManager_OnStartLoad(DaggerfallWorkshop.Game.Serialization.SaveData_v1 saveData)
        {
            UnregisterEvents();
        }

        #endregion

        #region Serialization

        [fsObject("v1")]
        public struct SaveData_v1
        {
            public int totalRestHoursNeeded;
            public int hoursConsecutive;
            public int hourTracker;
            public int consecutiveTracker;
        }

        public override object GetSaveData()
        {
            SaveData_v1 data = new SaveData_v1();
            data.totalRestHoursNeeded = totalRestHoursNeeded;
            data.hoursConsecutive = hoursConsecutive;
            data.hourTracker = hourTracker;
            data.consecutiveTracker = consecutiveTracker;

            return data;
        }

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

            SaveData_v1 data = (SaveData_v1)dataIn;
            totalRestHoursNeeded = data.totalRestHoursNeeded;
            hoursConsecutive = data.hoursConsecutive;
            hourTracker = data.hourTracker;
            consecutiveTracker = data.consecutiveTracker;

            // Register events when restoring action
            RegisterEvents();
        }

        #endregion
    }
}
Here is the source code so far for the entire project:
DFU-Mod_Quest-Makers-Toolbox.rar
(6.04 KiB) Downloaded 107 times
Here are some of the test quests I made purely for testing these new actions/conditions:
CustActTest1.rar
(3.16 KiB) Downloaded 110 times
Here is a save made for specifically accessing and testing these particular quests if desired.
SAVE142.rar
(192.85 KiB) Downloaded 103 times
But yeah besides all those attached resources, I don't have a clue why these tracked values are continuously resetting each time the event triggers UNLESS the quest condition has already been set. I've tried a few other random attempts to resolve this but none have worked so far and I'm just exhausted and a bit frustrated trying to understand what I have done wrong, thanks for any assistance.

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Trying To Make Custom Quest Action, Very Confused

Post by Interkarma »

Try adding IsAlwaysOnTriggerCondition=true to constructor as well:

Code: Select all

IsTriggerCondition = true;
IsAlwaysOnTriggerCondition = true;
It could be that trigger is unarming/rearming itself - and the rearm is clearing consecutive hours. Just a guess though.

Try adding a few Debug.Log messages in the mix to see when certain things are happening. It could help narrow down when the tracked values are being reset.

User avatar
Magicono43
Posts: 1139
Joined: Tue Nov 06, 2018 7:06 am

Re: Trying To Make Custom Quest Action, Very Confused

Post by Magicono43 »

Interkarma wrote: Wed Aug 10, 2022 6:56 am Try adding IsAlwaysOnTriggerCondition=true to constructor as well:

Code: Select all

IsTriggerCondition = true;
IsAlwaysOnTriggerCondition = true;
It could be that trigger is unarming/rearming itself - and the rearm is clearing consecutive hours. Just a guess though.

Try adding a few Debug.Log messages in the mix to see when certain things are happening. It could help narrow down when the tracked values are being reset.
Hey Interkarma, yeah that was one of the things I tried first and unfortunately the case was still the same even with "IsAlwaysOnTriggerCondition". I've watched the values going through the "OnSleepTick" event with the Visual Studio live debugging, and whenever the condition is not already set the value comes back to the event as 0. I've not put any debug lines though.

It's one of those issues that reading through the code to me it looks totally obvious what the intention is, but when actually running the computer thinks otherwise, lol. (I'm taking a break from the quest actions making today to start work on a spell effect adding mod like I had plans for, debugging this OnRest action was just exhausting last night, lol.)

Edit 1: Something I should try since you mentioned that maybe it's being rearmed over and over, is I should try and disable the rearm override I have, as well as watch that live and see if maybe every update it is rearming or something, even without being explicitly told to "clear" whenever it's not set. I'll have to give it another testing pass later on.

Post Reply