// Copyright (c) Pixel Crushers. All rights reserved. using UnityEngine; using System.Collections.Generic; namespace PixelCrushers.DialogueSystem { public delegate QuestState StringToQuestStateDelegate(string s); public delegate string QuestStateToStringDelegate(QuestState state); public delegate string CurrentQuestStateDelegate(string quest); public delegate void SetQuestStateDelegate(string quest, string state); public delegate void SetQuestEntryStateDelegate(string quest, int entryNumber, string state); public delegate string CurrentQuestEntryStateDelegate(string quest, int entryNumber); public struct QuestEntryArgs { public string questName; public int entryNumber; public QuestEntryArgs(string questName, int entryNumber) { this.questName = questName; this.entryNumber = entryNumber; } } /// /// A static class that manages a quest log. It uses the Lua "Item[]" table, where each item in /// the table whose 'Is Item' field is false represents a quest. This makes it easy to manage quests /// in Chat Mapper by adding, removing, and modifying items in the built-in Item[] table. The name /// of the item is the title of the quest. (Note that the Chat Mapper simulator doesn't have a /// quest system, so it treats elements of the Item[] table as items.) /// /// This class uses the following fields in the Item[] table, which is also aliased as Quest[]: /// /// - Display Name: (optional) Name to use in UIs. If blank or not present, UIs use Name field. /// - State (if using Chat Mapper, add this custom field or use the Dialogue System template /// project) /// - Valid values (case-sensitive): unassigned, active, success, /// failure, or done /// - Description: The description of the quest /// - Success Description (optional): The description to be displayed when the quest has been /// successfully completed /// - Failure Description (optional): The description to be displayed when the quest has ended /// in failure /// /// Note: done is essentially equivalent to success. In the remainder of the Dialogue /// System's documentation, either done or success may be used in examples, but when /// using the QuestLog class, they both correspond to the same enum state, QuestState.Success. /// /// As an example, you might define a simple quest like this: /// /// - Item["Kill 5 Rats"] /// - State = "unassigned" /// - Description = "The baker asked me to bring him 5 rat corpses to make a pie." /// - Success Description = "I brought the baker 5 dead rats, and we ate a delicious pie!" /// - Failure Description = "I freed the Pied Piper from jail. He took all the rats. No pie for me...." /// /// This class provides methods to add and delete quests, get and set their state, and get /// their descriptions. /// /// Note that quest states are usually updated during conversations. In most cases, you will /// probably set quest states in Lua code during conversations, so you may never need to use /// many of the methods in this class. /// /// The UnityQuestLogWindow provides a quest log window using Unity GUI. You can use it as-is /// or use it as a template for implementing your own quest log window in another GUI system /// such as NGUI. /// public static class QuestLog { /// /// Constant state string for unassigned quests. /// public const string UnassignedStateString = "unassigned"; /// /// Constant state string for active quests. /// public const string ActiveStateString = "active"; /// /// Constant state string for successfully-completed quests. /// public const string SuccessStateString = "success"; /// /// Constant state string for quests ending in failure. /// public const string FailureStateString = "failure"; /// /// Constant state string for quests that were abandoned. /// public const string AbandonedStateString = "abandoned"; /// /// Constant state string for quests that are grantable. /// This state isn't used by the Dialogue System, but it's made available for those who want to use it. /// public const string GrantableStateString = "grantable"; /// /// Constant state string for quests that are waiting to return to NPC. /// This state isn't used by the Dialogue System, but it's made available for those who want to use it. /// public const string ReturnToNPCStateString = "returnToNPC"; /// /// Constant state string for quests that are done, if you want to track done instead of success/failure. /// This is essentially the same as success, and corresponds to the same enum value, QuestState.Success /// public const string DoneStateString = "done"; /// /// You can reassign this delegate method to override the default conversion of /// strings to QuestStates. /// public static StringToQuestStateDelegate StringToState = DefaultStringToState; public static QuestStateToStringDelegate StateToString = DefaultStateToString; /// /// You can assign a method to override the default CurrentQuestState. /// public static CurrentQuestStateDelegate CurrentQuestStateOverride = null; /// /// You can assign a method to override the default SetQuestState. /// public static SetQuestStateDelegate SetQuestStateOverride = null; /// /// You can assign a method to override the default CurrentQuestEntryState. /// public static CurrentQuestEntryStateDelegate CurrentQuestEntryStateOverride = null; /// /// You can assign a method to override the default SetQuestEntryState. /// public static SetQuestEntryStateDelegate SetQuestEntryStateOverride = null; /// /// Set true to allow only one quest to be tracked at a time. /// public static bool trackOneQuestAtATime = false; public static void RegisterQuestLogFunctions() { // Unity 2017.3 bug IL2CPP can't do lambdas: //Lua.RegisterFunction("CurrentQuestState", null, SymbolExtensions.GetMethodInfo(() => CurrentQuestState(string.Empty))); //Lua.RegisterFunction("CurrentQuestEntryState", null, SymbolExtensions.GetMethodInfo(() => CurrentQuestEntryState(string.Empty, (double)0))); //Lua.RegisterFunction("SetQuestState", null, SymbolExtensions.GetMethodInfo(() => SetQuestState(string.Empty, string.Empty))); //Lua.RegisterFunction("SetQuestEntryState", null, SymbolExtensions.GetMethodInfo(() => SetQuestEntryState(string.Empty, (double)0, string.Empty))); Lua.RegisterFunction("CurrentQuestState", null, typeof(QuestLog).GetMethod("CurrentQuestState")); Lua.RegisterFunction("CurrentQuestEntryState", null, typeof(QuestLog).GetMethod("CurrentQuestEntryState")); Lua.RegisterFunction("SetQuestState", null, typeof(QuestLog).GetMethod("SetQuestState", new[] { typeof(string), typeof(string) })); Lua.RegisterFunction("SetQuestEntryState", null, typeof(QuestLog).GetMethod("SetQuestEntryState", new[] { typeof(string), typeof(double), typeof(string) })); Lua.RegisterFunction("UpdateQuestIndicators", null, typeof(QuestLog).GetMethod("UpdateQuestIndicators", new[] { typeof(string) })); } /// /// Adds a quest to the Lua Item[] table. /// /// /// Name of the quest. /// /// /// Description of the quest when active. /// /// /// Description of the quest when successfully completed. /// /// /// Description of the quest when completed in failure. /// /// /// Quest state. /// /// /// QuestLog.AddQuest("Kill 5 Rats", "The baker asked me to bring 5 rat corpses.", QuestState.Unassigned); /// public static void AddQuest(string questName, string description, string successDescription, string failureDescription, QuestState state) { if (!string.IsNullOrEmpty(questName)) { Lua.Run(string.Format("Item[\"{0}\"] = {{ Name = \"{1}\", Is_Item = false, Description = \"{2}\", Success_Description = \"{3}\", Failure_Description = \"{4}\", State = \"{5}\" }}", new System.Object[] { DialogueLua.StringToTableIndex(questName), DialogueLua.DoubleQuotesToSingle(questName), DialogueLua.DoubleQuotesToSingle(description), DialogueLua.DoubleQuotesToSingle(successDescription), DialogueLua.DoubleQuotesToSingle(failureDescription), StateToString(state) }), DialogueDebug.logInfo); } } /// /// Adds a quest to the Lua Item[] table. /// /// /// Name of the quest. /// /// /// Description of the quest. /// /// /// Quest state. /// /// /// QuestLog.AddQuest("Kill 5 Rats", "The baker asked me to bring 5 rat corpses.", QuestState.Unassigned); /// public static void AddQuest(string questName, string description, QuestState state) { if (!string.IsNullOrEmpty(questName)) { Lua.Run(string.Format("Item[\"{0}\"] = {{ Name = \"{1}\", Is_Item = false, Description = \"{2}\", State = \"{3}\" }}", new System.Object[] { DialogueLua.StringToTableIndex(questName), DialogueLua.DoubleQuotesToSingle(questName), DialogueLua.DoubleQuotesToSingle(description), StateToString(state) }), DialogueDebug.logInfo); } } /// /// Adds a quest to the Lua Item[] table, and sets the state to Unassigned. /// /// /// Name of the quest. /// /// /// Description of the quest. /// /// /// QuestLog.AddQuest("Kill 5 Rats", "The baker asked me to bring 5 rat corpses."); /// public static void AddQuest(string questName, string description) { AddQuest(questName, description, QuestState.Unassigned); } /// /// Deletes a quest from the Lua Item[] table. Use this method if you want to remove a quest entirely. /// If you just want to set the state of a quest, use SetQuestState. /// /// /// Name of the quest. /// /// /// QuestLog.RemoveQuest("Kill 5 Rats"); /// public static void DeleteQuest(string questName) { if (!string.IsNullOrEmpty(questName)) { Lua.Run(string.Format("Item[\"{0}\"] = nil", new System.Object[] { DialogueLua.StringToTableIndex(questName) }), DialogueDebug.logInfo); } } /// /// Gets the quest state. /// /// /// The quest state. /// /// /// Name of the quest. /// /// /// if (QuestLog.QuestState("Kill 5 Rats") == QuestState.Active) { /// Smith.Say("Killing rats, eh? Here, take this hammer."); /// } /// public static QuestState GetQuestState(string questName) { return StringToState(CurrentQuestState(questName)); //---Was: DialogueLua.GetQuestField(questName, "State").AsString); } /// /// Gets the quest state. /// /// Name of the quest. /// The quest state string ("unassigned", "success", etc.). public static string CurrentQuestState(string questName) { if (CurrentQuestStateOverride != null) { return CurrentQuestStateOverride(questName); } else { return DefaultCurrentQuestState(questName); } } /// /// Default built-in version of CurrentQuestState. /// public static string DefaultCurrentQuestState(string questName) { return DialogueLua.GetQuestField(questName, "State").asString; } /// /// Sets the quest state. /// /// /// Name of the quest. /// /// /// New state. /// /// /// if (PiedPiperIsFree) { /// QuestLog.SetQuestState("Kill 5 Rats", QuestState.Failure); /// } /// public static void SetQuestState(string questName, QuestState state) { SetQuestState(questName, StateToString(state)); } /// /// Sets the quest state, using the override delegate if assigned; otherwise /// using the default method DefaultSetQuestState. /// /// Name of the quest. /// New state. public static void SetQuestState(string questName, string state) { if (SetQuestStateOverride != null) { SetQuestStateOverride(questName, state); } else { DefaultSetQuestState(questName, state); } } /// /// Default built-in method to set quest state. /// public static void DefaultSetQuestState(string questName, string state) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { DialogueLua.SetQuestField(questName, "State", state); SendUpdateTracker(); InformQuestStateChange(questName); } else { if (DialogueDebug.logWarnings) Debug.LogWarning("Dialogue System: Quest '" + questName + "' doesn't exist. Can't set state to " + state); } } private static void SendUpdateTracker() { DialogueManager.SendUpdateTracker(); } public static void InformQuestStateChange(string questName) { DialogueManager.instance.BroadcastMessage(DialogueSystemMessages.OnQuestStateChange, questName, SendMessageOptions.DontRequireReceiver); } public static void InformQuestEntryStateChange(string questName, int entryNumber) { DialogueManager.instance.BroadcastMessage(DialogueSystemMessages.OnQuestEntryStateChange, new QuestEntryArgs(questName, entryNumber), SendMessageOptions.DontRequireReceiver); } /// /// Reports whether a quest is unassigned. /// /// /// true if the quest is unassigned; otherwise, false. /// /// /// Name of the quest. /// public static bool IsQuestUnassigned(string questName) { return GetQuestState(questName) == QuestState.Unassigned; } /// /// Reports whether a quest is active. /// /// /// true if the quest is active; otherwise, false. /// /// /// Name of the quest. /// public static bool IsQuestActive(string questName) { return GetQuestState(questName) == QuestState.Active; } /// /// Reports whether a quest was successfully completed. /// /// /// true if the quest was successfully completed; otherwise, false. /// /// /// Name of the quest. /// public static bool IsQuestSuccessful(string questName) { return GetQuestState(questName) == QuestState.Success; } /// /// Reports whether a quest ended in failure. /// /// /// true if the quest ended in failure; otherwise, false. /// /// /// Name of the quest. /// public static bool IsQuestFailed(string questName) { return GetQuestState(questName) == QuestState.Failure; } /// /// Reports whether a quest was abandoned (i.e., in the Abandoned state). /// /// true if the quest was abandoned; otherwise, false. /// Name of the quest. public static bool IsQuestAbandoned(string questName) { return GetQuestState(questName) == QuestState.Abandoned; } /// /// Reports whether a quest is done, either successful or failed. /// /// /// true if the quest is done; otherwise, false. /// /// /// Name of the quest. /// public static bool IsQuestDone(string questName) { QuestState state = GetQuestState(questName); return ((state == QuestState.Success) || (state == QuestState.Failure)); } /// /// Reports whether a quest's current state is one of the states marked in a state bit mask. /// /// /// true if the quest's current state is in the state bit mask. /// /// /// Name of the quest. /// /// /// A QuestState bit mask (e.g., QuestState.Success | QuestState.Failure). /// public static bool IsQuestInStateMask(string questName, QuestState stateMask) { QuestState state = GetQuestState(questName); return ((stateMask & state) == state); } /// /// Reports whether a quest entry's current state is one of the states marked in a state bit mask. /// /// /// true if the quest entry's current state is in the state bit mask. /// /// /// Name of the quest. /// /// /// Quest entry number. /// /// /// A QuestState bit mask (e.g., QuestState.Success | QuestState.Failure). /// public static bool IsQuestEntryInStateMask(string questName, int entryNumber, QuestState stateMask) { QuestState state = GetQuestEntryState(questName, entryNumber); return ((stateMask & state) == state); } /// /// Starts a quest by setting its state to active. /// /// /// Name of the quest. /// /// /// StartQuest("Kill 5 Rats"); /// public static void StartQuest(string questName) { SetQuestState(questName, QuestState.Active); } /// /// Marks a quest successful. /// /// /// Name of the quest. /// public static void CompleteQuest(string questName) { SetQuestState(questName, QuestState.Success); } /// /// Marks a quest as failed. /// /// /// Name of the quest. /// public static void FailQuest(string questName) { SetQuestState(questName, QuestState.Failure); } /// /// Marks a quest as abandoned (i.e., in the Abandoned state). /// /// Name of the quest. public static void AbandonQuest(string questName) { SetQuestState(questName, QuestState.Abandoned); } /// /// Converts a string representation into a state enum value. /// /// /// The state (e.g., QuestState.Active). /// /// /// The string representation (e.g., "active"). /// public static QuestState DefaultStringToState(string s) { if (string.Equals(s, ActiveStateString)) return QuestState.Active; if (string.Equals(s, SuccessStateString) || string.Equals(s, DoneStateString)) return QuestState.Success; if (string.Equals(s, FailureStateString)) return QuestState.Failure; if (string.Equals(s, AbandonedStateString)) return QuestState.Abandoned; if (string.Equals(s, GrantableStateString)) return QuestState.Grantable; if (string.Equals(s, ReturnToNPCStateString)) return QuestState.ReturnToNPC; return QuestState.Unassigned; } public static string DefaultStateToString(QuestState state) { switch (state) { default: case QuestState.Unassigned: return UnassignedStateString; case QuestState.Active: return ActiveStateString; case QuestState.Success: return SuccessStateString; case QuestState.Failure: return FailureStateString; case QuestState.Abandoned: return AbandonedStateString; case QuestState.Grantable: return GrantableStateString; case QuestState.ReturnToNPC: return ReturnToNPCStateString; } } ///// ///// Converts a state to its string representation. ///// ///// ///// The string representation (e.g., "active"). ///// ///// ///// The state (e.g., QuestState.Active). ///// //public static string StateToString(QuestState state) //{ // switch (state) // { // case QuestState.Unassigned: return UnassignedStateString; // case QuestState.Active: return ActiveStateString; // case QuestState.Success: return SuccessStateString; // case QuestState.Failure: return FailureStateString; // case QuestState.Abandoned: return AbandonedStateString; // case QuestState.Grantable: return GrantableStateString; // case QuestState.ReturnToNPC: return ReturnToNPCStateString; // default: return UnassignedStateString; // } //} /// /// Gets the localized quest display name. /// /// /// The quest title (display name) in the current language. /// /// /// Name of the quest. /// public static string GetQuestTitle(string questName) { var title = DialogueLua.GetLocalizedQuestField(questName, "Display Name").asString; if (string.IsNullOrEmpty(title)) title = DialogueLua.GetLocalizedQuestField(questName, "Name").asString; return title; } /// /// Gets a quest description, based on the current state of the quest (i.e., SuccessDescription, FailureDescription, or just Description). /// /// /// The quest description. /// /// /// Name of the quest. /// /// /// GUILayout.Label("Objective: " + QuestLog.GetQuestDescription("Kill 5 Rats")); /// public static string GetQuestDescription(string questName) { switch (GetQuestState(questName)) { case QuestState.Success: return GetQuestDescription(questName, QuestState.Success) ?? GetQuestDescription(questName, QuestState.Active); case QuestState.Failure: return GetQuestDescription(questName, QuestState.Failure) ?? GetQuestDescription(questName, QuestState.Active); default: return GetQuestDescription(questName, QuestState.Active); } } /// /// Gets the localized quest description for a specific state. /// /// /// The quest description. /// /// /// Name of the quest. /// /// /// State to check. /// public static string GetQuestDescription(string questName, QuestState state) { string descriptionFieldName = GetDefaultDescriptionFieldForState(state); string result = DialogueLua.GetLocalizedQuestField(questName, descriptionFieldName).asString; return (string.Equals(result, "nil") || string.IsNullOrEmpty(result)) ? null : result; } private static string GetDefaultDescriptionFieldForState(QuestState state) { switch (state) { case QuestState.Success: return "Success_Description"; case QuestState.Failure: return "Failure_Description"; default: return "Description"; } } /// /// Sets the quest description for a specified state. /// /// /// Name of the quest. /// /// /// Set the description for this state (i.e., regular, success, or failure). /// /// /// The description. /// public static void SetQuestDescription(string questName, QuestState state, string description) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { DialogueLua.SetQuestField(questName, GetDefaultDescriptionFieldForState(state), description); } } /// /// Gets the quest abandon sequence. The QuestLogWindow plays this sequence when the player /// abandons a quest. /// /// The quest abandon sequence. /// Quest name. public static string GetQuestAbandonSequence(string questName) { return DialogueLua.GetLocalizedQuestField(questName, "Abandon Sequence").asString; } /// /// Sets the quest abandon sequence. The QuestLogWindow plays this sequence when the /// player abandons a quest. /// /// Quest name. /// Sequence to play when the quest is abandoned. public static void SetQuestAbandonSequence(string questName, string sequence) { DialogueLua.SetLocalizedQuestField(questName, "Abandon Sequence", sequence); } /// /// Gets the quest entry count. /// /// The quest entry count. /// Name of the quest. public static int GetQuestEntryCount(string questName) { return DialogueLua.GetQuestField(questName, "Entry_Count").asInt; } /// /// Adds a quest entry to a quest. /// /// Name of the quest. /// Entry number. /// The quest entry description. public static void AddQuestEntry(string questName, string description) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { int entryCount = GetQuestEntryCount(questName); entryCount++; DialogueLua.SetQuestField(questName, "Entry_Count", entryCount); string entryFieldName = GetEntryFieldName(entryCount); DialogueLua.SetQuestField(questName, entryFieldName, DialogueLua.DoubleQuotesToSingle(description)); string entryStateFieldName = GetEntryStateFieldName(entryCount); DialogueLua.SetQuestField(questName, entryStateFieldName, "unassigned"); } } /// /// Gets the localized quest entry description. /// /// The quest entry description. /// Name of the quest. /// Entry number. public static string GetQuestEntry(string questName, int entryNumber) { var state = GetQuestEntryState(questName, entryNumber); string entryFieldName = GetEntryFieldName(entryNumber); if (state == QuestState.Success && DialogueLua.DoesTableElementExist("Quest", entryFieldName + " Success")) { entryFieldName += " Success"; } else if (state == QuestState.Failure && DialogueLua.DoesTableElementExist("Quest", entryFieldName + " Failure")) { entryFieldName += " Failure"; } return DialogueLua.GetLocalizedQuestField(questName, entryFieldName).asString; } /// /// Sets the localized quest entry description. /// /// Name of the quest. /// Entry number. /// The quest entry description. public static void SetQuestEntry(string questName, int entryNumber, string description) { string entryFieldName = GetEntryFieldName(entryNumber); DialogueLua.SetLocalizedQuestField(questName, entryFieldName, DialogueLua.DoubleQuotesToSingle(description)); } /// /// Gets the state of the quest entry. /// /// The quest entry state. /// Name of the quest. /// Entry number. public static QuestState GetQuestEntryState(string questName, int entryNumber) { //---Was: string s = DialogueLua.GetQuestField(questName, GetEntryStateFieldName(entryNumber)).AsString; //--- return StringToState(s); return StringToState(CurrentQuestEntryState(questName, entryNumber)); } /// /// Gets the state of the quest entry. /// /// The quest entry state. /// Name of the quest. /// Entry number. public static string CurrentQuestEntryState(string questName, double entryNumber) { if (CurrentQuestEntryStateOverride != null) { return CurrentQuestEntryStateOverride(questName, (int)entryNumber); } else { return DefaultCurrentQuestEntryState(questName, (int)entryNumber); } } /// /// Default built-in implementation of CurrentQuestEntryState. /// public static string DefaultCurrentQuestEntryState(string questName, int entryNumber) { return DialogueLua.GetQuestField(questName, GetEntryStateFieldName((int)entryNumber)).asString; } /// /// Sets the state of the quest entry. /// /// Name of the quest. /// Entry number. /// State. public static void SetQuestEntryState(string questName, int entryNumber, QuestState state) { SetQuestEntryState(questName, entryNumber, StateToString(state)); } /// /// Sets the state of a quest entry. /// /// Name of the quest. /// Entry number. /// State. public static void SetQuestEntryState(string questName, double entryNumber, string state) { if (SetQuestEntryStateOverride != null) { SetQuestEntryStateOverride(questName, (int)entryNumber, state); } else { DefaultSetQuestEntryState(questName, (int)entryNumber, state); } } /// /// Default built-in method to set quest entry state. /// public static void DefaultSetQuestEntryState(string questName, int entryNumber, string state) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { DialogueLua.SetQuestField(questName, GetEntryStateFieldName((int)entryNumber), state); InformQuestStateChange(questName); InformQuestEntryStateChange(questName, (int)entryNumber); SendUpdateTracker(); } else { if (DialogueDebug.logWarnings) Debug.LogWarning("Dialogue System: Quest '" + questName + "' doesn't exist. Can't set entry " + (int)entryNumber + " state to " + state); } } public static string GetEntryFieldName(int entryNumber) { return string.Format("Entry_{0}", new System.Object[] { entryNumber }); } public static string GetEntryStateFieldName(int entryNumber) { return string.Format("Entry_{0}_State", new System.Object[] { entryNumber }); } /// /// Determines if quest tracking is available (that is, if the quest has a "Trackable" field). /// /// true if quest tracking is available; otherwise, false. /// Quest name. public static bool IsQuestTrackingAvailable(string questName) { return DialogueLua.GetQuestField(questName, "Trackable").asBool; } /// /// Specifies whether quest tracking is available (that is, if the quest has a "Trackable" field). /// /// Quest name. /// Trackable or not. public static void SetQuestTrackingAvailable(string questName, bool value) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { DialogueLua.SetQuestField(questName, "Trackable", value); SendUpdateTracker(); } } /// /// Determines if tracking is enabled for a quest. /// /// true if tracking enabled on the specified quest; otherwise, false. /// Quest name. public static bool IsQuestTrackingEnabled(string questName) { return IsQuestTrackingAvailable(questName) ? DialogueLua.GetQuestField(questName, "Track").asBool : false; } /// /// Sets quest tracking on or off (by setting the Track field). If turning on tracking /// and tracking is currently not available (Trackable field is false), this also sets /// the Trackable field true. /// /// Quest name. /// If set to true, tracking is enabled. public static void SetQuestTracking(string questName, bool value) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { if (value == true) { // If only one quest can be tracked at time, untrack all others: if (trackOneQuestAtATime) { var quests = GetAllQuests(); foreach (var otherQuestName in quests) { if (string.Equals(otherQuestName, questName)) continue; if (IsQuestTrackingEnabled(otherQuestName)) { DialogueLua.SetQuestField(otherQuestName, "Track", false); DialogueManager.instance.BroadcastMessage(DialogueSystemMessages.OnQuestTrackingDisabled, otherQuestName, SendMessageOptions.DontRequireReceiver); } } } // Make sure tracking is set to be available for this quest: if (!IsQuestTrackingAvailable(questName)) { SetQuestTrackingAvailable(questName, true); } } // Track this quest: DialogueLua.SetQuestField(questName, "Track", value); SendUpdateTracker(); DialogueManager.instance.BroadcastMessage(value ? DialogueSystemMessages.OnQuestTrackingEnabled : DialogueSystemMessages.OnQuestTrackingDisabled, questName, SendMessageOptions.DontRequireReceiver); } } /// /// Determines if a quest is abandonable (that is, is has a field named "Abandonable" that's true.) /// /// true if the quest is abandonable; otherwise, false. /// Quest name. public static bool IsQuestAbandonable(string questName) { return DialogueLua.GetQuestField(questName, "Abandonable").asBool; } /// /// Returns true if quest has a field named "Visible" that is currently true or doesn't have the field. /// public static bool IsQuestVisible(string questName) { var result = Lua.Run($"return Quest[{DialogueLua.StringToTableIndex(questName)}].Visible").asString; if (string.IsNullOrEmpty(result) || string.Equals(result, "nil")) return true; return string.Compare(result, "false", true) == 0; } /// /// Sets a quest's Visible field true or false. /// public static void SetQuestVisibility(string questName) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { DialogueLua.SetQuestField(questName, "Visible", true); } } /// /// Returns true if quest has a field named "Viewed" that is currently true. /// Used if QuestLogWindow.newQuestText is not blank. /// public static bool WasQuestViewed(string questName) { return DialogueLua.GetQuestField(questName, "Viewed").asBool; } /// /// Marks a quest as viewed (i.e., in the quest log window). /// Generally only set/used when QuestLogWindow.newQuestText is not blank. /// /// public static void MarkQuestViewed(string questName) { if (DialogueLua.DoesTableElementExist("Quest", questName)) { DialogueLua.SetQuestField(questName, "Viewed", true); } } /// /// Gets the group that a quest belongs to. /// /// The quest group name, or empty string if no group. /// Quest name. public static string GetQuestGroup(string questName) { return DialogueLua.GetLocalizedQuestField(questName, "Group").asString; } public static string GetQuestGroupDisplayName(string questName) { var result = DialogueLua.GetLocalizedQuestField(questName, "Group Display Name").asString; if (string.IsNullOrEmpty(result) || result == "nil") result = GetQuestGroup(questName); return result; } /// /// Gets all quest group names. /// /// The group names for active quests, sorted by name. public static string[] GetAllGroups() { return GetAllGroups(QuestState.Active, true); } /// /// Gets all quest group names. /// /// The group names, sorted by name. /// Flags for the quest states to filter. public static string[] GetAllGroups(QuestState flags) { return GetAllGroups(flags, true); } /// /// Gets all quest group names. /// /// The group names. /// Flags for the quest states to filter. /// If set to true sort by group name. public static string[] GetAllGroups(QuestState flags, bool sortByGroupName) { List groups = new List(); LuaTableWrapper itemTable = Lua.Run("return Item").asTable; if (!itemTable.isValid) { if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: Quest Log couldn't access Lua Item[] table. Has the Dialogue Manager loaded a database yet?", new System.Object[] { DialogueDebug.Prefix })); return groups.ToArray(); } foreach (var itemTableValue in itemTable.values) { LuaTableWrapper fields = itemTableValue as LuaTableWrapper; if (fields == null) continue; string questName = null; string group = null; bool isItem = false; try { object questNameObject = fields["Name"]; questName = (questNameObject != null) ? questNameObject.ToString() : string.Empty; object groupObject = fields["Group"]; group = (groupObject != null) ? groupObject.ToString() : string.Empty; isItem = false; object isItemObject = fields["Is_Item"]; if (isItemObject != null) { if (isItemObject.GetType() == typeof(bool)) { isItem = (bool)isItemObject; } else { isItem = Tools.StringToBool(isItemObject.ToString()); } } } catch { } if (!isItem) { if (!groups.Contains(group) && IsQuestInStateMask(questName, flags)) { groups.Add(group); } } } if (sortByGroupName) groups.Sort(); return groups.ToArray(); } /// /// Gets an array of all active quests. /// /// /// The names of all active quests, sorted by Name. /// /// /// string[] activeQuests = QuestLog.GetAllQuests(); /// public static string[] GetAllQuests() { return GetAllQuests(QuestState.Active, true, null); } /// /// Gets an array of all quests matching the specified state bitmask. /// /// The names of all quests matching the specified state bitmask, sorted by Name. /// A bitmask of QuestState values. /// /// string[] completedQuests = QuestLog.GetAllQuests( QuestState.Success | QuestState.Failure ); /// public static string[] GetAllQuests(QuestState flags) { return GetAllQuests(flags, true, null); } /// /// Gets an array of all quests matching the specified state bitmask. /// /// The names of all quests matching the specified state bitmask. /// A bitmask of QuestState values. /// If `true`, sorts the names by name. /// /// string[] completedQuests = QuestLog.GetAllQuests( QuestState.Success | QuestState.Failure, true ); /// public static string[] GetAllQuests(QuestState flags, bool sortByName) { return GetAllQuests(flags, sortByName, null); } /// /// Gets an array of all quests matching the specified state bitmask and in the specified group. /// /// The names of all quests matching the specified state bitmask. /// A bitmask of QuestState values. /// If `true`, sorts the names by name. /// If not null, return only quests in the specified group. /// /// string[] completedQuests = QuestLog.GetAllQuests( QuestState.Success | QuestState.Failure, true ); /// public static string[] GetAllQuests(QuestState flags, bool sortByName, string group) { List questNames = new List(); LuaTableWrapper itemTable = Lua.Run("return Item").asTable; if (!itemTable.isValid) { if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: Quest Log couldn't access Lua Item[] table. Has the Dialogue Manager loaded a database yet?", new System.Object[] { DialogueDebug.Prefix })); return questNames.ToArray(); } var filterGroup = (group != null); foreach (var itemTableValue in itemTable.values) { LuaTableWrapper fields = itemTableValue as LuaTableWrapper; if (fields == null) continue; string questName = null; string thisGroup = null; bool isItem = false; try { object questNameObject = fields["Name"]; questName = (questNameObject != null) ? questNameObject.ToString() : string.Empty; if (filterGroup) { object groupObject = fields["Group"]; thisGroup = (groupObject != null) ? groupObject.ToString() : string.Empty; } isItem = false; object isItemObject = fields["Is_Item"]; if (isItemObject != null) { if (isItemObject.GetType() == typeof(bool)) { isItem = (bool)isItemObject; } else { isItem = Tools.StringToBool(isItemObject.ToString()); } } } catch { } if (!isItem) { if (string.IsNullOrEmpty(questName)) { if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: A quest name (item name in Item[] table) is null or empty", new System.Object[] { DialogueDebug.Prefix })); } else if (!filterGroup || string.Equals(group, thisGroup)) { if (IsQuestInStateMask(questName, flags)) { questNames.Add(questName); } } } } if (sortByName) questNames.Sort(); return questNames.ToArray(); } /// /// Gets all quests (including their group names) in a specified state. /// /// An array of QuestGroupRecord elements. /// A bitmask of QuestState values. /// Sort by group and name. public static QuestGroupRecord[] GetAllGroupsAndQuests(QuestState flags, bool sort = true) { List list = new List(); LuaTableWrapper itemTable = Lua.Run("return Item").asTable; if (!itemTable.isValid) { if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: Quest Log couldn't access Lua Item[] table. Has the Dialogue Manager loaded a database yet?", new System.Object[] { DialogueDebug.Prefix })); return list.ToArray(); } foreach (var itemTableValue in itemTable.values) { LuaTableWrapper fields = itemTableValue as LuaTableWrapper; if (fields == null) continue; string questName = null; string group = null; bool isItem = false; try { object questNameObject = fields["Name"]; questName = (questNameObject != null) ? questNameObject.ToString() : string.Empty; object groupObject = fields["Group"]; group = (groupObject != null) ? groupObject.ToString() : string.Empty; isItem = false; object isItemObject = fields["Is_Item"]; if (isItemObject != null) { if (isItemObject.GetType() == typeof(bool)) { isItem = (bool)isItemObject; } else { isItem = Tools.StringToBool(isItemObject.ToString()); } } } catch { } if (!isItem) { if (string.IsNullOrEmpty(questName)) { if (DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: A quest name (item name in Item[] table) is null or empty", new System.Object[] { DialogueDebug.Prefix })); } else if (IsQuestInStateMask(questName, flags)) { list.Add(new QuestGroupRecord(group, questName)); } } } if (sort) list.Sort(); return list.ToArray(); } /// /// Quest changed delegate. /// public delegate void QuestChangedDelegate(string questName, QuestState newState); /// /// The quest watch item class is used internally by the QuestLog class to manage /// Lua observers on quest states. /// public class QuestWatchItem { private string questName; private int entryNumber; private LuaWatchFrequency frequency; private string luaExpression; private QuestChangedDelegate questChangedHandler; public QuestWatchItem(string questName, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { this.questName = questName; this.entryNumber = 0; this.frequency = frequency; this.luaExpression = string.Format("return Item[\"{0}\"].State", new System.Object[] { DialogueLua.StringToTableIndex(questName) }); this.questChangedHandler = questChangedHandler; DialogueManager.AddLuaObserver(luaExpression, frequency, OnLuaChanged); } public QuestWatchItem(string questName, int entryNumber, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { this.questName = questName; this.entryNumber = entryNumber; this.frequency = frequency; this.luaExpression = string.Format("return Item[\"{0}\"].Entry_{1}_State", new System.Object[] { DialogueLua.StringToTableIndex(questName), entryNumber }); this.questChangedHandler = questChangedHandler; DialogueManager.AddLuaObserver(luaExpression, frequency, OnLuaChanged); } public bool Matches(string questName, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { return string.Equals(questName, this.questName) && (frequency == this.frequency) && (questChangedHandler == this.questChangedHandler); } public bool Matches(string questName, int entryNumber, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { return string.Equals(questName, this.questName) && (entryNumber == this.entryNumber) && (frequency == this.frequency) && (questChangedHandler == this.questChangedHandler); } public void StopObserving() { DialogueManager.RemoveLuaObserver(luaExpression, frequency, OnLuaChanged); } private void OnLuaChanged(LuaWatchItem luaWatchItem, Lua.Result newResult) { if (string.Equals(luaWatchItem.luaExpression, this.luaExpression) && (questChangedHandler != null)) { questChangedHandler(questName, StringToState(newResult.asString)); } } } /// /// The quest watch list. /// private static readonly List questWatchList = new List(); /// /// Adds a quest state observer. /// /// /// Name of the quest. /// /// /// Frequency to check the quest state. /// /// /// Delegate to call when the quest state changes. This should be in the form: /// void MyDelegate(string questName, QuestState newState) {...} /// public static void AddQuestStateObserver(string questName, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { questWatchList.Add(new QuestWatchItem(questName, frequency, questChangedHandler)); } /// /// Adds a quest state observer. /// /// /// Name of the quest. /// /// /// The entry number (1...Entry Count) in the quest. /// /// /// Frequency to check the quest state. /// /// /// Delegate to call when the quest state changes. This should be in the form: /// void MyDelegate(string questName, QuestState newState) {...} /// public static void AddQuestStateObserver(string questName, int entryNumber, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { questWatchList.Add(new QuestWatchItem(questName, entryNumber, frequency, questChangedHandler)); } /// /// Removes a quest state observer. To be removed, the questName, frequency, and delegate must /// all match. /// /// /// Name of the quest. /// /// /// Frequency that the quest state is being checked. /// /// /// Quest changed handler delegate. /// public static void RemoveQuestStateObserver(string questName, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { foreach (var questWatchItem in questWatchList) { if (questWatchItem.Matches(questName, frequency, questChangedHandler)) questWatchItem.StopObserving(); } questWatchList.RemoveAll(questWatchItem => questWatchItem.Matches(questName, frequency, questChangedHandler)); } /// /// Removes a quest state observer. To be removed, the questName, frequency, and delegate must /// all match. /// /// /// Name of the quest. /// /// /// The entry number (1...Entry Count) in the quest. /// /// /// Frequency that the quest state is being checked. /// /// /// Quest changed handler delegate. /// public static void RemoveQuestStateObserver(string questName, int entryNumber, LuaWatchFrequency frequency, QuestChangedDelegate questChangedHandler) { foreach (var questWatchItem in questWatchList) { if (questWatchItem.Matches(questName, entryNumber, frequency, questChangedHandler)) questWatchItem.StopObserving(); } questWatchList.RemoveAll(questWatchItem => questWatchItem.Matches(questName, entryNumber, frequency, questChangedHandler)); } /// /// Removes all quest state observers. /// public static void RemoveAllQuestStateObservers() { foreach (var questWatchItem in questWatchList) { questWatchItem.StopObserving(); } questWatchList.Clear(); } /// /// Updates all quest state listeners who are listening for questName. /// public static void UpdateQuestIndicators(string questName) { var dispatcher = PixelCrushers.GameObjectUtility.FindFirstObjectByType(); if (dispatcher != null) dispatcher.OnQuestStateChange(questName); } } }