Weird Issue with Quest Completion

So I just finished the Complex Conditions portion of the quest section, and whenever I pick up the quest from the questgiver, it’s automatically showing me the “complete quest” dialogue node. I can then complete the quest over and over again.

There has to be something I missed, but I can’t see any differences in code, nor can I figure something out in the editor.

Might need a wee bit more info… Conditions are probably the hardest things to debug in our system.
Show a screenshot of your Complete Quest Dialogue Node, and paste in your Condition.cs script, and we’ll go from there.

1 Like

so it’s showing the equivalent of the “I have the foot cream” node, while also showing the “pick up the quest” node if that makes sense.

This is the player response node for the “I have the item to complete the quest (or what I called the “complete quest”) node”

and this is the condition script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Sanctuary.Harry.Core
{
    [System.Serializable]
    public class Condition
    {
        [SerializeField]
        Disjunction[] and;

        public bool Check(IEnumerable<IPredicateEvaluator> evaluators)
        {
            foreach (Disjunction dis in and)
            {
                if (!dis.Check(evaluators)) { return false; }
            }
            return true;
        }

        [System.Serializable]
        class Disjunction
        {
            [SerializeField] Predicate[] or;

            public bool Check(IEnumerable<IPredicateEvaluator> evaluators)
            {
                foreach (Predicate pred in or)
                {
                    if (pred.Check(evaluators)) { return true; }
                }
                return false;
            }
        }

        [System.Serializable]
        class Predicate
        {
            [SerializeField] string predicate;
            [SerializeField] string[] parameters;
            [SerializeField] bool negate = false;

            public bool Check(IEnumerable<IPredicateEvaluator> evaluators)
            {
                foreach (var evaluator in evaluators)
                {
                    bool? result = evaluator.Evaluate(predicate, parameters);

                    if (result == null) { continue; }

                    if (result == negate) return false;
                }
                return true;
            }
        }
    }
}

This is functionally equivalent to:

