Script Driven Quests
Script Driven Quests
Will you have a tutorial on making script driven quests? In other words, I would like to create quests via scripts (C#) and I was hoping for a tutorial. Thank you.
Re: Script Driven Quests
That's a good idea. The manual briefly discusses the QuestBuilder class, but certainly not in enough detail to qualify as a step-by-step tutorial.
Are you interested in creating quests with your own script logic or by invoking the procedural quest generator?
Are you interested in creating quests with your own script logic or by invoking the procedural quest generator?
Re: Script Driven Quests
Of course, I want it all! At this point in my design, I am considering both types of quests as they would both be very useful!
Re: Script Driven Quests
Using the procedural quest generator is easiest. Here's an example that assumes the script's GameObject has a QuestGeneratorEntity component:
Otherwise, if you don't have a QuestGeneratorEntity, you can create a QuestGenerator manually:
As you can see, QuestGenerator.GenerateQuest() takes a lot of parameters. It runs in the background. When it's done, it calls a function that you specify (OnGeneratedQuest in the example above), passing it the newly-generated quest.
Building a quest with your own script logic isn't too hard, but it's a lengthy process. It looks something like this:
Code: Select all
GetComponent<QuestGeneratorEntity>().GenerateQuest();
Code: Select all
var questGenerator = new QuestGenerator();
questGenerator.GenerateQuest(this, questGroup, domainType, worldModel, requireReturnToComplete, rewardsUIContents, rewardSystems, existingQuestList, OnGeneratedQuest);
void OnGeneratedQuest(Quest quest)
{
Debug.Log("We just generated a new quest titled " + quest.title);
GetComponent<QuestGiver>().AddQuest(quest);
}
Building a quest with your own script logic isn't too hard, but it's a lengthy process. It looks something like this:
Code: Select all
// Start our QuestBuilder: (It's like .NET's StringBuilder, but for quests.)
var questBuilder = new QuestBuilder(name, id, title);
// Set up some basic properties:
questBuilder.quest.icon = questIcon;
questBuilder.quest.isTrackable = true;
questBuilder.quest.showInTrackHUD = true;
// Add offer text:
questBuilder.AddOfferContents(questBuilder.CreateTitleContent(), questBuilder.CreateBodyContent("Do this quest!));
// Add titles to the Active and Successful states' dialogue text:
var activeState = questBuilder.quest.stateInfoList[(int)QuestState.Active];
var activeContents = activeState.categorizedContentList[(int)QuestContentCategory.Dialogue]
questBuilder.AddContents(activeContents, questBuilder.CreateTitleContent());
// (You'll probably also want to add this to QuestContentCategory.Journal and .HUD.)
// Add a Condition node: [START] --> [Condition]
var conditionNode = questBuilder.AddConditionNode(questBuilder.GetStartNode(), id, internalName, ConditionCountMode.All);
// ...and add a message condition "Did Something":
questBuilder.AddMessageCondition(conditionNode, QuestMessageParticipant.Any, null, QuestMessageParticipant.Any, null, new StringField("Did"), new StringField("Something")));
// (Should also add text content to dialogue, journal, and HUD categories.)
// Add a Success node: [START] --> [Condition] --> [SUCCESS]
questBuilder.AddSuccessNode(conditionNode);
// Finally(!) get the quest:
Quest myQuest = questBuilder.ToQuest();
Re: Script Driven Quests
I am trying to generate the Find Coins quest via C#. Given what you posted earlier, I have created this C# class.
I am missing several pieces like adding text to dialogue, journal, and HUD categories. I am not clear on what the questBuilder.AddMessageCondition is doing (how does this relate to the UI?).
Also, notice that the counter definition does not include the Message definition.
If you would be so kind, would you please look at this code and find in the gaps?
Thank you,
Gandoff
I am missing several pieces like adding text to dialogue, journal, and HUD categories. I am not clear on what the questBuilder.AddMessageCondition is doing (how does this relate to the UI?).
Also, notice that the counter definition does not include the Message definition.
If you would be so kind, would you please look at this code and find in the gaps?
Thank you,
Gandoff
Code: Select all
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;
using PixelCrushers.QuestMachine.Demo;
public class CreateFindCoinsQuest : MonoBehaviour
{
// Quest parameters
public string questId = "FindCoins";
public string questTitle = "Find Coins";
public Sprite questIcon;
public string questOfferText = "Orcs stole my gold and hid it in containers around the village! Will you please find 3 coins so I can buy seeds next season?";
// Counter parameters
public string counterName = "coins";
public int counterInitial = 0;
public int counterMin = 0;
public int counterMax = 3;
public bool counterRndInitVal = false;
// State parameters
public string state1Id = "Condition1";
public string state1Name = "Find Coins";
public string state1ActDialogText = "Please find my money.";
public string state1JournalText = "Find the farmer's coins hidden in containters.";
public string state1HUDText = "{#coins}/3 Coins";
public string state1TrueJournalText = "You found the farmer's coins and saved the farm!";
// Use this for initialization
void Start ()
{
// Start our QuestBuilder: (It's like .NET's StringBuilder, but for quests.)
var questBuilder = new QuestBuilder(name, questId, questTitle);
// Set up some basic properties:
questBuilder.quest.icon = questIcon;
questBuilder.quest.isTrackable = true;
questBuilder.quest.showInTrackHUD = true;
// Add offer text:
questBuilder.AddOfferContents(questBuilder.CreateTitleContent(), questBuilder.CreateBodyContent(questOfferText));
// Add titles to the Active and Successful states' dialogue text:
var activeState = questBuilder.quest.stateInfoList[(int)QuestState.Active];
var activeContents = activeState.categorizedContentList[(int)QuestContentCategory.Dialogue];
questBuilder.AddContents(activeContents, questBuilder.CreateTitleContent());
// (You'll probably also want to add this to QuestContentCategory.Journal and .HUD.)
// Add counter
questBuilder.AddCounter(counterName, counterInitial, counterMin, counterMax, counterRndInitVal, PixelCrushers.QuestMachine.QuestCounterUpdateMode
.Messages);
// Add a Condition node: [START] --> [Condition]
var conditionNode = questBuilder.AddConditionNode(questBuilder.GetStartNode(), state1Id, state1Name, ConditionCountMode.All);
// ...and add a message condition "Did Something":
questBuilder.AddMessageCondition(conditionNode, QuestMessageParticipant.Any, null, QuestMessageParticipant.Any, null, new StringField("Did"), new StringField("Something")));
// (Should also add text content to dialogue, journal, and HUD categories.)
// Add a Success node: [START] --> [Condition] --> [SUCCESS]
questBuilder.AddSuccessNode(conditionNode);
// Finally(!) get the quest:
Quest myQuest = questBuilder.ToQuest();
}
}
Re: Script Driven Quests
Here you go! If anything doesn't make sense, just ask. I added a "return to NPC" node. So after finding 3 coins, the next step is to talk to the villager again, instead of the quest immediately becoming successful.
Code: Select all
using UnityEngine;
using PixelCrushers;
using PixelCrushers.QuestMachine;
[RequireComponent(typeof(QuestGiver))]
public class CreateFindCoinsQuest : MonoBehaviour
{
// Quest parameters
public string questId = "FindCoins";
public string questTitle = "Find Coins";
public Sprite questIcon;
public string questOfferText = "Orcs stole my gold and hid it in containers around the village! Will you please find 3 coins so I can buy seeds next season?";
// Counter parameters
public string counterName = "coins";
public int counterInitial = 0;
public int counterMin = 0;
public int counterMax = 3;
public bool counterRndInitVal = false;
// State 1 parameters
public string state1Id = "Condition1";
public string state1Name = "Find Coins";
public string state1ActDialogText = "Please find my money.";
public string state1ActJournalText = "Find the farmer's coins hidden in containers.";
public string state1HUDText = "{#coins}/3 Coins"; // {#coins} is a special tag representing the value of the coins counter.
// State 1 parameters
public string state2ActDialogText = "Thanks for finding my coins!";
public string state2ActJournalText = "Return to the villager.";
public string state2HUDText = "Return to villager";
public string state2TrueJournalText = "You found the farmer's coins and saved the farm!";
// Use this for initialization
void Start()
{
// Start our QuestBuilder: (It's like .NET's StringBuilder, but for quests.)
var questBuilder = new QuestBuilder(name, questId, questTitle);
// Set up some basic properties:
questBuilder.quest.icon = questIcon;
questBuilder.quest.isTrackable = true;
questBuilder.quest.showInTrackHUD = true;
// You could also set questBuilder.quest.group if you want to put this quest in a group.
// Add offer text:
questBuilder.AddOfferContents(questBuilder.CreateTitleContent(), questBuilder.CreateBodyContent(questOfferText));
// Add quest title to the Active and Successful states' dialogue, journal, and HUD text:
//
// The first line below looks up the Active state, and the Dialogue content for the Active state.
// Then it adds title content, which is a HeadingTextQuestContent object with useQuestTitle set true.
questBuilder.AddContents(questBuilder.quest.GetStateInfo(QuestState.Active).GetContentList(QuestContentCategory.Dialogue), questBuilder.CreateTitleContent());
// The remaining lines for the same for Journal and HUD content in the Active and Successful states.
questBuilder.AddContents(questBuilder.quest.GetStateInfo(QuestState.Active).GetContentList(QuestContentCategory.Journal), questBuilder.CreateTitleContent());
questBuilder.AddContents(questBuilder.quest.GetStateInfo(QuestState.Active).GetContentList(QuestContentCategory.HUD), questBuilder.CreateTitleContent());
questBuilder.AddContents(questBuilder.quest.GetStateInfo(QuestState.Successful).GetContentList(QuestContentCategory.Dialogue), questBuilder.CreateTitleContent());
questBuilder.AddContents(questBuilder.quest.GetStateInfo(QuestState.Successful).GetContentList(QuestContentCategory.Journal), questBuilder.CreateTitleContent());
//===== COUNTER CONDITION NODE =====
// Add counter to keep track of number of coins picked up:
var counter = questBuilder.AddCounter(counterName, counterInitial, counterMin, counterMax, counterRndInitVal, PixelCrushers.QuestMachine.QuestCounterUpdateMode.Messages);
// When picked up, coins send the message "Get" "Coin". Tell the counter to increment when this message is sent:
var counterMessageEvent = new QuestCounterMessageEvent(null, new StringField("Get"), new StringField("Coin"), QuestCounterMessageEvent.Operation.ModifyByLiteralValue, 1);
counter.messageEventList.Add(counterMessageEvent);
// Add a counter condition node ([START] --> [Condition]):
var conditionNode = questBuilder.AddConditionNode(questBuilder.GetStartNode(), state1Id, state1Name, ConditionCountMode.All);
questBuilder.AddCounterCondition(conditionNode, counterName, CounterValueConditionMode.AtLeast, counterMax);
// Add text to show when node is active:
questBuilder.AddContents(conditionNode.GetStateInfo(QuestNodeState.Active).GetContentList(QuestContentCategory.Dialogue), questBuilder.CreateBodyContent(state1ActDialogText));
questBuilder.AddContents(conditionNode.GetStateInfo(QuestNodeState.Active).GetContentList(QuestContentCategory.Journal), questBuilder.CreateBodyContent(state1ActJournalText));
questBuilder.AddContents(conditionNode.GetStateInfo(QuestNodeState.Active).GetContentList(QuestContentCategory.HUD), questBuilder.CreateBodyContent(state1HUDText));
//===== RETURN TO NPC NODE =====
// Add a "return to quest giver" node ([START] --> [Condition] --> [Return]):
var questGiver = GetComponent<QuestGiver>();
var returnNode = questBuilder.AddDiscussQuestNode(conditionNode, QuestMessageParticipant.QuestGiver, questGiver.id);
// Text when active:
// Add text to show when node is active:
questBuilder.AddContents(returnNode.GetStateInfo(QuestNodeState.Active).GetContentList(QuestContentCategory.Dialogue), questBuilder.CreateBodyContent(state2ActDialogText));
questBuilder.AddContents(returnNode.GetStateInfo(QuestNodeState.Active).GetContentList(QuestContentCategory.Journal), questBuilder.CreateBodyContent(state2ActJournalText));
questBuilder.AddContents(returnNode.GetStateInfo(QuestNodeState.Active).GetContentList(QuestContentCategory.HUD), questBuilder.CreateBodyContent(state2HUDText));
//// Add text to show when node is true:
questBuilder.AddContents(returnNode.GetStateInfo(QuestNodeState.True).GetContentList(QuestContentCategory.Dialogue), questBuilder.CreateBodyContent(state2ActDialogText));
questBuilder.AddContents(returnNode.GetStateInfo(QuestNodeState.True).GetContentList(QuestContentCategory.Journal), questBuilder.CreateBodyContent(state2TrueJournalText));
// Add an alert action to when this node becomes active:
var actionList = returnNode.GetStateInfo(QuestNodeState.Active).actionList;
var alertAction = questBuilder.CreateAlertAction(state2HUDText);
actionList.Add(alertAction);
// Set the quest indicator to Talk when active:
var indicatorAction = questBuilder.CreateSetIndicatorAction(questBuilder.quest.id, questGiver.id, QuestIndicatorState.Talk);
returnNode.GetStateInfo(QuestNodeState.Active).actionList.Add(indicatorAction);
// Set the Indicator to None when true:
indicatorAction = questBuilder.CreateSetIndicatorAction(questBuilder.quest.id, questGiver.id, QuestIndicatorState.None);
returnNode.GetStateInfo(QuestNodeState.True).actionList.Add(indicatorAction);
//===== SUCCESS NODE =====
// Add a Success node: [START] --> [Condition] --> [Return] -- > [SUCCESS]
questBuilder.AddSuccessNode(returnNode);
//===== FINISH CREATION PROCESS =====
// Finally(!) get the quest:
Quest myQuest = questBuilder.ToQuest();
// And add it to the QuestGiver:
GetComponent<QuestGiver>().AddQuest(myQuest);
}
}
Re: Script Driven Quests
Wow, thanks! Is it possible to assign a quest directly to a player without going through a quest giver in C#?
I will assimilate your code and let you know.
I will assimilate your code and let you know.
Re: Script Driven Quests
Sure! At the end, instead of:
Code: Select all
GetComponent<QuestGiver>().AddQuest(myQuest);
Code: Select all
GetComponent<QuestJournal>().AddQuest(myQuest);
myQuest.SetState(QuestState.Active);
The second line (myQuest.SetState) activates the quest.
Re: Script Driven Quests
Your code worked like a charm.
Thank you.
Thank you.