// Copyright (c) Pixel Crushers. All rights reserved. using UnityEngine; using System; using System.Collections.Generic; namespace PixelCrushers.QuestMachine { /// /// Utility class for procedurally building quests. /// public class QuestBuilder { public Quest quest { get; set; } #region Constructors /// /// Creates a new quest. /// /// Name to use for quest name, ID, and title. public QuestBuilder(StringField name) { CreateQuest(StringField.GetStringValue(name), name, name); } /// /// Creates a new quest. /// /// Name to use for quest name, ID, and title. public QuestBuilder(string name) { CreateQuest(name, name, name); } /// /// Creates a new quest. /// /// Quest name. /// Quest ID. /// Quest title (visible in UIs). public QuestBuilder(string name, StringField id, StringField title) { CreateQuest(name, id, title); } /// /// Creates a new quest. /// /// Quest name. /// Quest ID. /// Quest title (visible in UIs). public QuestBuilder(string name, string id, string title) { CreateQuest(name, new StringField(id), new StringField(title)); } /// /// Creates a QuestBuilder for an existing quest. Use this form to add /// new content to an existing quest. /// /// The quest to edit. public QuestBuilder(Quest quest) { this.quest = quest; } private void CreateQuest(string name, StringField id, StringField title) { quest = Quest.CreateInstance(); quest.Initialize(); quest.isAsset = false; quest.isInstance = true; quest.name = name; quest.id = id; quest.title = title; GetStartNode().id = new StringField(id + ".start"); } private void CreateQuest(string name, string id, string title) { CreateQuest(name, new StringField(id), new StringField(title)); } public Quest ToQuest() { if (quest == null) return null; ValidateListSizes(); quest.SetRuntimeReferences(); return quest; } private void ValidateListSizes() { var numStates = Enum.GetNames(typeof(QuestState)).Length; QuestStateInfo.ValidateStateInfoListCount(quest.stateInfoList, numStates); for (int i = 0; i < numStates; i++) { QuestStateInfo.ValidateCategorizedContentListCount(quest.stateInfoList[i].categorizedContentList); } } #endregion #region Counters public QuestCounter AddCounter(StringField counterName, int initialValue, int minValue, int maxValue, bool randomizeInitialValue, QuestCounterUpdateMode updateMode) { if (quest.counterList.Find(x => StringField.Equals(x.name, counterName)) != null) { if (Debug.isDebugBuild) Debug.LogWarning("Quest Machine: Counter '" + counterName + "' already exists in QuestBuilder."); return null; } var counter = new QuestCounter(counterName, initialValue, minValue, maxValue, updateMode); quest.counterList.Add(counter); return counter; } public QuestCounter AddCounter(string counterName, int initialValue, int minValue, int maxValue, bool randomizeInitialValue, QuestCounterUpdateMode updateMode) { return AddCounter(new StringField(counterName), initialValue, minValue, maxValue, randomizeInitialValue, updateMode); ; } public void AddCounterMessageEvent(StringField counterName, StringField targetID, StringField message, StringField parameter, QuestCounterMessageEvent.Operation operation, int literalValue = 0) { var counter = quest.GetCounter(counterName); if (counter == null) { if (Debug.isDebugBuild) Debug.LogWarning("Quest Machine: Counter '" + counterName + "' isn't present in QuestBuilder."); return; } counter.updateMode = QuestCounterUpdateMode.Messages; counter.messageEventList.Add(new QuestCounterMessageEvent(targetID, message, parameter, operation, literalValue)); } public void AddCounterMessageEvent(StringField counterName, string targetID, string message, string parameter, QuestCounterMessageEvent.Operation operation, int literalValue = 0) { AddCounterMessageEvent(counterName, new StringField(targetID), new StringField(message), new StringField(parameter), operation, literalValue); } public void AddCounterMessageEvent(string counterName, string targetID, string message, string parameter, QuestCounterMessageEvent.Operation operation, int literalValue = 0) { AddCounterMessageEvent(new StringField(counterName), new StringField(targetID), new StringField(message), new StringField(parameter), operation, literalValue); } #endregion #region Rewards public void AssignRewards(RewardSystem[] rewardSystems, int points) { if (rewardSystems == null) return; var pointsRemaining = points; for (int i = 0; i < rewardSystems.Length; i++) { var rewardSystem = rewardSystems[i]; if (rewardSystem == null) continue; pointsRemaining = rewardSystem.DetermineReward(pointsRemaining, quest); if (pointsRemaining <= 0) break; } } #endregion #region Offer Content public void AddOfferContents(params QuestContent[] contents) { AddContents(quest.offerContentList, contents); } public void AddOfferUnmetContents(params QuestContent[] contents) { AddContents(quest.offerConditionsUnmetContentList, contents); } #endregion #region Create Content //[TODO] Add error reporting to all null checks in QuestBuilder. public void AddContents(QuestContentSet contentSet, params QuestContent[] contents) { if (contentSet == null) return; AddContents(contentSet.contentList, contents); } public void AddContents(List contentList, params QuestContent[] contents) { if (contentList == null || contents == null) return; contentList.AddRange(contents); } public QuestContent CreateTitleContent() { var content = HeadingTextQuestContent.CreateInstance(); content.name = "title"; content.useQuestTitle = true; content.headingLevel = 1; return content; } public QuestContent CreateHeadingContent(StringField text, int level) { var content = HeadingTextQuestContent.CreateInstance(); content.name = "heading"; content.useQuestTitle = false; content.headingText = text; content.headingLevel = level; return content; } public QuestContent CreateHeadingContent(string text, int level) { return CreateHeadingContent(new StringField(text), level); } public QuestContent CreateBodyContent(StringField text) { var content = BodyTextQuestContent.CreateInstance(); content.name = "text"; content.bodyText = new StringField(text); return content; } public QuestContent CreateBodyContent(string text) { return CreateBodyContent(new StringField(text)); } //[TODO] Other content types in QuestBuilder. #endregion #region Nodes public QuestNode GetStartNode() { return quest.nodeList[0]; } public QuestNode AddNode(QuestNode parent, StringField id, StringField internalName, QuestNodeType nodeType, bool isOptional = false) { if (parent == null) { if (Debug.isDebugBuild) Debug.LogWarning("Quest Machine: QuestBuilder.AddNode must be provided a valid parent node."); return null; } if (parent.childIndexList == null) return null; parent.childIndexList.Add(quest.nodeList.Count); var node = new QuestNode(id, internalName, nodeType, isOptional); node.canvasRect = new Rect(parent.canvasRect.x, parent.canvasRect.y + 20 + QuestNode.DefaultNodeHeight, QuestNode.DefaultNodeWidth, QuestNode.DefaultNodeHeight); quest.nodeList.Add(node); QuestStateInfo.ValidateStateInfoListCount(node.stateInfoList); QuestStateInfo.ValidateCategorizedContentListCount(node.stateInfoList[(int)QuestNodeState.Active].categorizedContentList); QuestStateInfo.ValidateCategorizedContentListCount(node.stateInfoList[(int)QuestNodeState.Inactive].categorizedContentList); QuestStateInfo.ValidateCategorizedContentListCount(node.stateInfoList[(int)QuestNodeState.True].categorizedContentList); return node; } public QuestNode AddNode(QuestNode parent, string id, string internalName, QuestNodeType nodeType, bool isOptional = false) { return AddNode(parent, new StringField(id), new StringField(internalName), nodeType, isOptional); } public QuestNode AddSuccessNode(QuestNode parent) { return AddNode(parent, "success", "Success", QuestNodeType.Success); } public QuestNode AddFailureNode(QuestNode parent) { return AddNode(parent, "failure", "Failure", QuestNodeType.Failure); } public QuestNode AddPassthroughNode(QuestNode parent, StringField id, StringField internalName) { return AddNode(parent, id, internalName, QuestNodeType.Passthrough); } public QuestNode AddPassthroughNode(QuestNode parent, string id, string internalName) { return AddPassthroughNode(parent, new StringField(id), new StringField(internalName)); } public QuestNode AddConditionNode(QuestNode parent, StringField id, StringField internalName, ConditionCountMode conditionCountMode = ConditionCountMode.All, bool isOptional = false) { var node = AddNode(parent, id, internalName, QuestNodeType.Condition, isOptional); if (node == null) return null; node.conditionSet.conditionCountMode = conditionCountMode; return node; } public QuestNode AddConditionNode(QuestNode parent, string id, string internalName, ConditionCountMode conditionCountMode = ConditionCountMode.All, bool isOptional = false) { return AddConditionNode(parent, new StringField(id), new StringField(internalName), conditionCountMode, isOptional); } public QuestNode AddDiscussQuestNode(QuestNode parent, QuestMessageParticipant targetSpecifier, StringField targetID, bool isOptional = false) { var node = AddConditionNode(parent, "talkTo" + targetID, "Talk to " + targetID, ConditionCountMode.All, isOptional); AddMessageCondition(node, QuestMessageParticipant.Quester, new StringField(), targetSpecifier, targetID, new StringField(QuestMachineMessages.DiscussedQuestMessage), quest.id); return node; } #endregion #region Conditions public CounterQuestCondition AddCounterCondition(QuestNode node, StringField counterName, CounterValueConditionMode conditionMode, QuestNumber requiredValue) { var condition = CounterQuestCondition.CreateInstance(); condition.name = "counterCondition"; condition.counterIndex = quest.GetCounterIndex(counterName); condition.counterValueMode = conditionMode; condition.requiredCounterValue = requiredValue; node.conditionSet.conditionList.Add(condition); return condition; } public CounterQuestCondition AddCounterCondition(QuestNode node, StringField counterName, CounterValueConditionMode conditionMode, int requiredValue) { return AddCounterCondition(node, counterName, conditionMode, new QuestNumber(requiredValue)); } public CounterQuestCondition AddCounterCondition(QuestNode node, string counterName, CounterValueConditionMode conditionMode, QuestNumber requiredValue) { return AddCounterCondition(node, new StringField(counterName), conditionMode, requiredValue); } public CounterQuestCondition AddCounterCondition(QuestNode node, string counterName, CounterValueConditionMode conditionMode, int requiredValue) { return AddCounterCondition(node, new StringField(counterName), conditionMode, new QuestNumber(requiredValue)); } public MessageQuestCondition AddMessageCondition(QuestNode node, QuestMessageParticipant senderSpecifier, StringField senderID, QuestMessageParticipant targetSpecifier, StringField targetID, StringField message, StringField parameter, MessageValue value = null) { var condition = MessageQuestCondition.CreateInstance(); condition.name = "messageCondition"; condition.senderSpecifier = senderSpecifier; condition.senderID = senderID; condition.targetSpecifier = targetSpecifier; condition.targetID = targetID; condition.message = message; condition.parameter = parameter; condition.value = (value != null) ? value : new MessageValue(); node.conditionSet.conditionList.Add(condition); return condition; } public MessageQuestCondition AddMessageCondition(QuestNode node, QuestMessageParticipant senderSpecifier, string senderID, QuestMessageParticipant targetSpecifier, string targetID, string message, string parameter, MessageValue value = null) { return AddMessageCondition(node, senderSpecifier, new StringField(targetID), targetSpecifier, new StringField(targetID), new StringField(message), new StringField(parameter), value); } #endregion #region Actions public QuestAction CreateAlertAction(StringField text) { return CreateAlertAction(StringField.GetStringValue(text)); } public QuestAction CreateAlertAction(string text) { var alertAction = AlertQuestAction.CreateInstance(); AddContents(alertAction.contentList, CreateBodyContent(text)); return alertAction; } public QuestAction CreateMessageAction(StringField message, StringField parameter) { return CreateMessageAction(StringField.GetStringValue(message), StringField.GetStringValue(parameter)); } public QuestAction CreateMessageAction(string message, string parameter) { var messageAction = MessageQuestAction.CreateInstance(); messageAction.message = new StringField(message); messageAction.parameter = new StringField(parameter); return messageAction; } public QuestAction CreateMessageAction(StringField text) { return CreateMessageAction(StringField.GetStringValue(text)); } public QuestAction CreateMessageAction(string text) { if (text.Contains(":")) // Parameter? { var colonPos = text.IndexOf(':'); return CreateMessageAction(text.Substring(colonPos + 1), text.Substring(0, colonPos)); } else { return CreateMessageAction(text, string.Empty); } } public QuestAction CreateSetIndicatorAction(StringField questID, StringField entityID, QuestIndicatorState indicatorState) { var indicatorAction = SetIndicatorQuestAction.CreateInstance(); indicatorAction.questID = questID; indicatorAction.entityID = entityID; indicatorAction.questIndicatorState = indicatorState; return indicatorAction; } //[TODO] Other action types in QuestBuilder. #endregion } }