Adventure Creator Wikia
Advertisement

AC v1.70 introduced Objectives, which offer a way to provide feedback on the player's progress performing the game's tasks.

Objectives can have multiple states, but themselves are independent from one another.  This script allows you to classify specific Objectives as "Quests", which then depend on sub-Objectives for their completion state.  If all sub-Objectives are complete, the Quest Objective will be complete.  Similarly, if all sub-Objectives are failed, the Quest Objective will be failed.

This behaviour might not be desirable for all situations, so the script is intended to act as a demonstration for how the Objective system can be expanded upon through scripting.

Additionally, the state of all active Quests - and their sub-Objectives - will be condensed into a formatted Global String variable, which can then be e.g. displayed in a Label menu element for player feedback.

To use this script:

  1. In the Inventory Manager's Quest tab, name your sub-Objectives using the convention "QuestTitle/SubObjectiveTitle" (no quotes).  The script works through naming-convention - so a sub-Objective will be automatically classes as such so long as another Objective named "QuestTitle" (or whatever was used to name the sub-Objective) is found.
  2. For each Objective, enter in the text to display into the Description box.
  3. Define a new Global String Variable in the Variables Manager
  4. Paste in the code below into a C# script named QuestSystem.cs
  5. Add a new GameObject to your game's first scene and attach the new Quest System component to it
  6. In the Quest System Inspector, enter in the number of Quests you want - and enter each Quest's ID number into each index entry.  A Quests ID number can be found to the left of its name in the Inventory Manager.
  7. Also in the Inspector, enter in the ID number of your new String variable.  This too will be to the left of its name in the Variables Manager.
  8. (Optional) Create a new Menu with an Appear type set to During Gameplay, position it to the side of the screen, and give it a single Label element.
  9. (Optional) Set that Label element's Label type to Global Variable, and select the String variable created in step 3.  Increase the size of the element so that it's large enough to display the list of objectives

QuestSystem.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace AC
{

public class QuestSystem : MonoBehaviour
{

#region Variables

[SerializeField] private Quest[] quests;
[SerializeField] private int stringVariableID;

#endregion


#region UnityStandards

private void OnEnable ()
{
DontDestroyOnLoad (gameObject);

foreach (Quest quest in quests)
{
quest.GenerateSubIDs ();
}

EventManager.OnObjectiveUpdate += OnObjectiveUpdate;
}


private void OnDisable ()
{
EventManager.OnObjectiveUpdate -= OnObjectiveUpdate;
}

#endregion


#region PrivateFunctions

private void OnObjectiveUpdate (Objective objective, ObjectiveState state)
{
foreach (Quest quest in quests)
{
quest.OnObjectiveUpdate (objective.ID);
}

UpdateActiveQuestsString ();
}


private void UpdateActiveQuestsString ()
{
GVar stringVariable = GlobalVariables.GetVariable (stringVariableID);

if (stringVariable == null)
{
return;
}

string result = string.Empty;

foreach (Quest quest in quests)
{
result += quest.GetActiveString ();
result += "\n";
}

stringVariable.SetStringValue (result);
}

#endregion


[System.Serializable]
private class Quest
{

#region Variables

[SerializeField] private int mainObjectiveID;
private List<int> subObjectiveIDs = new List<int>();

#endregion


#region PublicFunctions

public void GenerateSubIDs ()
{
Objective mainObjective = KickStarter.inventoryManager.GetObjective (mainObjectiveID);
if (mainObjective == null)
{
return;
}

string requiredPrefix = mainObjective.Title + "/";
subObjectiveIDs.Clear ();

foreach (Objective objective in KickStarter.inventoryManager.objectives)
{
if (objective != mainObjective && objective.Title.StartsWith (requiredPrefix))
{
// Is a sub-objective
subObjectiveIDs.Add (objective.ID);
}
}
}


public void OnObjectiveUpdate (int objectiveID)
{
if (!subObjectiveIDs.Contains (objectiveID) ||
!IsValid ())
{
return;
}

AutoUpdateMainState ();
}


public string GetActiveString ()
{
if (!IsValid ())
{
return string.Empty;
}

ObjectiveInstance mainObjectiveInstance = KickStarter.runtimeObjectives.GetObjective (mainObjectiveID);
if (mainObjectiveInstance == null || mainObjectiveInstance.CurrentState.stateType != ObjectiveStateType.Active)
{
return string.Empty;
}

int language = Options.GetLanguage ();
string result = "<b>" + mainObjectiveInstance.Objective.GetDescription (language) + ":</b>";
result += "\n";

foreach (int subObjectiveID in subObjectiveIDs)
{
ObjectiveInstance subObjectiveInstance = KickStarter.runtimeObjectives.GetObjective (subObjectiveID);
if (subObjectiveInstance == null)
{
continue;
}

result += " - " + subObjectiveInstance.Objective.GetDescription (language);

switch (subObjectiveInstance.CurrentState.stateType)
{
case ObjectiveStateType.Fail:
result += " (FAILED)";
break;

case ObjectiveStateType.Complete:
result += " (COMPLETE)";
break;

default:
break;
}

result += "\n";
}

return result;
}

#endregion


#region PrivateFunctions

private void AutoUpdateMainState ()
{
int completedSubObjectives = 0;
int failedSubObjectives = 0;
int activeSubObjectives = 0;

foreach (int subObjectiveID in subObjectiveIDs)
{
ObjectiveState subObjectiveState = KickStarter.runtimeObjectives.GetObjectiveState (subObjectiveID);
if (subObjectiveState == null)
{
continue;
}

switch (subObjectiveState.stateType)
{
case ObjectiveStateType.Active:
activeSubObjectives ++;
break;

case ObjectiveStateType.Complete:
completedSubObjectives ++;
break;

case ObjectiveStateType.Fail:
failedSubObjectives ++;
break;

default:
break;
}
}

int totalSubObjectives = subObjectiveIDs.Count;
Objective mainObjective = KickStarter.inventoryManager.GetObjective (mainObjectiveID);

if (completedSubObjectives == totalSubObjectives)
{
// Completed all sub-objectives, main is completed
SetObjectiveStateType (mainObjective, ObjectiveStateType.Complete);
}
else if (failedSubObjectives > 0)
{
// Failed a sub-objectives, main is failed
SetObjectiveStateType (mainObjective, ObjectiveStateType.Fail);
}
else if (activeSubObjectives > 0)
{
// Main is active
SetObjectiveStateType (mainObjective, ObjectiveStateType.Active);
}
else
{
// Main is inactive
}
}


private void SetObjectiveStateType (Objective objective, ObjectiveStateType stateType)
{
int failedStateID = GetFirstObjectiveStateType (objective, stateType);
if (failedStateID >= 0)
{
KickStarter.runtimeObjectives.SetObjectiveState (objective.ID, failedStateID);
}
}


private int GetFirstObjectiveStateType (Objective objective, ObjectiveStateType stateType)
{
foreach (ObjectiveState state in objective.states)
{
if (state.stateType == stateType)
{
return state.ID;
}
}
return -1;
}


private bool IsValid ()
{
if (mainObjectiveID < 0 ||
subObjectiveIDs.Contains (mainObjectiveID) ||
subObjectiveIDs.Count == 0)
{
return false;
}
return true;
}

#endregion

}

}

}
Advertisement