249 lines
10 KiB
C#
249 lines
10 KiB
C#
// Copyright (c) Pixel Crushers. All rights reserved.
|
|
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace PixelCrushers.QuestMachine
|
|
{
|
|
|
|
/// <summary>
|
|
/// Utility to serialize the minimum data necessary for design-time quests. Only saves:
|
|
/// - Quest giver ID
|
|
/// - Static tags
|
|
/// - Times accepted
|
|
/// - Time last accepted
|
|
/// - Cooldown time remaining
|
|
/// - Show in HUD
|
|
/// - Quest state
|
|
/// - Quest node states [skipped if inactive]
|
|
/// - Counter values [skipped if inactive]
|
|
/// - True condition count on all condition sets [skipped if inactive]
|
|
/// - Quest indicator states [skipped if inactive]
|
|
/// </summary>
|
|
public static class QuestStateSerializer
|
|
{
|
|
|
|
/// <summary>
|
|
/// - Version 2: Added quest condition "alreadyTrue" values.
|
|
/// - Version 1: Use for compatibility with saves made in QM version 1.2.29 or earlier.
|
|
/// </summary>
|
|
public static int version = 2;
|
|
|
|
/// <summary>
|
|
/// Returns minimum save data for a design-time quest.
|
|
/// </summary>
|
|
/// <param name="quest">The quest to serialize.</param>
|
|
/// <returns>A byte array containing states and counter values.</returns>
|
|
public static byte[] Serialize(Quest quest)
|
|
{
|
|
if (quest == null) return null;
|
|
using (var memoryStream = new MemoryStream())
|
|
{
|
|
using (var binaryWriter = new BinaryWriter(memoryStream))
|
|
{
|
|
WriteQuestDataToStream(binaryWriter, quest);
|
|
}
|
|
return memoryStream.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies data from a byte array into an existing design-time quest.
|
|
/// </summary>
|
|
/// <param name="quest">The quest to receive the data.</param>
|
|
/// <param name="bytes">A byte array generated by the Serialize method.</param>
|
|
public static void DeserializeInto(Quest quest, byte[] bytes, bool allowThrowExceptions = false)
|
|
{
|
|
if (quest == null || bytes == null) return;
|
|
using (var memoryStream = new MemoryStream(bytes))
|
|
{
|
|
using (var binaryReader = new BinaryReader(memoryStream))
|
|
{
|
|
try
|
|
{
|
|
ReadQuestDataFromStream(binaryReader, quest);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
if (allowThrowExceptions) throw e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// Save:
|
|
|
|
private static void WriteQuestDataToStream(BinaryWriter binaryWriter, Quest quest)
|
|
{
|
|
if (quest == null) return;
|
|
var state = quest.GetState();
|
|
binaryWriter.Write((byte)state);
|
|
WriteTagDictionaryToStream(binaryWriter, quest.tagDictionary, quest.name);
|
|
binaryWriter.Write(StringField.GetStringValue(quest.questGiverID));
|
|
binaryWriter.Write(quest.timesAccepted);
|
|
quest.UpdateCooldown();
|
|
binaryWriter.Write((double)quest.cooldownSecondsRemaining);
|
|
binaryWriter.Write(quest.showInTrackHUD);
|
|
WriteConditionSetDataToStream(binaryWriter, quest.autostartConditionSet);
|
|
WriteConditionSetDataToStream(binaryWriter, quest.offerConditionSet);
|
|
|
|
// Don't save the info below if waiting to start:
|
|
if (state == QuestState.WaitingToStart && !quest.saveAllIfWaitingToStart) return;
|
|
for (int i = 0; i < quest.counterList.Count; i++)
|
|
{
|
|
binaryWriter.Write(quest.counterList[i].currentValue);
|
|
}
|
|
for (int i = 0; i < quest.nodeList.Count; i++)
|
|
{
|
|
WriteQuestNodeDataToStream(binaryWriter, quest.nodeList[i]);
|
|
}
|
|
WriteQuestIndicatorsToStream(binaryWriter, quest.indicatorStates);
|
|
}
|
|
|
|
private static void WriteQuestNodeDataToStream(BinaryWriter binaryWriter, QuestNode node)
|
|
{
|
|
if (node == null) return;
|
|
var state = node.GetState();
|
|
binaryWriter.Write((byte)state);
|
|
WriteConditionSetDataToStream(binaryWriter, node.conditionSet);
|
|
WriteTagDictionaryToStream(binaryWriter, node.tagDictionary, StringField.GetStringValue(node.internalName));
|
|
}
|
|
|
|
private static void WriteConditionSetDataToStream(BinaryWriter binaryWriter, QuestConditionSet conditionSet)
|
|
{
|
|
if (conditionSet == null) return;
|
|
binaryWriter.Write((byte)conditionSet.numTrueConditions);
|
|
if (version >= 2)
|
|
{
|
|
for (int i = 0; i < conditionSet.conditionList.Count; i++)
|
|
{
|
|
binaryWriter.Write(conditionSet.conditionList[i].alreadyTrue);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteTagDictionaryToStream(BinaryWriter binaryWriter, TagDictionary tags, string questOrNodeName)
|
|
{
|
|
if (tags == null) return;
|
|
binaryWriter.Write(tags.dict.Count);
|
|
foreach (var kvp in tags.dict)
|
|
{
|
|
if (string.IsNullOrEmpty(kvp.Key))
|
|
{
|
|
if (Debug.isDebugBuild) Debug.LogWarning("Quest Machine: While serializing quest tags, found a tag with a blank name in " + questOrNodeName + ".");
|
|
}
|
|
else
|
|
{
|
|
binaryWriter.Write(kvp.Key);
|
|
binaryWriter.Write(kvp.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteQuestIndicatorsToStream(BinaryWriter binaryWriter, Dictionary<string, QuestIndicatorState> indicatorRecords)
|
|
{
|
|
if (indicatorRecords == null) return;
|
|
binaryWriter.Write(indicatorRecords.Count);
|
|
foreach (var kvp in indicatorRecords)
|
|
{
|
|
binaryWriter.Write(kvp.Key);
|
|
binaryWriter.Write((int)kvp.Value);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// Load:
|
|
|
|
private static void ReadQuestDataFromStream(BinaryReader binaryReader, Quest quest)
|
|
{
|
|
if (quest == null) return;
|
|
var state = (QuestState)binaryReader.ReadByte();
|
|
ReadTagDictionaryFromStream(binaryReader, quest.tagDictionary, quest.name);
|
|
quest.questGiverID.value = binaryReader.ReadString();
|
|
quest.timesAccepted = binaryReader.ReadInt32();
|
|
quest.cooldownSecondsRemaining = (float)binaryReader.ReadDouble();
|
|
quest.showInTrackHUD = binaryReader.ReadBoolean();
|
|
ReadConditionSetDataFromStream(binaryReader, quest.autostartConditionSet);
|
|
ReadConditionSetDataFromStream(binaryReader, quest.offerConditionSet);
|
|
if (state == QuestState.WaitingToStart && !quest.saveAllIfWaitingToStart)
|
|
{
|
|
quest.SetState(state, false);
|
|
return;
|
|
}
|
|
|
|
// Don't load the info below if waiting to start:
|
|
for (int i = 0; i < quest.counterList.Count; i++)
|
|
{
|
|
quest.counterList[i].SetValue(binaryReader.ReadInt32(), QuestCounterSetValueMode.DontInformListeners);
|
|
}
|
|
for (int i = 0; i < quest.nodeList.Count; i++)
|
|
{
|
|
ReadQuestNodeDataFromStream(binaryReader, quest.nodeList[i]);
|
|
}
|
|
ReadQuestIndicatorsFromStream(binaryReader, quest.indicatorStates);
|
|
quest.SetRuntimeReferences();
|
|
quest.SetState(state, false);
|
|
QuestMachineMessages.QuestStateChanged(quest, quest.id, quest.GetState());
|
|
}
|
|
|
|
private static void ReadQuestNodeDataFromStream(BinaryReader binaryReader, QuestNode node)
|
|
{
|
|
node.SetState((QuestNodeState)binaryReader.ReadByte(), false);
|
|
ReadConditionSetDataFromStream(binaryReader, node.conditionSet);
|
|
ReadTagDictionaryFromStream(binaryReader, node.tagDictionary, StringField.GetStringValue(node.internalName));
|
|
}
|
|
|
|
private static void ReadConditionSetDataFromStream(BinaryReader binaryReader, QuestConditionSet conditionSet)
|
|
{
|
|
conditionSet.numTrueConditions = binaryReader.ReadByte();
|
|
if (version >= 2)
|
|
{
|
|
for (int i = 0; i < conditionSet.conditionList.Count; i++)
|
|
{
|
|
conditionSet.conditionList[i].alreadyTrue = binaryReader.ReadBoolean();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ReadTagDictionaryFromStream(BinaryReader binaryReader, TagDictionary tags, string questOrNodeName)
|
|
{
|
|
if (tags == null) return;
|
|
tags.dict.Clear();
|
|
var count = binaryReader.ReadInt32();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var key = binaryReader.ReadString();
|
|
var value = binaryReader.ReadString();
|
|
if (string.IsNullOrEmpty(key))
|
|
{
|
|
//---Suppress warning: if (Debug.isDebugBuild) Debug.LogWarning("Quest Machine: While deserializing quest tags, found a tag with a blank name in " + questOrNodeName + ".");
|
|
}
|
|
else if (tags.dict.ContainsKey(key))
|
|
{
|
|
//---Suppress warning: if (Debug.isDebugBuild) Debug.LogWarning("Quest Machine: While deserializing quest tags, found two tags with the name '" + key + "' in " + questOrNodeName + ".");
|
|
}
|
|
else
|
|
{
|
|
tags.dict.Add(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ReadQuestIndicatorsFromStream(BinaryReader binaryReader, Dictionary<string, QuestIndicatorState> indicatorRecords)
|
|
{
|
|
if (indicatorRecords == null) return;
|
|
indicatorRecords.Clear();
|
|
var count = binaryReader.ReadInt32();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var key = binaryReader.ReadString();
|
|
var value = binaryReader.ReadInt32();
|
|
indicatorRecords.Add(key, (QuestIndicatorState)value);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|