Files
2024-11-20 15:21:28 +01:00

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);
}
}
}
}