protected virtual void VerifyTrueNodeChildrenAreActive()
{
// Create a copy of the quest list to iterate over safely
var questListCopy = new List<Quest>(questList);
foreach (var quest in questListCopy)
{
// Only process active quests
if (quest.GetState() != QuestState.Active) continue;
var processed = new HashSet<QuestNode>();
foreach (var node in quest.nodeList)
{
if (processed.Contains(node)) continue;
processed.Add(node);
// Check for True nodes
if (node.GetState() == QuestNodeState.True)
{
foreach (var child in node.childList)
{
// Activate inactive children
if (child.GetState() == QuestNodeState.Inactive)
{
child.SetState(QuestNodeState.Active);
}
}
}
}
}
}
Hey, probably you are on holidays but I ll leave some issues here
1) There is a core issue with conditions. I use ORK Variables for offering or not the quests from the quest givers, but it seems that if a player finishes a 1 time quest, if he acts fast and presses back on the quest giver dialogue they can select the quest again, even though the success node of the quest changes the variable of the quest (maybe a timing issue).
To recap: 1 Time quests are being given again to players even though conditions are not met (and they are 1 time quests). Attaching couple of quests
2) Due to this issue, players have some duplicate quests (or 3-4plicate), can we handle those as well on the script you send me above? Thanks and happy holidays!
Does the player do this while in the same dialogue UI window session? In other words, they complete a quest, click the Back button, and the quest appears again in the list of offerable quests?
Or does it appear if the player closes the dialogue UI and immediately interacts with the quest giver again, which reopens the dialogue UI?
Sorry just saw your message, yes it happens while the window is still open, they can press back. If the windows closes then the back button is not there.
Would you please remind me which Quest Machine version you're using? You can find the version number in Plugins > Pixel Crushers > Quest Machine / _README.txt.
In the current version (1.2.50) and most previous versions, when the player clicks the Back button it will recheck the offer conditions before showing the list of available quests. In addition, if it's a one-time quest, then the quest giver should remove the quest from its list as soon as the player accepts it the first time.
If you can reproduce the issue in the Unity editor's play mode, keep an inspector view on the Quest Giver component's Quests list. For example, here's the Villager in Quest Machine's Demo scene:
questList1.png (418.63 KiB) Viewed 11381 times
When I accept the quest:
questList2.png (355.84 KiB) Viewed 11381 times
The Quest Giver removes it from its Quests list since it's a one-time quest:
Oh I see, I use 1.2.48 plus the custom changes you made in this thread, avoding too much updating due to custom code we implemented. Will update and get back to you. Also can we add the duplicate quest fix as well, here is the script I use now:
using System.Collections.Generic;
using UnityEngine;
using ORKFramework;
namespace PixelCrushers.QuestMachine.ORKSupport
{
public class QuestJournalForORKWithVerify : QuestJournalForORK
{
public override void LoadGame(DataObject data)
{
// If the saved game doesn't have QuestJournalForORK data,
// then we know that it's saved through ORKQuestMachineSaveData,
// and we don't need to do anything.
if (data == null)
{
Debug.Log("QuestJournalForORKWithVerify has no saved data. Exiting.");
return;
}
// Ensure questList is initialized
if (questList == null)
{
Debug.LogError("QuestJournalForORKWithVerify: questList is null.");
return;
}
// First, restore the quests.
Debug.Log("QuestJournalForORKWithVerify is restoring the quest journal.");
base.LoadGame(data);
// Make lists of "bad" and completed quests
Debug.Log("QuestJournalForORKWithVerify is making a list of bad quests and completed quests.");
var badQuests = new List<Quest>();
var completedQuests = new List<Quest>();
foreach (var quest in questList)
{
if (quest == null)
{
Debug.LogWarning("QuestJournalForORKWithVerify: Found a null quest in questList.");
continue;
}
if (quest.GetState() == QuestState.Successful)
{
Debug.Log($"QuestJournalForORKWithVerify: quest is completed: {quest.id}");
completedQuests.Add(quest);
}
else if (quest.startNode == null || quest.startNode.GetState() != QuestNodeState.True)
{
Debug.Log($"QuestJournalForORKWithVerify: quest is bad: {quest.id}");
badQuests.Add(quest);
}
}
// Remove completed quests
foreach (var completedQuest in completedQuests)
{
Debug.Log($"QuestJournalForORKWithVerify removing completed quest {completedQuest.id}");
DeleteQuest(completedQuest);
}
// Replace the bad quests with fresh copies
foreach (var badQuest in badQuests)
{
if (badQuest.originalAsset == null)
{
Debug.LogWarning($"QuestJournalForORKWithVerify: Quest {badQuest.id} has a null original asset.");
continue;
}
Debug.Log($"QuestJournalForORKWithVerify adding fresh copy of bad quest {badQuest.id}");
var questAsset = badQuest.originalAsset;
DeleteQuest(badQuest);
var questInstance = questAsset.Clone();
if (questInstance == null)
{
Debug.LogWarning($"QuestJournalForORKWithVerify: Failed to clone quest asset for quest {badQuest.id}.");
continue;
}
var questerTextInfo = new QuestParticipantTextInfo(id, displayName, image, null);
questInstance.AssignQuester(questerTextInfo);
questInstance.timesAccepted = 1;
deletedStaticQuests.Remove(StringField.GetStringValue(questInstance.id));
AddQuest(questInstance);
questInstance.SetState(QuestState.Active);
}
// Refresh the UI to show the updated quest journal
QuestMachineMessages.RefreshUIs(this);
}
}
}
using System.Collections.Generic;
using UnityEngine;
using ORKFramework;
namespace PixelCrushers.QuestMachine.ORKSupport
{
public class QuestJournalForORKWithVerify : QuestJournalForORK
{
public override void LoadGame(DataObject data)
{
// If the saved game doesn't have QuestJournalForORK data,
// then we know that it's saved through ORKQuestMachineSaveData,
// and we don't need to do anything.
if (data == null)
{
Debug.Log("QuestJournalForORKWithVerify has no saved data. Exiting.");
return;
}
// Ensure questList is initialized
if (questList == null)
{
Debug.LogError("QuestJournalForORKWithVerify: questList is null.");
return;
}
// First, restore the quests.
Debug.Log("QuestJournalForORKWithVerify is restoring the quest journal.");
base.LoadGame(data);
// Identify and remove duplicate quests
var firstQuestInstances = new List<Quest>();
var duplicateQuestInstances = new List<Quest>();
foreach (var quest in questList)
{
bool alreadyHasQuest = firstQuestInstances.Find(x => StringField.Equals(x.id, quest.id)) != null;
if (!alreadyHasQuest)
{
firstQuestInstances.Add(quest);
}
else
{
duplicateQuestInstances.Add(quest);
}
}
foreach (var quest in duplicateQuestInstances)
{
Debug.Log($"QuestJournalForORKWithVerify: removing duplicate quest: {quest.id}");
DeleteQuest(quest);
}
// Make lists of "bad" and completed quests
Debug.Log("QuestJournalForORKWithVerify is making a list of bad quests and completed quests.");
var badQuests = new List<Quest>();
var completedQuests = new List<Quest>();
foreach (var quest in questList)
{
if (quest == null)
{
Debug.LogWarning("QuestJournalForORKWithVerify: Found a null quest in questList.");
continue;
}
if (quest.GetState() == QuestState.Successful)
{
Debug.Log($"QuestJournalForORKWithVerify: quest is completed: {quest.id}");
completedQuests.Add(quest);
}
else if (quest.startNode == null || quest.startNode.GetState() != QuestNodeState.True)
{
Debug.Log($"QuestJournalForORKWithVerify: quest is bad: {quest.id}");
badQuests.Add(quest);
}
}
// Remove completed quests
foreach (var completedQuest in completedQuests)
{
Debug.Log($"QuestJournalForORKWithVerify removing completed quest {completedQuest.id}");
DeleteQuest(completedQuest);
}
// Replace the bad quests with fresh copies
foreach (var badQuest in badQuests)
{
if (badQuest.originalAsset == null)
{
Debug.LogWarning($"QuestJournalForORKWithVerify: Quest {badQuest.id} has a null original asset.");
continue;
}
Debug.Log($"QuestJournalForORKWithVerify adding fresh copy of bad quest {badQuest.id}");
var questAsset = badQuest.originalAsset;
DeleteQuest(badQuest);
var questInstance = questAsset.Clone();
if (questInstance == null)
{
Debug.LogWarning($"QuestJournalForORKWithVerify: Failed to clone quest asset for quest {badQuest.id}.");
continue;
}
var questerTextInfo = new QuestParticipantTextInfo(id, displayName, image, null);
questInstance.AssignQuester(questerTextInfo);
questInstance.timesAccepted = 1;
deletedStaticQuests.Remove(StringField.GetStringValue(questInstance.id));
AddQuest(questInstance);
questInstance.SetState(QuestState.Active);
}
// Refresh the UI to show the updated quest journal
QuestMachineMessages.RefreshUIs(this);
}
}
}
So this new script seems to remove duplicates if they haven't been progressed (they are still on first node), others that have been progressed but haven't been delivered (NPC doesnt allow to deliver it after you deliver it once since I m disabling the quest giver to avoid duplicate rewards) are not being removed