I'm working on a custom random script generator for my project. I've made good progress but am encountering two issues:
I need to create a counter with the value mode set to "message" that I can modify.
I want to add this counter to the condition node, but despite including it in my code, it does not appear.
My goal is to have a player interact with a quest giver, who then assigns a random quest to the player. Below is my current code implementation:
Code: Select all
using UnityEngine;
using System.Collections.Generic;
using PixelCrushers.QuestMachine;
using PixelCrushers;
public class RandomQuestCreator : MonoBehaviour
{
[Header("Item Pool")]
public List<Item> possibleItems;
[Header("Quantity Range")]
public int minAmount = 1;
public int maxAmount = 10;
[Header("Reward Settings")]
public int coinReward = 50;
QuestGiver questGiver;
private void Awake()
{
questGiver = GetComponent<QuestGiver>();
if (questGiver == null)
{
Debug.LogWarning("RandomQuestCreator: QuestGiver component not found on this GameObject.");
}
}
public void AssignRandomCollectQuest()
{
Debug.Log("AssignRandomCollectQuest: Started.");
if (questGiver == null)
{
Debug.LogWarning("No QuestGiver assigned!");
return;
}
if (questGiver.questList == null)
{
Debug.LogWarning("QuestGiver.questList is null!");
return;
}
if (questGiver.questList.Count > 0)
{
Debug.Log("QuestGiver already has a quest. Starting dialogue.");
questGiver.StartDialogueWithPlayer();
return;
}
if (possibleItems == null || possibleItems.Count == 0)
{
Debug.LogWarning("No items assigned!");
return;
}
// Pick random item & quantity
Item randomItem = possibleItems[Random.Range(0, possibleItems.Count)];
if (randomItem == null)
{
Debug.LogWarning("RandomQuestCreator: Selected randomItem is null!");
return;
}
int requiredAmount = Random.Range(minAmount, maxAmount + 1);
Debug.Log($"RandomQuestCreator: Selected item {randomItem.itemName}, requiredAmount = {requiredAmount}");
// Create a new Quest using ScriptableObject.CreateInstance
Quest newQuest = ScriptableObject.CreateInstance<Quest>();
newQuest.isInstance = true;
newQuest.originalAsset = null;
// IMPORTANT: Set the ID BEFORE initializing the quest!
newQuest.id = new StringField(System.Guid.NewGuid().ToString());
Debug.Log("RandomQuestCreator: Setting Quest ID to " + newQuest.id.value);
newQuest.Initialize();
// Check that the quest was initialized correctly:
if (newQuest.nodeList == null || newQuest.nodeList.Count == 0)
{
Debug.LogError("RandomQuestCreator: Quest.Initialize() did not create any nodes!");
return;
}
// Assign Title
newQuest.title = new StringField($"Collect {requiredAmount} x {randomItem.itemName}");
// --- Offer Text ---
HeadingTextQuestContent offerTitle = ScriptableObject.CreateInstance<HeadingTextQuestContent>();
BodyTextQuestContent offerText = ScriptableObject.CreateInstance<BodyTextQuestContent>();
offerTitle.useQuestTitle = true;
offerText.bodyText = new StringField(
$"Hey! I need you to collect {requiredAmount} x {randomItem.itemName} for me. " +
$"I'll pay you {coinReward} coins. What do you say?"
);
newQuest.offerContentList.Add(offerTitle);
newQuest.offerContentList.Add(offerText);
// --- Journal & HUD Descriptions ---
var activeStateInfo = newQuest.GetStateInfo(QuestState.Active);
if (activeStateInfo == null)
{
Debug.LogWarning("RandomQuestCreator: Active state info is null.");
}
else
{
var journalTitle = ScriptableObject.CreateInstance<HeadingTextQuestContent>();
var journalDesc = ScriptableObject.CreateInstance<BodyTextQuestContent>();
journalTitle.useQuestTitle = true;
journalDesc.bodyText = new StringField(
$"I need to collect {requiredAmount} x {randomItem.itemName} for the NPC. " +
$"Once I have enough, I'll return for a reward of {coinReward} coins."
);
activeStateInfo.GetContentList(QuestContentCategory.Journal).Add(journalTitle);
activeStateInfo.GetContentList(QuestContentCategory.Journal).Add(journalDesc);
var hudTitle = ScriptableObject.CreateInstance<HeadingTextQuestContent>();
var hudDesc = ScriptableObject.CreateInstance<BodyTextQuestContent>();
hudTitle.useQuestTitle = true;
hudDesc.bodyText = new StringField($"Collect {requiredAmount} x {randomItem.itemName}");
activeStateInfo.GetContentList(QuestContentCategory.HUD).Add(hudTitle);
activeStateInfo.GetContentList(QuestContentCategory.HUD).Add(hudDesc);
}
// --- Start Node ---
QuestNode startNode = newQuest.nodeList[0];
startNode.id = new StringField("StartNode");
startNode.internalName = new StringField("Start");
startNode.childList = new List<QuestNode>();
// --- Requirement Node ---
// Initialize conditionSet explicitly if needed.
QuestNode requirementNode = new QuestNode
{
id = new StringField("RequirementNode"),
internalName = new StringField("Collect Condition"),
childList = new List<QuestNode>(),
parentList = new List<QuestNode>(),
conditionSet = new QuestConditionSet() // Ensure this is not null.
};
if (requirementNode.conditionSet.conditionList == null)
{
requirementNode.conditionSet.conditionList = new List<QuestCondition>();
}
newQuest.nodeList.Add(requirementNode);
startNode.childList.Add(requirementNode);
requirementNode.parentList.Add(startNode);
// --- Success Node ---
QuestNode successNode = new QuestNode
{
id = new StringField("SuccessNode"),
internalName = new StringField("Complete Quest"),
childList = new List<QuestNode>(),
parentList = new List<QuestNode>()
};
newQuest.nodeList.Add(successNode);
requirementNode.childList.Add(successNode);
successNode.parentList.Add(requirementNode);
// --- Add custom actions to the success node:
var successInfo = successNode.GetStateInfo(QuestNodeState.True);
if (successInfo == null)
{
// If no state info exists for the True state, create one and add it.
successInfo = new QuestStateInfo();
// Depending on your Quest Machine version, you might need to add it to the node’s state info list.
successNode.stateInfoList.Add(successInfo);
}
if (successInfo.actionList == null)
successInfo.actionList = new List<QuestAction>();
// Create actions using ScriptableObject.CreateInstance:
RemoveItem removeAction = ScriptableObject.CreateInstance<RemoveItem>();
removeAction.itemToRemove = randomItem;
removeAction.amount = requiredAmount;
successInfo.actionList.Add(removeAction);
GiveMoney moneyAction = ScriptableObject.CreateInstance<GiveMoney>();
moneyAction.amount = coinReward;
successInfo.actionList.Add(moneyAction);
// Add a "Thank you" dialogue content:
BodyTextQuestContent thankYouText = ScriptableObject.CreateInstance<BodyTextQuestContent>();
thankYouText.bodyText = new StringField("Thank you for bringing me the items!");
successInfo.GetContentList(QuestContentCategory.Dialogue).Add(thankYouText);
// --- Update childIndexList ---
foreach (QuestNode node in newQuest.nodeList)
{
node.childIndexList.Clear();
foreach (QuestNode child in node.childList)
{
int idx = newQuest.nodeList.IndexOf(child);
if (idx >= 0)
node.childIndexList.Add(idx);
}
}
// --- Set Runtime References ---
newQuest.SetRuntimeReferences();
foreach (var node in newQuest.nodeList)
{
node.InitializeRuntimeReferences(newQuest);
node.ConnectRuntimeNodeReferences();
node.SetRuntimeNodeReferences();
}
// --- Add a new counter to the quest ---
QuestCounter itemCounter = new QuestCounter
{
name = new StringField("ItemCounter"),
currentValue = 0,
minValue = 0,
maxValue = requiredAmount
};
newQuest.counterList.Add(itemCounter);
// Assume our new counter is now the last one in the list.
int counterIndex = newQuest.counterList.Count - 1;
// --- Attach a CounterQuestCondition ---
CounterQuestCondition counterCondition = ScriptableObject.CreateInstance<CounterQuestCondition>();
counterCondition.counterIndex = counterIndex; // Index of the counter added to the quest.
counterCondition.counterValueMode = CounterValueConditionMode.AtLeast;
counterCondition.requiredCounterValue = new QuestNumber(requiredAmount);
// Add the counter condition to the requirement node.
requirementNode.conditionSet.conditionList.Add(counterCondition);
newQuest.SetRuntimeReferences();
foreach (var node in newQuest.nodeList)
{
node.InitializeRuntimeReferences(newQuest);
node.ConnectRuntimeNodeReferences();
node.SetRuntimeNodeReferences();
}
// --- Final: Add Quest & Start Dialogue ---
questGiver.AddQuest(newQuest);
Debug.Log($"Assigned quest: '{newQuest.title}' => Collect {requiredAmount} x {randomItem.itemName}, reward {coinReward} coins.");
questGiver.StartDialogueWithPlayer();
// Debug print each node's internal name
foreach (QuestNode node in newQuest.nodeList)
{
Debug.Log("Node: " + node.internalName.value);
}
}
}