Page 2 of 2

Re: Checking for specific date

Posted: Tue Feb 16, 2021 4:21 pm
by haloterm
Hazelnut wrote: Mon Feb 15, 2021 6:32 pm yes that's correct
Can I simply have the .cs file in my mod's "script" folder ("blackhorsecourier/scripts") or do I need to adher to the folder structure of DFU (like "blackhorsecourier/scripts/game/questing/actions")?

Re: Checking for specific date

Posted: Tue Feb 16, 2021 7:31 pm
by Hazelnut
You can have it wherever you like since you register it with DFU it doesn't need to go looking.

Re: Checking for specific date

Posted: Wed Feb 17, 2021 1:57 pm
by haloterm
Hazelnut wrote: Tue Feb 16, 2021 7:31 pm You can have it wherever you like since you register it with DFU it doesn't need to go looking.
Hm... I am not sure how to do that correctly.

I have the following content of my action (posting it here, so maybe someone who knows what he's doing can tell me if that's basically correct; it should simply allow "yearly from day.month. to day.month." such as "yearly from 28.8. to 31.8."):

Code: Select all

// custom quest action for Black Horse Courier
//
// this creates a new quest action which checks if we are in a given range of days
//
// _Example_ task:
//     when yearly from 15.1. to 4.5.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using DaggerfallWorkshop.Utility;
using FullSerializer;

namespace DaggerfallWorkshop.Game.Questing
{
    /// <summary>
    /// Raise or lower task state based on month
    /// Date must be in day1.month1. to day2.month2. (like "1.8. to 15.8." for August 8th to 15th)
    /// </summary>
    public class YearlyFrom : ActionTemplate
    {
        int day1;
        int day2;
        int month1;
        int month2;

        public override string Pattern
        {
            get { return @"yearly from (?<day1>\d+).(?<month1>\d+). to (?<day2>\d+).(?<month2>\d+)."; }
        }

        public YearlyFrom(Quest parentQuest)
            : base(parentQuest)
        {
            IsTriggerCondition = true;
            IsAlwaysOnTriggerCondition = 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
            YearlyFrom action = new YearlyFrom(parentQuest);
            action.day1 = Parser.ParseInt(match.Groups["day1"].Value);
            action.day2 = Parser.ParseInt(match.Groups["day2"].Value);
            action.month1 = Parser.ParseInt(match.Groups["month1"].Value);
            action.month2 = Parser.ParseInt(match.Groups["month2"].Value);

            return action;
        }

        public override bool CheckTrigger(Task caller)
        {
            // Get live world time (day 1 to 30, month 1 to 12)
            DaggerfallDateTime worldTime = DaggerfallUnity.Instance.WorldTime.Now;
            int currentDay = worldTime.day;
            int currentMonth = worldTime.month;


            // Check if inside range
            if ((currentDay >= day1 && currentDay <= day2)  &&  (currentMonth >= month1 && currentMonth <= month2))
                return true;
            else
                return false;
        }


        #region Serialization

        [fsObject("v1")]
        public struct SaveData_v1
        {
            public int day1;
            public int day2;
            public int month1;
            public int month2;

        }

        public override object GetSaveData()
        {
            SaveData_v1 data = new SaveData_v1();
            data.day1 = day1;
            data.day2 = day2;
            data.month1 = month1;
            data.month2 = month2;

            return data;
        }

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

            SaveData_v1 data = (SaveData_v1)dataIn;
            day1 = data.day1;
            day2 = data.day2;
            month1 = data.month1;
            month2 = data.month2;
        }

        #endregion
    }
}
And then I have to put it somewhere with RegisterAction in my BlackHorseCourier.cs file:

Code: Select all

// Black Horse Courier

using System;
using System.Collections.Generic;
using UnityEngine;
using DaggerfallConnect;
using DaggerfallConnect.Arena2;
using DaggerfallConnect.FallExe;
using DaggerfallWorkshop;
using DaggerfallWorkshop.Game;
using DaggerfallWorkshop.Game.Banking;
using DaggerfallWorkshop.Game.Guilds;
using DaggerfallWorkshop.Game.Questing;
using DaggerfallWorkshop.Game.Entity;
using DaggerfallWorkshop.Game.Formulas;
using DaggerfallWorkshop.Game.Items;
using DaggerfallWorkshop.Game.MagicAndEffects;
using DaggerfallWorkshop.Game.UserInterface;
using DaggerfallWorkshop.Game.UserInterfaceWindows;
using DaggerfallWorkshop.Game.Utility.ModSupport;
using DaggerfallWorkshop.Game.Utility.ModSupport.ModSettings;
using DaggerfallWorkshop.Utility;

