update to unity 6.2, add save system and quest system

This commit is contained in:
2025-12-02 16:52:02 -06:00
parent aea12ab091
commit c7dca2f8e5
23 changed files with 767 additions and 48 deletions
+30
View File
@@ -1,9 +1,11 @@
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public DialogueManager DialogueManager { get; private set; }
public Storybools Storybools { get; private set; }
private void Awake()
{
@@ -19,6 +21,34 @@ private void Awake()
DontDestroyOnLoad(gameObject);
DialogueManager = GetComponent<DialogueManager>();
SaveSystem.Load();
}
}
#region Storybool Save/Load
public void SaveStoryBools(ref StoryboolSaveData data)
{
data.Storybools = Instance.Storybools;
}
public void LoadStoryBools(StoryboolSaveData data)
{
if (data.Storybools != null)
{
Instance.Storybools = data.Storybools;
}
else
{
Instance.Storybools = new Storybools();
}
}
#endregion
public int StarShards = 0; // will replace with inventory system
}
[System.Serializable]
public struct StoryboolSaveData
{
public Storybools Storybools;
}
+10 -1
View File
@@ -1,2 +1,11 @@
fileFormatVersion: 2
guid: 65dc1a5d5b17b874087eb45227f838f2
guid: 65dc1a5d5b17b874087eb45227f838f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -1
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+2
View File
@@ -7,6 +7,8 @@ public class PickupableItem : Interactable
public override void Interact()
{
GameManager.Instance.StarShards++; // REMOVE, JUST HERE FOR TESTING BEFORE ADDING INVENTORY SYSTEM
Debug.Log("Picked up " + item.itemName + "!");
MoveOutsideRange(); // I don't love this but if we destroy the object we probably need to do this first
Destroy(gameObject);
+25 -5
View File
@@ -6,6 +6,10 @@ public class PlayerController : MonoBehaviour
public Shovel Shovel;
public float walkSpeed;
float digTime = 0.9f;
float digTimestamp = 0;
bool isDigging = false;
Interactable nearestInteractable;
// Start is called once before the first execution of Update after the MonoBehaviour is created
@@ -17,9 +21,13 @@ void Start()
// Update is called once per frame
void Update()
{
RotatePlayerTowardMouse();
Walk();
TryInteract();
if (!isDigging)
{
RotatePlayerTowardMouse();
Walk();
TryInteract();
}
DigDetector();
}
@@ -31,9 +39,21 @@ void Walk()
void DigDetector()
{
if (Input.GetMouseButtonDown(0))
if (!isDigging)
{
Shovel.Dig();
if (Input.GetMouseButtonDown(0))
{
Shovel.Dig();
isDigging = true;
digTimestamp = Time.time + digTime; // fuck coroutines fr fr
}
}
else
{
if (Time.time > digTimestamp)
{
isDigging = false;
}
}
}
+65
View File
@@ -0,0 +1,65 @@
using UnityEngine;
public class Quest : MonoBehaviour
{
public bool hasStarted = false;
public bool hasCompleted = false;
public string[] askText; // ask the player to complete the task
public string[] duringText; // what to say to the player when the task is complete
public string[] completionText; // what to say to the player upon completion
public int questID = 0; // connects the quest to the registry, which gives us conditions
public void Start()
{
// check if we've completed the quest
hasCompleted = QuestRegistry.Instance.CompletedQuests.Contains(questID);
hasStarted = QuestRegistry.Instance.ActiveQuests.Contains(questID);
// this shouldn't be possible, but if the save is changed manually it could happen
if (hasStarted && hasCompleted)
{
hasStarted = false;
// make sure only one reference exists
QuestRegistry.Instance.CompletedQuests.RemoveAll(id => id == questID);
QuestRegistry.Instance.CompletedQuests.Add(questID);
QuestRegistry.Instance.ActiveQuests.RemoveAll(id => id == questID);
SaveSystem.Save();
}
}
public void StartQuest()
{
hasStarted = true;
QuestRegistry.Instance.ActiveQuests.Add(questID);
SaveSystem.Save();
}
public bool CheckComplete()
{
QuestBool[] conditionList = QuestRegistry.Instance.QuestBoolMap[questID];
if (conditionList != null)
{
foreach (QuestBool condition in conditionList)
{
// try each condition until we hit a false
if (!condition.getValue())
return false;
}
// otherwise return true and mark quest complete
QuestRegistry.Instance.ActiveQuests.Remove(questID);
QuestRegistry.Instance.CompletedQuests.Add(questID);
SaveSystem.Save();
hasCompleted = true;
return true;
}
return false;
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2db38219bed101e44b995a3611e4cedd
+106
View File
@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class QuestRegistry : MonoBehaviour
{
// This will keep a register of all quest data and status
public static QuestRegistry Instance { get; private set; }
public Dictionary<int, QuestBool[]> QuestBoolMap;
public List<int> CompletedQuests;
public List<int> ActiveQuests;
private void Awake()
{
// If there is an instance, and it's not me, delete myself.
if (Instance != null && Instance != this)
{
Destroy(this);
}
else
{
Instance = this;
Instance.QuestBoolMap = PopulateQuestData();
PopulateQuestData();
DontDestroyOnLoad(gameObject);
}
}
private Dictionary<int, QuestBool[]> PopulateQuestData()
{
Dictionary<int, QuestBool[]> questBoolMap = new Dictionary<int, QuestBool[]>();
// Quest Bool Structure: QID<QuestID>_<Bool Number>_<descriptiveName>
// Example: QID11_4_landBurgerGreaseCleared
#region 1 - Sam's Plea
QuestBool[] QID1_List = new QuestBool[2];
// QID1_1_hasEnoughStarshards
QID1_List[0] = new QuestBool(() =>
{
return GameManager.Instance.StarShards > 2;
});
// QID1_2_hasHelpedSam
QID1_List[1] = new QuestBool(() =>
{
return GameManager.Instance.Storybools.hasHelpedSam;
});
questBoolMap.Add(1, QID1_List);
#endregion
return questBoolMap;
}
// Save completed quests to disk
public void SaveQuestData(ref QuestSaveData data)
{
data.ActiveQuests = Instance.ActiveQuests;
data.CompletedQuests = Instance.CompletedQuests;
}
public void LoadQuestData(QuestSaveData data)
{
if (data.ActiveQuests != null)
{
Instance.ActiveQuests = data.ActiveQuests;
}
else
{
Instance.ActiveQuests = new List<int>();
}
if (data.CompletedQuests != null)
{
Instance.CompletedQuests = data.CompletedQuests;
}
else
{
Instance.CompletedQuests = new List<int>();
}
}
}
public class QuestBool
{
// This let's us define complex behavior for quests. We can also store
// raw booleans for this if we have story beats we want to keep track of.
public Func<bool> getValue;
public QuestBool(Func<bool> getValue)
{
this.getValue = getValue;
}
}
[System.Serializable]
public struct QuestSaveData
{
public List<int> CompletedQuests;
public List<int> ActiveQuests;
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf9658f7202522245b3ac80235b98b50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -2
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+83
View File
@@ -0,0 +1,83 @@
using System;
using System.IO;
using UnityEngine;
public class SaveSystem
{
private static SaveData _saveData = new SaveData();
[System.Serializable]
public struct SaveData
{
public QuestSaveData QuestData;
public StoryboolSaveData StoryboolData;
}
public static string SaveFileName()
{
return Application.persistentDataPath + "/save.data";
}
public static void GenerateCorruptedSaveReadme()
{
string path = Application.persistentDataPath + "/README.txt";
string errorText = "Your save was corrupted.\n\n" +
"This can happen when you edit the save manually, or if the game closed while saving. Your\n" +
"corrupted save file has been saved in this directory as 'save.data.backup' if you'd like to\n" +
"try manually fixing it. It should follow standard JSON formatting.\n\n" +
"Once you fix it, just delete the current save.data file, and remove the '.backup' from your \n" +
"old save file. Good luck, and sorry for the inconvenience!";
File.WriteAllText(path, errorText);
}
public static void Save()
{
HandleSaveData();
File.WriteAllText(SaveFileName(), JsonUtility.ToJson(_saveData, true));
}
private static void HandleSaveData()
{
GameManager.Instance.SaveStoryBools(ref _saveData.StoryboolData);
QuestRegistry.Instance.SaveQuestData(ref _saveData.QuestData);
}
public static void Load()
{
string saveContent;
try
{
saveContent = File.ReadAllText(SaveFileName());
}
catch (FileNotFoundException)
{
Save(); // create new save file if one does not exist
saveContent = File.ReadAllText(SaveFileName());
}
try
{
_saveData = JsonUtility.FromJson<SaveData>(saveContent);
}
catch (ArgumentException)
{
// Likely a JSON parse error. Let's back up the old version of the save and
// make a new one for now
File.WriteAllText(SaveFileName() + ".backup", saveContent);
GenerateCorruptedSaveReadme();
Save();
}
HandleLoadData();
}
public static void HandleLoadData()
{
GameManager.Instance.LoadStoryBools(_saveData.StoryboolData);
QuestRegistry.Instance.LoadQuestData(_saveData.QuestData);
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e2f06729fcc0a08408d90d49c2336d22
+10
View File
@@ -0,0 +1,10 @@
using UnityEngine;
[System.Serializable]
public class Storybools
{
// not sure how we'll organize this yet, but right now lets do it by quest
#region QID1
public bool hasHelpedSam = false;
#endregion
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 70e96df6062507943b1f7ae7df89dee5
+70 -3
View File
@@ -3,18 +3,85 @@
public class Talkable : Interactable
{
// this is a character or object that will talk upon interaction
public Dialogue dialogue;
public Dialogue defaultDialogue;
public GameObject talkIndicator;
public Quest[] quests;
Dialogue currentDialogue;
public void Start()
{
talkIndicator.SetActive(false);
}
void DetermineDialogue()
{
// based on if we have any open quests and where we're at in the quest,
// determine what dialogue to show
Quest currentQuest = null;
// Grab the first ACTIVE quest
// If none active, grab the first AVAILABLE quest
// If none available, revert to default dialogue
if (quests.Length > 0)
{
// check for ACTIVE quests
foreach (Quest q in quests)
{
if (q.hasStarted && !q.hasCompleted)
{
currentQuest = q;
}
}
// check for AVAILABLE quests
if (currentQuest == null)
{
foreach (Quest q in quests)
{
if (!q.hasStarted && !q.hasCompleted)
{
currentQuest = q;
}
}
}
}
// revert to default if no suitable quest found
if (currentQuest == null)
{
currentDialogue = defaultDialogue;
}
else
{
currentQuest.CheckComplete();
currentDialogue = new Dialogue();
currentDialogue.charName = defaultDialogue.charName;
// set up dialogue based on quest
if (currentQuest.hasStarted && !currentQuest.hasCompleted)
{
currentDialogue.sentences = currentQuest.duringText;
}
else if (!currentQuest.hasStarted && !currentQuest.hasCompleted)
{
currentDialogue.sentences = currentQuest.askText;
currentQuest.StartQuest();
}
else
{
currentDialogue.sentences = currentQuest.completionText;
}
}
}
public override void Interact()
{
if (!dialogue.isActive)
GameManager.Instance.DialogueManager.StartDialogue(dialogue);
if (currentDialogue == null || !currentDialogue.isActive)
{
DetermineDialogue();
GameManager.Instance.DialogueManager.StartDialogue(currentDialogue);
}
else
GameManager.Instance.DialogueManager.DisplayNextSentence();