if(HasQuest("Sandbox Quest") && (CompletedQuest("Sandbox Quest" || HasInventoryItem(whetstone))

So we know at this point that the first condition is true (HasQuest"SandBoxQuest") so we can reduce the condition to CompletedQuest("Sandbox Quest" || HasInventoryItem(whetstone). The first of these conditions is unlikely to be true, but the second one could be… That being said, it’s best to look at the IPredicateEvaluator for both QuestList.cs and InventoryItem.cs.

So the QuestList.cs evaluate is…

public bool? Evaluate(string predicate, string[] parameters)
        {
            if (predicate != "HasQuest") return null;

            switch (predicate)
            {
                case "HasQuest":
                    return HasQuest(Quest.GetByName(parameters[0]));
                case "CompletedQuest":
                    return GetQuestStatus(Quest.GetByName(parameters[0])).IsComplete();
            }


            return null;
        }

and the Inventory.cs is…

public bool? Evaluate(string predicate, string[] parameters)
        {
            switch (predicate)
            {
                case "HasInventoryItem":
                    return HasItem(InventoryItem.GetFromID(parameters[0]));
            }
            return null;
        }

I’m starting without ANYTHING in my inventory, so it definitely doesn’t have anything in there.

So far, so good, we follow the rabbit trail up the chain…

Let’s check these via some debugs:

                case "CompletedQuest":
                    Debug.Log($"CompletedQuest {parameters[0]) = {GetQuestStatus(Quest.GetByName(parameters[0])).IsComplete()}"
                    return GetQuestStatus(Quest.GetByName(parameters[0])).IsComplete();

                case "HasInventoryItem":
                    Debug.Log($"HasInventoryItem {parameters[0]} = {HasItem(InventoryItem.GetFromID(parameters[0]))}";
                    return HasItem(InventoryItem.GetFromID(parameters[0]));

It’s not showing ANY of the debug logs. In fact, Not only is it not doing the debug logs, I just completed it twice, and the objective counter went up to 2/1 lol

Add a Debug to the HasQuest condition

Okay that’s going through. I am just unsure what I’m supposed to be looking for. I think I might have messed something up with Negate somehow?

HasQuest Sandbox Quest = True
UnityEngine.Debug:Log (object)
Sanctuary.Harry.Quests.QuestList:Evaluate (string,string[]) (at Assets/Harry/Scripts/Quests/QuestList.cs:138)
Sanctuary.Harry.Core.Condition/Predicate:Check (System.Collections.Generic.IEnumerable`1<Sanctuary.Harry.Core.IPredicateEvaluator>) (at Assets/Harry/Scripts/Core/Condition.cs:48)
Sanctuary.Harry.Core.Condition/Disjunction:Check (System.Collections.Generic.IEnumerable`1<Sanctuary.Harry.Core.IPredicateEvaluator>) (at Assets/Harry/Scripts/Core/Condition.cs:31)
Sanctuary.Harry.Core.Condition:Check (System.Collections.Generic.IEnumerable`1<Sanctuary.Harry.Core.IPredicateEvaluator>) (at Assets/Harry/Scripts/Core/Condition.cs:17)
DialogueNode:CheckConditon (System.Collections.Generic.IEnumerable`1<Sanctuary.Harry.Core.IPredicateEvaluator>) (at Assets/Harry/Scripts/Dialogue/DialogueNode.cs:83)
Sanctuary.Harry.Dialogue.PlayerConversant/<FilterOnCondition>d__21:MoveNext () (at Assets/Harry/Scripts/Dialogue/PlayerConversant.cs:147)
System.Linq.Enumerable:Count<DialogueNode> (System.Collections.Generic.IEnumerable`1<DialogueNode>)
Sanctuary.Harry.Dialogue.PlayerConversant:HasNext () (at Assets/Harry/Scripts/Dialogue/PlayerConversant.cs:128)
Sanctuary.Harry.UI.DialogueUI:UpdateUI () (at Assets/Harry/Scripts/UI/DialogueUI.cs:53)
Sanctuary.Harry.Dialogue.PlayerConversant:StartDialogue () (at Assets/Harry/Scripts/Dialogue/PlayerConversant.cs:61)
Sanctuary.Harry.Dialogue.PlayerConversant:Update () (at Assets/Harry/Scripts/Dialogue/PlayerConversant.cs:51)

I’m going through your Condition.cs line by line, trying to spot a difference… it looks like the if(HasQuest), being true, is letting the condition pass, but it shoudln’t until the second (if CompledQuest || HasInventoryItem) also tests true.

Try making this change:

        class Predicate
        {
            [SerializeField] string predicate;
            [SerializeField] string[] parameters;
            [SerializeField] bool negate = false;

            public bool Check(IEnumerable<IPredicateEvaluator> evaluators)
            {
                Debug.Log($"Testing Predicate {predicate}({parameters[0]}");
                foreach (var evaluator in evaluators)
                {
                    bool? result = evaluator.Evaluate(predicate, parameters);

                    if (result == null) { continue; }

                    if (result == negate) 
                       {
                            Debug.Log($"result = {result}, but expecting {!negate} returning false");
                            return false;
                }
                return true;
            }
        }

image

That’s what I’m getting

That might make more sense if you turn off collapse

yeah that makes sense haha

Do you have an IPredicateEvaluator interface on your Inventory?

Actually, that doesn’t matter, because the Predicate Debug isn’t even listing it

And it wouldn’t, if CompletedQuest is true… so that’s the million dollar question… Why is CompletedQuest returning true?

The IsComplete function matches in the QuestStatus.cs, so it shouldn’t be having an issue with that.

So QuestList.Evaluate() is never evaluating CompletedQuest… which makes me wonder if something else is…

            public bool Check(IEnumerable<IPredicateEvaluator> evaluators)
            {
                Debug.Log($"Testing Predicate {predicate}({parameters[0]}");
                foreach (var evaluator in evaluators)
                {
                    Debug.Log($"{evaluator} is evaluating {predicate}");
                    bool? result = evaluator.Evaluate(predicate, parameters);

                    if (result == null) 
                   { 
                          Debug.Log($"{evaluator} cannot handle {predicate}");
                          continue;
                    }

                    if (result == negate) 
                       {
                            Debug.Log($"result = {result}, but expecting {!negate} returning false");
                            return false;
                }
                return true;
            }

so it’s a long one haha.

Privacy & Terms