namespace BlackHorseCourier
{
    public class BlackHorseCourier : MonoBehaviour
    {

        protected static string[] placesTable =
        {
            "TuluneCity,            0x435d, 1, -1",
            "CredenceHall,          0xc544, 1, -1",
            "Camelford,             0xc545, 1, -1",
            "Elizay,                0xc546, 1, -1",
            "OldLysoldasFarm,       0x3FA7, 1, -1",
            "PilgrimKynareth,       0x2E9E, 1, -1",
            "PilgrimMara,           0x38F2, 1, -1",
            "PilgrimDibella,        0x4FF4, 1, -1",
            "PilgrimAkatosh,        0x57B7, 1, -1",
            "PilgrimZenithar,       0x6582, 1, -1",
            "PilgrimArkay,          0x6681, 1, -1",
            "PilgrimStendarr,       0x67AF, 1, -1",
            "PilgrimJulianos,       0x72DC, 1, -1",
        };


        static Mod mod;

        [Invoke(StateManager.StateTypes.Start, 0)]
        public static void Init(InitParams initParams)
        {
            mod = initParams.Mod;
            var go = new GameObject(mod.Title);
            go.AddComponent<BlackHorseCourier>();
        }


        public static void InitMod()
        {
            Debug.Log("Begin mod init: BlackHorseCourier");

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

            // Add additional data into the quest machine for the quests
            Debug.Log("Adding new BlackHorseCourier locations into quest places table");
            QuestMachine questMachine = GameManager.Instance.QuestMachine;
            questMachine.PlacesTable.AddIntoTable(placesTable);

            Debug.Log("Finished mod init: BlackHorseCourier");
        }


        void Awake()
        {
            //var settings = mod.GetSettings();
            // ---

            InitMod();

            mod.IsReady = true;
        }

    }
}
Intuitively, I would do that simply after I added my quests to the questmachine, but how do I tell RegisterAction to use my custom action?

I would write

// Register new quest action DaysInYear
questMachine.RegisterAction( SOMETHING )

but what should that "SOMETHING" be?

It seems RegisterAction requires a parameter of the type "IQuestAction", but I have no idea how to relate that to my custom action.

Sorry for these dumb questions, but I am no programmer, I can just programming in non-object oriented FreePascal, and I just want to write a simple quest that checks for a given month period -.- So I really have no idea what I am doing here. More help (or simply the required code lines) would be appreciated.

Re: Checking for specific date

Posted: Wed Feb 17, 2021 3:21 pm
by TheLacus
Hi, this is how to register a new action:

Code: Select all

questMachine.RegisterAction(new YearlyFrom(null));
While not strictly required, I'd also suggest to put all your mod code inside namespace BlackHorseCourier, inlcuding custom action.

Re: Checking for specific date

Posted: Wed Feb 17, 2021 5:00 pm
by haloterm
TheLacus wrote: Wed Feb 17, 2021 3:21 pm Hi, this is how to register a new action:

Code: Select all

questMachine.RegisterAction(new YearlyFrom(null));
While not strictly required, I'd also suggest to put all your mod code inside namespace BlackHorseCourier, inlcuding custom action.
Oh. That is easy :) Thank you.

I will also follow your namespace suggestion.

Re: Checking for specific date

Posted: Wed Feb 17, 2021 5:44 pm
by haloterm
Hmm.... after correcting a few typos (day instead of Day, month instead of Month) the scripts compile fine (checked in Unity editor with "precompile" option in mod builder).

However, it does not seem to have an effect while ingame. In the player.log I get

Unknown line signature encounted 'yearly from 1.1. to 30.12.'.

so it apparently does not recognize my action as valid.

(I use it in a test quest at the same place where usually one could use the "daily" action instead).

So either my action did not register successfully, or my pattern matching is wrong:

Code: Select all

public override string Pattern
        {
            get { return @"yearly from (?<day1>\d+).(?<month1>\d+). to (?<day2>\d+).(?<month2>\d+)."; }
        }

Re: Checking for specific date

Posted: Wed Feb 17, 2021 5:53 pm
by Hazelnut
Dots are a special regex char that mean match any character, so they will need backspaces I think. It might be easier to use dashes as date component separators.

for example:

Code: Select all

        public override string Pattern
        {
            get { return @"yearly from (?<day1>\d+)-(?<month1>\d+) to (?<day2>\d+)-(?<month2>\d+)"; }
        }
should match "yearly from 1-1 to 30-12"