First commit for private source control. Older commits available on Github.

This commit is contained in:
2026-03-26 12:52:52 +00:00
parent a04c602626
commit 2d449c4a17
2176 changed files with 408185 additions and 0 deletions

BIN
Assets/Scripts/.DS_Store vendored Normal file

Binary file not shown.

3
Assets/Scripts/Data.meta Normal file
View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75e2bfdd1e61468781777eb41e041df7
timeCreated: 1769700576

BIN
Assets/Scripts/Data/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 11e83d25ac154a7c9d8d905c6ba277a7
timeCreated: 1769711173

View File

@@ -0,0 +1,85 @@
using System;
using BriarQueen.Data.Identifiers;
using NaughtyAttributes;
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace BriarQueen.Data.Assets
{
[Serializable]
[CreateAssetMenu(fileName = "New Asset Entry", menuName = "Briar Queen/Assets/New Asset Entry")]
public class AssetEntry : ScriptableObject
{
public enum AssetEntryType
{
None = 0,
UI = 1,
Scene = 2,
Level = 3,
Item = 4
}
[Header("Asset Type")]
[SerializeField]
private AssetEntryType _entryType;
[Header("Asset Key")]
[SerializeField]
[ShowIf(nameof(IsUI))]
private UIKey _uiKey;
[SerializeField]
[ShowIf(nameof(IsScene))]
private SceneKey _sceneKey;
[SerializeField]
[ShowIf(nameof(IsLevel))]
private LevelKey _levelKey;
[SerializeField]
[ShowIf(nameof(IsItem))]
private AssetItemKey _itemKey;
[Header("Addressable Reference")]
[Tooltip("Addressable asset reference to load.")]
[SerializeField]
private AssetReference _asset;
public bool IsUI => _entryType == AssetEntryType.UI;
public bool IsScene => _entryType == AssetEntryType.Scene;
public bool IsLevel => _entryType == AssetEntryType.Level;
public bool IsItem => _entryType == AssetEntryType.Item;
public AssetEntryType EntryType => _entryType;
public UIKey UIKey => _uiKey;
public SceneKey SceneKey => _sceneKey;
public LevelKey LevelKey => _levelKey;
public AssetItemKey ItemKey => _itemKey;
public AssetReference Asset => _asset;
public string AssetKey
{
get
{
return _entryType switch
{
AssetEntryType.UI when _uiKey != UIKey.None =>
AssetKeyIdentifiers.Get(_uiKey),
AssetEntryType.Scene when _sceneKey != SceneKey.None =>
AssetKeyIdentifiers.Get(_sceneKey),
AssetEntryType.Level when _levelKey != LevelKey.None =>
AssetKeyIdentifiers.Get(_levelKey),
AssetEntryType.Item when _itemKey != AssetItemKey.None =>
AssetKeyIdentifiers.Get(_itemKey),
_ => string.Empty
};
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b492dfc759934d71b4f9cc4f8b41a155
timeCreated: 1769711173

View File

@@ -0,0 +1,17 @@
{
"name": "BriarQueen.Data",
"rootNamespace": "BriarQueen",
"references": [
"GUID:776d03a35f1b52c4a9aed9f56d7b4229",
"GUID:9e24947de15b9834991c9d8411ea37cf"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bdf0eff65032c4178bf18aa9c96b1c70
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 42bc99ebdabe4d1fb2065896a1f6e552
timeCreated: 1769703501

View File

@@ -0,0 +1,13 @@
using System.IO;
using UnityEngine;
namespace BriarQueen.Data.IO
{
public static class FilePaths
{
private static readonly string ApplicationData = Application.persistentDataPath;
public static readonly string SaveDataFolder = Path.Combine(ApplicationData, "Saves");
public static readonly string SaveBackupDataFolder = Path.Combine(Application.persistentDataPath, "Backups");
public static readonly string ConfigFolder = Path.Combine(Application.persistentDataPath, "Configs");
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 437ea8a487054abc95c09442f73f0374
timeCreated: 1769703501

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 377fc30dd8794b1a8fa98f9842272263
timeCreated: 1769703058

View File

@@ -0,0 +1,150 @@
// ==============================
// SaveGame.cs (refactored)
// ==============================
using System;
using System.Collections.Generic;
using BriarQueen.Data.Identifiers;
using MemoryPack;
namespace BriarQueen.Data.IO.Saves
{
[Serializable]
[MemoryPackable]
public partial class SaveGame
{
public string SaveVersion = "0.0.2-alpha";
public string SaveFileName;
// Inventory & item tracking
public List<ItemSaveData> InventoryData = new();
public List<ItemSaveData> CollectedItems = new();
public List<ItemSaveData> RemovedItems = new();
public Dictionary<ToolID, bool> Tools = new();
// Codex
public List<CodexSaveData> DiscoveredCodexEntries = new();
// Current location
public string CurrentSceneID;
public string CurrentLevelID;
// Per-puzzle persisted state (resume progress when revisiting)
public List<PuzzleStateSaveData> PuzzleStates = new();
public bool OpeningCinematicPlayed;
// Centralized game variables
public PersistentGameVariables PersistentVariables = new();
// Level-specific hints
public Dictionary<string, int> LevelHintStages = new();
}
[Serializable]
[MemoryPackable]
public partial class ItemSaveData
{
public string UniqueIdentifier;
}
[Serializable]
[MemoryPackable]
public partial class CodexSaveData
{
public string UniqueIdentifier;
}
[Serializable]
[MemoryPackable]
public partial class PuzzleStateSaveData
{
public string PuzzleID;
public byte[] State;
public bool Completed;
}
[Serializable]
[MemoryPackable]
public partial class PersistentGameVariables
{
public GameVariables Game = new();
public TutorialPopupVariables TutorialPopupVariables = new();
}
[Serializable]
[MemoryPackable]
public partial class TutorialPopupVariables
{
// Tracks which popups have been shown
public HashSet<TutorialPopupID> DisplayedPopups = new();
public bool HasBeenDisplayed(TutorialPopupID id)
{
return DisplayedPopups.Contains(id);
}
public void MarkDisplayed(TutorialPopupID id)
{
DisplayedPopups.Add(id);
}
}
[Serializable]
public enum LevelFlag
{
None = 0,
FountainVinesCut,
PumpHouseOpened,
PumpHousePipesFixed,
PumpWaterRestored,
WorkshopBagHoleDug,
WorkshopSafeUnlocked,
WorkshopDownstairsDoorOpen,
WorkshopDownstairsLightOn,
WorkshopGrindstoneRepaired,
StreetGateOpen,
StreetVinesCut,
}
[Serializable]
[MemoryPackable]
public partial class GameVariables
{
// Type-safe level flags using enum
public Dictionary<LevelFlag, bool> LevelFlags = new();
// Tracks completed puzzles
public Dictionary<string, bool> PuzzleCompleted = new();
// Candle slots
public Dictionary<int, string> WorkshopCandleSlotsFilled = new()
{
{ 0, ItemIDs.Pickups[ItemKey.BlueCandle] },
{ 3, ItemIDs.Pickups[ItemKey.OrangeCandle] },
{ 5, ItemIDs.Pickups[ItemKey.RedCandle] }
};
// -------- Helper Methods --------
public bool IsPuzzleCompleted(PuzzleKey puzzle)
{
return PuzzleCompleted.TryGetValue(PuzzleIdentifiers.AllPuzzles[puzzle], out var completed) && completed;
}
public void SetPuzzleCompleted(PuzzleKey puzzle, bool completed = true)
{
PuzzleCompleted[PuzzleIdentifiers.AllPuzzles[puzzle]] = completed;
}
public bool GetLevelFlag(LevelFlag flag)
{
return LevelFlags.TryGetValue(flag, out var value) && value;
}
public void SetLevelFlag(LevelFlag flag, bool value = true)
{
LevelFlags[flag] = value;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bc844b48999c44058fe4696c9841c339
timeCreated: 1769703058

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec4654a3dc1a46858389e602b21615b5
timeCreated: 1769700576

View File

@@ -0,0 +1,9 @@
namespace BriarQueen.Data.Identifiers
{
public enum AchievementID
{
WorkshopSafeUnlocked,
WorkshopPuzzleBoxSolved,
FountainGemPuzzleSolved,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7157dac196b247998ae88da719c9aedc
timeCreated: 1772721612

View File

@@ -0,0 +1,8 @@
namespace BriarQueen.Data.Identifiers
{
public static class ActionMaps
{
public const string GAMEPLAY_MAP = "Gameplay";
public const string UI_MAP = "UI";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 33b4f39a4dbe41acb0a53cdb18e8daaa
timeCreated: 1769708292

View File

@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
namespace BriarQueen.Data.Identifiers
{
public enum UIKey
{
None = 0,
InventorySlot,
CodexLocationButton,
CodexEntryButton
}
public enum SceneKey
{
None = 0,
MainMenuScene,
UIScene,
OpeningCinematicScene,
GameScene
}
[Serializable]
public enum LevelKey
{
None = 0,
ChapterOneVillageEdge,
ChapterOneVillage,
ChapterOneVillageFurther,
ChapterOnePumphouse,
ChapterOneFountain,
ChapterOneWorkshop,
ChapterOnePumphousePipes,
ChapterOneWorkshopDrawer,
ChapterOneWorkshopUpstairs,
ChapterOneWorkshopCandlePuzzle,
ChapterOneWorkshopJewelleryBox,
ChapterOneWorkshopJewelleryBoxOpen,
ChapterOneWorkshopSafe,
ChapterOneWorkshopBag,
ChapterOneWorkshopDownstairs,
ChapterOnePumphouseTable,
ChapterOneWorkshopBookcase,
ChapterOneFountainPuzzle,
ChapterOneStreetGateSign,
ChapterOneStreetCart,
}
public enum AssetItemKey
{
None = 0,
ChapterOneBoxPuzzlePiece1,
ChapterOneBoxPuzzlePiece2,
ChapterOneBoxPuzzlePiece3,
ChapterOneBoxPuzzlePiece4,
ChapterOneBoxPuzzlePiece5,
ChapterOneBoxPuzzlePiece6,
ChapterOneBoxPuzzlePiece7,
ChapterOneBoxPuzzlePiece8,
ChapterOneWorkshopWornBook
}
public static class AssetKeyIdentifiers
{
private const string UI_PREFIX = "UI:";
private const string SCENE_PREFIX = "Scene:";
private const string LEVEL_PREFIX = "Level:";
private const string ITEM_PREFIX = "Item:";
public static string Get(UIKey key)
{
if (key == UIKey.None)
return string.Empty;
return $"{UI_PREFIX}{key}";
}
public static string Get(SceneKey key)
{
if (key == SceneKey.None)
return string.Empty;
return $"{SCENE_PREFIX}{key}";
}
public static string Get(LevelKey key)
{
if (key == LevelKey.None)
return string.Empty;
return $"{LEVEL_PREFIX}{key}";
}
public static string Get(AssetItemKey key)
{
if (key == AssetItemKey.None)
return string.Empty;
return $"{ITEM_PREFIX}{key}";
}
public static bool TryGet(UIKey key, out string value)
{
value = Get(key);
return !string.IsNullOrEmpty(value);
}
public static bool TryGet(SceneKey key, out string value)
{
value = Get(key);
return !string.IsNullOrEmpty(value);
}
public static bool TryGet(LevelKey key, out string value)
{
value = Get(key);
return !string.IsNullOrEmpty(value);
}
public static bool TryGet(AssetItemKey key, out string value)
{
value = Get(key);
return !string.IsNullOrEmpty(value);
}
public static IEnumerable<string> GetAllKeys()
{
foreach (UIKey key in Enum.GetValues(typeof(UIKey)))
if (key != UIKey.None)
yield return Get(key);
foreach (SceneKey key in Enum.GetValues(typeof(SceneKey)))
if (key != SceneKey.None)
yield return Get(key);
foreach (LevelKey key in Enum.GetValues(typeof(LevelKey)))
if (key != LevelKey.None)
yield return Get(key);
foreach (AssetItemKey key in Enum.GetValues(typeof(AssetItemKey)))
if (key != AssetItemKey.None)
yield return Get(key);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 70c2ada094a34e04b4f5d7d1cc2c5327
timeCreated: 1769711192

View File

@@ -0,0 +1,112 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace BriarQueen.Data.Identifiers
{
public enum MusicKey
{
None = 0,
}
public enum SFXKey
{
None = 0,
WorkshopSafeUnlocked,
WorkshopPuzzleBoxUnlocked,
DoorCreek,
ItemCollected,
GateOpening,
PuzzleIncorrect,
ResetPuzzle,
SharpenKnife
}
public enum UIFXKey
{
None = 0,
AchievementUnlocked,
CodexEntryUnlocked
}
public enum AmbienceKey
{
None = 0,
}
public enum VoiceKey
{
None = 0,
}
public static class AudioNameIdentifiers
{
public static readonly IReadOnlyDictionary<MusicKey, string> Music =
new ReadOnlyDictionary<MusicKey, string>(
new Dictionary<MusicKey, string>
{
// Add when you have music
// { MusicKey.SomeTrack, "MUSIC_SomeTrack" }
});
public static readonly IReadOnlyDictionary<SFXKey, string> SFX =
new ReadOnlyDictionary<SFXKey, string>(
new Dictionary<SFXKey, string>
{
{ SFXKey.WorkshopSafeUnlocked, "SFX_WorkshopSafeUnlocked" },
{ SFXKey.WorkshopPuzzleBoxUnlocked, "SFX_WorkshopPuzzleBoxUnlocked" },
{ SFXKey.DoorCreek, "SFX_DoorCreek" },
{ SFXKey.ItemCollected, "SFX_ItemCollected" },
{ SFXKey.GateOpening, "SFX_GateOpening" },
{ SFXKey.PuzzleIncorrect, "SFX_PuzzleIncorrect" },
{ SFXKey.ResetPuzzle, "SFX_ResetPuzzle" },
{ SFXKey.SharpenKnife, "SFX_SharpenKnife"}
});
public static readonly IReadOnlyDictionary<UIFXKey, string> UIFX =
new ReadOnlyDictionary<UIFXKey, string>(
new Dictionary<UIFXKey, string>
{
{ UIFXKey.AchievementUnlocked, "UIFX_AchievementUnlocked" },
{ UIFXKey.CodexEntryUnlocked, "UIFX_CodexEntryUnlocked" },
});
public static readonly IReadOnlyDictionary<AmbienceKey, string> Ambience =
new ReadOnlyDictionary<AmbienceKey, string>(
new Dictionary<AmbienceKey, string>
{
// Add ambience mappings here
});
public static readonly IReadOnlyDictionary<VoiceKey, string> Voice =
new ReadOnlyDictionary<VoiceKey, string>(
new Dictionary<VoiceKey, string>
{
// Add voice mappings here
});
public static string Get(MusicKey key)
{
return Music.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(SFXKey key)
{
return SFX.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(UIFXKey key)
{
return UIFX.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(AmbienceKey key)
{
return Ambience.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(VoiceKey key)
{
return Voice.TryGetValue(key, out var value) ? value : string.Empty;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 07b91c112560408d842204fefd8f68db
timeCreated: 1769802314

View File

@@ -0,0 +1,22 @@
namespace BriarQueen.Data.Identifiers
{
public static class AudioMixerParameters
{
public const string MASTER_VOLUME = "Master_Volume";
public const string MUSIC_VOLUME = "Music_Volume";
public const string SFX_VOLUME = "SFX_Volume";
public const string AMBIENCE_VOLUME = "Ambience_Volume";
public const string VOICE_VOLUME = "Voice_Volume";
public const string UI_VOLUME = "UI_Volume";
}
public static class AudioMixerGroups
{
public const string MASTER_GROUP = "Master";
public const string MUSIC_GROUP = "Music";
public const string SFX_GROUP = "SFX";
public const string AMBIENCE_GROUP = "Ambience";
public const string VOICE_GROUP = "Voice";
public const string UI_GROUP = "UI";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ad39fe6d08874fe69d0acf0a6e8a195e
timeCreated: 1769802894

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace BriarQueen.Data.Identifiers
{
public enum BookEntryID
{
None = 0,
WorkshopDiary = 1
}
public enum ClueEntryID
{
None = 0,
WorkshopBookshelfClue = 1,
WorkshopRainbowClue = 2,
PumphouseScratchedTable = 3
}
public enum PhotoEntryID
{
None = 0,
WorkshopFadedPhoto = 1
}
public enum LocationEntryID
{
None = 0,
Village = 1,
Workshop = 2
}
public static class CodexEntryIDs
{
public static readonly IReadOnlyDictionary<BookEntryID, string> Books =
new ReadOnlyDictionary<BookEntryID, string>(
new Dictionary<BookEntryID, string>
{
{ BookEntryID.WorkshopDiary, "BOOK_WorkshopDiary" }
});
public static readonly IReadOnlyDictionary<ClueEntryID, string> Clues =
new ReadOnlyDictionary<ClueEntryID, string>(
new Dictionary<ClueEntryID, string>
{
{ ClueEntryID.WorkshopBookshelfClue, "CLUE_WorkshopBookshelf" },
{ ClueEntryID.WorkshopRainbowClue , "CLUE_WorkshopRainbow" }
});
public static readonly IReadOnlyDictionary<PhotoEntryID, string> Photos =
new ReadOnlyDictionary<PhotoEntryID, string>(
new Dictionary<PhotoEntryID, string>
{
{ PhotoEntryID.WorkshopFadedPhoto, "PHOTO_WorkshopFadedPhoto" }
});
public static string Get(BookEntryID id)
{
return Books.TryGetValue(id, out var value) ? value : string.Empty;
}
public static string Get(ClueEntryID id)
{
return Clues.TryGetValue(id, out var value) ? value : string.Empty;
}
public static string Get(PhotoEntryID id)
{
return Photos.TryGetValue(id, out var value) ? value : string.Empty;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0acfa8405a384e76926ac6fd1cdf3542
timeCreated: 1773682133

View File

@@ -0,0 +1,10 @@
namespace BriarQueen.Data.Identifiers
{
public enum CodexType
{
None = 0,
BookEntry = 1,
PuzzleClue = 2,
Photo = 3
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1f418d7b7d864488b8fde53b789dc51c
timeCreated: 1773834385

View File

@@ -0,0 +1,136 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace BriarQueen.Data.Identifiers
{
public enum ItemInteractKey
{
None = 0,
EmptyHands = 1,
CantUseItem = 2,
RustyKnife = 3,
SomethingMissing = 4,
PliersSnapped = 5,
CarefulInteract = 6,
RagFallsApart = 7,
LooksImportant = 8,
WrongTool = 9,
}
public enum LevelInteractKey
{
None = 0,
WaterValve = 1,
ClearVinesOutside = 2,
PumphouseChain = 3,
CutVines = 4,
WorkshopLockedSafe = 5,
UnlockedPumphouse = 6,
}
public enum EnvironmentInteractKey
{
None = 0,
BrokenLantern = 1,
WorkshopWriting = 2,
UseGrindstone = 3,
WorkshopBookDisintegrating = 4,
UsingKnife = 5,
AlreadySharpened = 6,
Locked = 7,
CantGoThere = 8,
DirtyWindow = 9,
WorkshopBagNoItems = 10,
FindCandle = 11,
DoesntBelong = 12,
SharpGlass = 13,
FreshAndCoolWater = 14,
WorkshopBooks = 15,
PumpTurnOn = 16,
}
public enum UIInteractKey
{
None = 0,
EmptySlot = 1,
}
public static class InteractEventIDs
{
public static readonly IReadOnlyDictionary<ItemInteractKey, string> ItemInteractions =
new ReadOnlyDictionary<ItemInteractKey, string>(
new Dictionary<ItemInteractKey, string>
{
{ ItemInteractKey.EmptyHands, "I need to put my tools away." },
{ ItemInteractKey.CantUseItem, "That won't work here." },
{ ItemInteractKey.RustyKnife, "It's too blunt to be useful." },
{ ItemInteractKey.SomethingMissing, "Something's missing." },
{ ItemInteractKey.PliersSnapped, "The pliers snapped. They're no use now." },
{ ItemInteractKey.CarefulInteract, "I need to be careful with this." },
{ ItemInteractKey.RagFallsApart, "The rag fell apart." },
{ ItemInteractKey.LooksImportant, "That looks important." },
{ ItemInteractKey.WrongTool, "I need the proper tool for this."}
});
public static readonly IReadOnlyDictionary<LevelInteractKey, string> LevelInteractions =
new ReadOnlyDictionary<LevelInteractKey, string>(
new Dictionary<LevelInteractKey, string>
{
{ LevelInteractKey.WaterValve, "I've already turned the water on." },
{ LevelInteractKey.ClearVinesOutside, "I need to clear the vines outside first." },
{ LevelInteractKey.PumphouseChain, "There must be a key around here somewhere." },
{ LevelInteractKey.CutVines, "I need something to cut through these." },
{ LevelInteractKey.WorkshopLockedSafe, "It's locked tight." },
{ LevelInteractKey.UnlockedPumphouse, "You used the Pumphouse Key."},
});
public static readonly IReadOnlyDictionary<EnvironmentInteractKey, string> EnvironmentInteractions =
new ReadOnlyDictionary<EnvironmentInteractKey, string>(
new Dictionary<EnvironmentInteractKey, string>
{
{ EnvironmentInteractKey.BrokenLantern, "It's too broken to use." },
{ EnvironmentInteractKey.WorkshopWriting, "It could be worse... it could be blood." },
{ EnvironmentInteractKey.UseGrindstone, "I could sharpen something on this." },
{ EnvironmentInteractKey.WorkshopBookDisintegrating, "It fell apart in my hands." },
{ EnvironmentInteractKey.UsingKnife, "I should be careful cutting these." },
{ EnvironmentInteractKey.AlreadySharpened, "That should be sharp enough now." },
{ EnvironmentInteractKey.Locked, "It's locked." },
{ EnvironmentInteractKey.CantGoThere, "I can't go that way." },
{ EnvironmentInteractKey.DirtyWindow, "I can't see through all this grime." },
{ EnvironmentInteractKey.WorkshopBagNoItems, "There's nothing left inside." },
{ EnvironmentInteractKey.FindCandle, "I should look for the candle." },
{ EnvironmentInteractKey.DoesntBelong, "That doesn't belong here." },
{ EnvironmentInteractKey.SharpGlass, "Ow... that's sharp." },
{ EnvironmentInteractKey.FreshAndCoolWater, "The water feels cool and refreshing." },
{ EnvironmentInteractKey.WorkshopBooks, "The books are ancient and crumbling." },
{ EnvironmentInteractKey.PumpTurnOn, "The water pumps splutter into life."}
});
public static readonly IReadOnlyDictionary<UIInteractKey, string> UIInteractions =
new ReadOnlyDictionary<UIInteractKey, string>(
new Dictionary<UIInteractKey, string>
{
{ UIInteractKey.EmptySlot, "Empty slot." },
});
public static string Get(ItemInteractKey key)
{
return ItemInteractions.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(LevelInteractKey key)
{
return LevelInteractions.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(EnvironmentInteractKey key)
{
return EnvironmentInteractions.TryGetValue(key, out var value) ? value : string.Empty;
}
public static string Get(UIInteractKey key)
{
return UIInteractions.TryGetValue(key, out var value) ? value : string.Empty;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50e79aae8ef3433e835ccf3cdb6cf186
timeCreated: 1773862120

View File

@@ -0,0 +1,158 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace BriarQueen.Data.Identifiers
{
public enum ItemKey
{
None = 0,
RustedKnife = 1,
SharpenedKnife = 2,
EmeraldAmulet = 3,
DustyMirror = 4,
SmallRag = 5,
GreenCandle = 6,
IndigoCandle = 7,
DirtyMagnifyingGlass = 8,
PumphouseKey = 9,
RedCandle = 10,
OrangeCandle = 11,
YellowCandle = 12,
BlueCandle = 13,
VioletCandle = 14,
Pliers = 15,
Emerald = 16,
Sapphire = 17,
Ruby = 18,
RubyRing = 19,
SilverCoin = 20,
GoldCoin = 21,
GrindstoneAxlePin = 22,
Diamond = 23,
DiamondTiara = 24,
DustySapphire = 25,
}
public enum EnvironmentKey
{
None = 0,
ChainLock = 1,
FountainVines = 2,
GrindingStone = 3,
WorkshopWindow = 4,
WorkshopDamagedBook = 5,
WorkshopBookSlot = 6,
WorkshopDiary = 7,
PumphouseWaterValve = 8,
WorkshopFadedPhoto = 9,
WorkshopDownstairsLight = 10,
WorkshopBrokenLantern = 11,
WorkshopWriting = 12,
StreetVines = 13,
}
public enum PuzzleSlotKey
{
None = 0,
WorkshopCandleSlot = 1,
WorkshopPuzzleBoxSlot = 2,
FountainGemSlot = 3,
}
public static class ItemIDs
{
public static readonly IReadOnlyDictionary<PuzzleSlotKey, string> PuzzleSlots =
new ReadOnlyDictionary<PuzzleSlotKey, string>(
new Dictionary<PuzzleSlotKey, string>
{
{ PuzzleSlotKey.WorkshopCandleSlot, "PUZ_WorkshopCandleSlot" },
{ PuzzleSlotKey.WorkshopPuzzleBoxSlot, "PUZ_WorkshopPuzzleBoxSlot" }
});
public static readonly IReadOnlyDictionary<EnvironmentKey, string> Environment =
new ReadOnlyDictionary<EnvironmentKey, string>(
new Dictionary<EnvironmentKey, string>
{
{ EnvironmentKey.ChainLock, "ENV_ChainLock" },
{ EnvironmentKey.FountainVines, "ENV_FountainVines" },
{ EnvironmentKey.GrindingStone, "ENV_GrindingStone" },
{ EnvironmentKey.WorkshopWindow, "ENV_WorkshopWindow" },
{ EnvironmentKey.WorkshopDamagedBook, "ENV_WorkshopDamagedBook" },
{ EnvironmentKey.WorkshopBookSlot, "ENV_WorkshopBookSlot" },
{ EnvironmentKey.WorkshopDiary, "ENV_WorkshopDiary" },
{ EnvironmentKey.PumphouseWaterValve, "ENV_PumphouseWaterValve" },
{ EnvironmentKey.WorkshopFadedPhoto, "ENV_WorkshopFadedPhoto" },
{ EnvironmentKey.WorkshopDownstairsLight, "ENV_WorkshopDownstairsLight" },
{ EnvironmentKey.WorkshopBrokenLantern, "ENV_WorkshopBrokenLantern" },
{ EnvironmentKey.WorkshopWriting, "ENV_WorkshopWriting" },
{ EnvironmentKey.StreetVines, "ENV_StreetVines" },
});
public static readonly IReadOnlyDictionary<ItemKey, string> Pickups =
new ReadOnlyDictionary<ItemKey, string>(
new Dictionary<ItemKey, string>
{
{ ItemKey.RustedKnife, "00_RustedKnife" },
{ ItemKey.SharpenedKnife, "01_SharpenedKnife" },
{ ItemKey.EmeraldAmulet, "02_EmeraldAmulet" },
{ ItemKey.DustyMirror, "03_DustyMirror" },
{ ItemKey.SmallRag, "04_SmallRag" },
{ ItemKey.GreenCandle, "05_GreenCandle" },
{ ItemKey.IndigoCandle, "06_IndigoCandle" },
{ ItemKey.DirtyMagnifyingGlass, "07_DirtyMagnifyingGlass" },
{ ItemKey.PumphouseKey, "08_PumphouseKey" },
{ ItemKey.RedCandle, "09_RedCandle" },
{ ItemKey.OrangeCandle, "10_OrangeCandle" },
{ ItemKey.YellowCandle, "11_YellowCandle" },
{ ItemKey.BlueCandle, "12_BlueCandle" },
{ ItemKey.VioletCandle, "13_VioletCandle" },
{ ItemKey.Pliers, "14_Pliers" },
{ ItemKey.Emerald, "15_Emerald" },
{ ItemKey.Sapphire, "16_Sapphire" },
{ ItemKey.Ruby, "17_Ruby" },
{ ItemKey.RubyRing, "18_RubyRing" },
{ ItemKey.SilverCoin, "19_SilverCoin" },
{ ItemKey.GoldCoin, "20_GoldCoin" },
{ ItemKey.GrindstoneAxlePin, "21_GrindstoneAxlePin" },
{ ItemKey.Diamond, "22_Diamond" },
{ ItemKey.DiamondTiara, "23_DiamondTiara" },
{ ItemKey.DustySapphire, "24_DustySapphire" },
});
public static string Get(ItemKey key)
{
return Pickups.TryGetValue(key, out var id) ? id : string.Empty;
}
public static string Get(EnvironmentKey key)
{
return Environment.TryGetValue(key, out var id) ? id : string.Empty;
}
public static string Get(PuzzleSlotKey key)
{
return PuzzleSlots.TryGetValue(key, out var id) ? id : string.Empty;
}
public static bool TryGet(ItemKey key, out string id)
{
return Pickups.TryGetValue(key, out id);
}
public static bool TryGet(EnvironmentKey key, out string id)
{
return Environment.TryGetValue(key, out id);
}
public static bool TryGet(PuzzleSlotKey key, out string id)
{
return PuzzleSlots.TryGetValue(key, out id);
}
public static IEnumerable<string> GetAllItemIDs()
{
return Pickups.Values;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c40ae7704af14f6e99941aba25344bb6
timeCreated: 1769700576

View File

@@ -0,0 +1,9 @@
namespace BriarQueen.Data.Identifiers
{
public enum Location
{
None = 0,
Village = 1,
Workshop = 2
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1954efce3788423dbb70477d39ae226c
timeCreated: 1773685340

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace BriarQueen.Data.Identifiers
{
public enum PuzzleKey
{
WorkshopCandlePuzzle,
WorkshopPuzzleBox,
FountainGemPuzzle,
}
public static class PuzzleIdentifiers
{
public static readonly Dictionary<PuzzleKey, string> AllPuzzles = new()
{
{ PuzzleKey.WorkshopCandlePuzzle, "CH1:Puzzle:WorkshopCandles" },
{ PuzzleKey.WorkshopPuzzleBox, "CH1:Puzzle:WorkshopBox" },
{ PuzzleKey.FountainGemPuzzle , "CH1:Puzzle:FountainGems" },
};
// Optional helper to get all puzzle IDs
public static IEnumerable<string> GetAllPuzzleIDs()
{
return AllPuzzles.Values;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 06b4a47cafea4e5ea0f02896de78ac30
timeCreated: 1769876815

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace BriarQueen.Data.Identifiers
{
public enum SubtitleKey
{
None = 0,
// Example:
// IntroLine = 1,
// TutorialTip = 2
}
public static class SubtitleIdentifiers
{
public static readonly IReadOnlyDictionary<SubtitleKey, string> Subtitles =
new ReadOnlyDictionary<SubtitleKey, string>(
new Dictionary<SubtitleKey, string>
{
// { SubtitleKey.IntroLine, "SUB_IntroLine" },
// { SubtitleKey.TutorialTip, "SUB_TutorialTip" }
});
public static string Get(SubtitleKey key)
{
return Subtitles.TryGetValue(key, out var value) ? value : string.Empty;
}
public static IEnumerable<string> GetAll()
{
return Subtitles.Values;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edc4b3fdb3c844a280d7ffeb90c8395f
timeCreated: 1769802348

View File

@@ -0,0 +1,8 @@
namespace BriarQueen.Data.Identifiers
{
public enum ToolID
{
None = 0,
Knife = 1
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5c555346b81049da9f0203c0143823c7
timeCreated: 1773955001

View File

@@ -0,0 +1,11 @@
namespace BriarQueen.Data.Identifiers
{
public enum TrackType
{
Music = 0,
Ambience = 1,
Sfx = 2,
Voice = 3,
UIFX = 4
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4eebcb38d8494fd3874dff753a742b82
timeCreated: 1773850029

View File

@@ -0,0 +1,85 @@
using System.Collections.Generic;
namespace BriarQueen.Data.Identifiers
{
public enum TutorialPopupID
{
ReturnToPreviousLevel,
UsingItemsTogether,
HideHUD,
ExitItems,
MultipleUseItems,
DarkRooms,
Codex,
HiddenItems,
ResetPuzzles,
Tools,
ItemsAway,
ItemCycling,
ToolCycling,
}
public static class TutorialPopupTexts
{
public static readonly Dictionary<TutorialPopupID, string> AllPopups = new()
{
{
TutorialPopupID.ReturnToPreviousLevel,
"Click the bottom corners to go back the way you came."
},
{
TutorialPopupID.UsingItemsTogether,
"Select an item, then click another to use it on that object."
},
{
TutorialPopupID.HideHUD,
"Press 'H' to hide the HUD and see the world more clearly."
},
{
TutorialPopupID.ExitItems,
"Right-click to exit the current interaction."
},
{
TutorialPopupID.MultipleUseItems,
"Some items can be used more than once, but will eventually wear out."
},
{
TutorialPopupID.DarkRooms,
"Dark rooms hide what matters. Look carefully—light reveals what they conceal."
},
{
TutorialPopupID.Codex,
"New discoveries are added to your codex. Press 'C' to review what you've gathered."
},
{
TutorialPopupID.HiddenItems,
"Some things are hidden on purpose. Search carefully to uncover them."
},
{
TutorialPopupID.ResetPuzzles,
"Some puzzles can be reset if you make a mistake."
},
{
TutorialPopupID.Tools,
"You'll find tools as you explore. Each has its own purpose—try them on different objects. Press 'Y' to view your tools."
},
{
TutorialPopupID.ItemsAway,
"Right-click to put away any items."
},
{
TutorialPopupID.ItemCycling,
"Press '[' or ']' to cycle through your backpack."
},
{
TutorialPopupID.ToolCycling,
"Press 'Q' or 'E' to cycle through your tools."
}
};
public static IEnumerable<string> GetAllTexts()
{
return AllPopups.Values;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 96daeae63db0442bb8449e31e5867713
timeCreated: 1772823170

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 618d29950c9c4d518c6b56949f287669
timeCreated: 1771172163

View File

@@ -0,0 +1,19 @@
{
"name": "BriarQueen.Editor",
"rootNamespace": "BriarQueen",
"references": [
"GUID:bdf0eff65032c4178bf18aa9c96b1c70",
"GUID:ac1be664c635c449eb9f3f52cf5c97f5"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d63909dd781c643ecbc1295f0dc761a3
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1954361a8cce45498b2c8b2888144b5e
timeCreated: 1771172177

View File

@@ -0,0 +1,79 @@
using System;
using System.Linq;
using BriarQueen.Framework.Managers.Interaction.Data;
using UnityEditor;
using UnityEngine;
namespace BriarQueen.Editor.Drawers
{
[CustomPropertyDrawer(typeof(BaseInteraction), true)]
public class BaseItemInteractionDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
if (property.managedReferenceValue == null)
{
if (GUI.Button(position, "Select Interaction Type"))
{
ShowTypeMenu(property);
}
EditorGUI.EndProperty();
return;
}
var type = property.managedReferenceValue.GetType();
var headerRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
if (GUI.Button(headerRect, type.Name, EditorStyles.popup))
{
ShowTypeMenu(property);
}
var bodyRect = new Rect(
position.x,
position.y + EditorGUIUtility.singleLineHeight + 2,
position.width,
position.height - EditorGUIUtility.singleLineHeight - 2);
EditorGUI.indentLevel++;
EditorGUI.PropertyField(bodyRect, property, GUIContent.none, true);
EditorGUI.indentLevel--;
EditorGUI.EndProperty();
}
private void ShowTypeMenu(SerializedProperty property)
{
var menu = new GenericMenu();
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t =>
!t.IsAbstract &&
typeof(BaseInteraction).IsAssignableFrom(t));
foreach (var type in types)
{
menu.AddItem(new GUIContent(type.Name), false, () =>
{
property.serializedObject.Update();
property.managedReferenceValue = Activator.CreateInstance(type);
property.serializedObject.ApplyModifiedProperties();
});
}
menu.ShowAsContext();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (property.managedReferenceValue == null)
return EditorGUIUtility.singleLineHeight;
return EditorGUI.GetPropertyHeight(property, true) + EditorGUIUtility.singleLineHeight + 4;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 627cc55408bb4796839600a1516a5eba
timeCreated: 1771172177

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5c40c1262cbd4128a9b3c3446304a845
timeCreated: 1773921681

View File

@@ -0,0 +1,74 @@
using System.Collections.Generic;
using BriarQueen.Data.Assets;
using BriarQueen.Framework.Registries;
using UnityEditor;
using UnityEngine;
namespace BriarQueen.Editor.Drawers.Registries
{
[CustomEditor(typeof(AssetRegistry))]
public class AssetRegistryFill : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space();
if (GUILayout.Button("Discover AssetEntry Assets"))
{
Populate((AssetRegistry)target);
}
}
private static void Populate(AssetRegistry registry)
{
string[] guids = AssetDatabase.FindAssets("t:AssetEntry");
var scenes = new List<AssetEntry>();
var levels = new List<AssetEntry>();
var items = new List<AssetEntry>();
var ui = new List<AssetEntry>();
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var entry = AssetDatabase.LoadAssetAtPath<AssetEntry>(path);
if (entry == null)
continue;
switch (entry.EntryType)
{
case AssetEntry.AssetEntryType.Scene:
scenes.Add(entry);
break;
case AssetEntry.AssetEntryType.Level:
levels.Add(entry);
break;
case AssetEntry.AssetEntryType.Item:
items.Add(entry);
break;
case AssetEntry.AssetEntryType.UI:
ui.Add(entry);
break;
}
}
scenes.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
levels.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
items.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
ui.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
Undo.RecordObject(registry, "Discover AssetEntry Assets");
registry.SetDiscoveredEntries(scenes, levels, items, ui);
EditorUtility.SetDirty(registry);
AssetDatabase.SaveAssets();
Debug.Log($"[AssetRegistry] Discovery complete. Scenes: {scenes.Count}, Levels: {levels.Count}, Items: {items.Count}, UI: {ui.Count}");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f49114100e741bb849cbdfb5fc457cc
timeCreated: 1773602794

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using BriarQueen.Data.Identifiers;
using BriarQueen.Framework.Managers.Player.Data;
using BriarQueen.Framework.Registries;
using UnityEditor;
using UnityEngine;
namespace BriarQueen.Editor.Drawers.Registries
{
[CustomEditor(typeof(CodexRegistry))]
public class CodexRegistryFill : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space();
if (GUILayout.Button("Discover Codex Entry Assets"))
{
Populate((CodexRegistry)target);
}
}
private static void Populate(CodexRegistry registry)
{
string[] guids = AssetDatabase.FindAssets("t:CodexEntrySo");
var books = new List<CodexEntrySo>();
var clues = new List<CodexEntrySo>();
var photos = new List<CodexEntrySo>();
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var entry = AssetDatabase.LoadAssetAtPath<CodexEntrySo>(path);
if (entry == null)
continue;
switch (entry.EntryType)
{
case CodexType.BookEntry:
books.Add(entry);
break;
case CodexType.PuzzleClue:
clues.Add(entry);
break;
case CodexType.Photo:
photos.Add(entry);
break;
}
}
books.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
clues.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
photos.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
Undo.RecordObject(registry, "Discover Codex Assets");
registry.SetDiscoveredEntries(books, clues, photos);
EditorUtility.SetDirty(registry);
AssetDatabase.SaveAssets();
Debug.Log($"[AssetRegistry] Discovery complete. Books: {books.Count}, Clues: {clues.Count}, Photos: {photos.Count}");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 04adf387d1e94429a6c320005150cb90
timeCreated: 1773921705

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using BriarQueen.Framework.Managers.Player.Data;
using BriarQueen.Framework.Registries;
using UnityEditor;
using UnityEngine;
namespace BriarQueen.Editor.Drawers.Registries
{
[CustomEditor(typeof(ItemRegistry))]
public class ItemRegistryFill : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space();
if (GUILayout.Button("Discover Item Assets"))
{
Populate((ItemRegistry)target);
}
}
private static void Populate(ItemRegistry registry)
{
string[] guids = AssetDatabase.FindAssets("t:ItemDataSo");
var puzzleSlots = new List<ItemDataSo>();
var pickupItems = new List<ItemDataSo>();
var environmentInteractables = new List<ItemDataSo>();
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var entry = AssetDatabase.LoadAssetAtPath<ItemDataSo>(path);
if (entry == null)
continue;
switch (entry.IdType)
{
case ItemDataSo.ItemIdType.PuzzleSlot:
puzzleSlots.Add(entry);
break;
case ItemDataSo.ItemIdType.Pickup:
pickupItems.Add(entry);
break;
case ItemDataSo.ItemIdType.Environment:
environmentInteractables.Add(entry);
break;
}
}
puzzleSlots.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
pickupItems.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
environmentInteractables.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
Undo.RecordObject(registry, "Discover Item Assets");
registry.SetDiscoveredEntries(puzzleSlots, pickupItems, environmentInteractables);
EditorUtility.SetDirty(registry);
AssetDatabase.SaveAssets();
Debug.Log(
$"[ItemRegistry] Discovery complete. Puzzle Slots: {puzzleSlots.Count}, Pickup Items: {pickupItems.Count}, Environment Interactables: {environmentInteractables.Count}");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a9e6445d7af341b6995266514b666497
timeCreated: 1773921699

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 62c44b8b6745497c8ec129a63cbabd07
timeCreated: 1773597607

View File

@@ -0,0 +1,93 @@
using System.IO;
using BriarQueen.Data.IO;
using UnityEditor;
using UnityEngine;
namespace BriarQueen.Editor.Windows
{
public class FilePathsWindow : EditorWindow
{
[MenuItem("Briar Queen/IO/Open Persistent Data/Save Data")]
private static void OpenSaveDataFolder()
{
OpenFolder(FilePaths.SaveDataFolder);
}
[MenuItem("Briar Queen/IO/Open Persistent Data/Save Backups")]
private static void OpenSaveBackupFolder()
{
OpenFolder(FilePaths.SaveBackupDataFolder);
}
[MenuItem("Briar Queen/IO/Open Persistent Data/Configs")]
private static void OpenConfigFolder()
{
OpenFolder(FilePaths.ConfigFolder);
}
[MenuItem("Briar Queen/IO/Open Persistent Data/All Paths Window")]
private static void ShowWindow()
{
var window = GetWindow<FilePathsWindow>("Persistent Data Paths");
window.minSize = new Vector2(520f, 180f);
}
private void OnGUI()
{
GUILayout.Space(10f);
EditorGUILayout.LabelField("BriarQueen Persistent Data Paths", EditorStyles.boldLabel);
GUILayout.Space(8f);
DrawPathRow("Save Data", FilePaths.SaveDataFolder);
DrawPathRow("Save Backups", FilePaths.SaveBackupDataFolder);
DrawPathRow("Configs", FilePaths.ConfigFolder);
GUILayout.Space(12f);
if (GUILayout.Button("Open Application Persistent Data Root", GUILayout.Height(28f)))
{
OpenFolder(Application.persistentDataPath);
}
}
private static void DrawPathRow(string label, string path)
{
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
{
EditorGUILayout.LabelField(label, EditorStyles.boldLabel);
EditorGUILayout.SelectableLabel(path, EditorStyles.textField, GUILayout.Height(18f));
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Open", GUILayout.Height(24f)))
{
OpenFolder(path);
}
if (GUILayout.Button("Copy Path", GUILayout.Height(24f)))
{
EditorGUIUtility.systemCopyBuffer = path;
Debug.Log($"Copied path: {path}");
}
}
}
}
private static void OpenFolder(string folderPath)
{
if (string.IsNullOrWhiteSpace(folderPath))
{
Debug.LogError("Cannot open folder: path is null or empty.");
return;
}
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
Debug.Log($"Created folder: {folderPath}");
}
EditorUtility.RevealInFinder(folderPath);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 09ff6a774c3b4bb18d1fc39ac9d70284
timeCreated: 1773668525

View File

@@ -0,0 +1,655 @@
using System;
using System.Collections.Generic;
using BriarQueen.Data.Assets;
using BriarQueen.Data.Identifiers;
using BriarQueen.Framework.Managers.Player.Data;
using UnityEditor;
using UnityEngine;
namespace BriarQueen.Editor.Windows
{
public class ScriptableObjectGenerator : EditorWindow
{
private const string DEFAULT_ITEM_ROOT_FOLDER = "Assets/Data/Information/Items";
private const string DEFAULT_ICON_FOLDER = "Assets/Data/Artwork/Icons";
private const string DEFAULT_ASSET_ENTRY_ROOT_FOLDER = "Assets/Data/Information/Asset Keys";
private const string DEFAULT_CODEX_ROOT_FOLDER = "Assets/Data/Information/Codex";
private string _itemRootFolder = DEFAULT_ITEM_ROOT_FOLDER;
private string _iconFolder = DEFAULT_ICON_FOLDER;
private string _assetEntryRootFolder = DEFAULT_ASSET_ENTRY_ROOT_FOLDER;
private string _codexRootFolder = DEFAULT_CODEX_ROOT_FOLDER;
private bool _overwriteExisting;
private bool _assignIconsDuringCreate = true;
private bool _overwriteExistingIcons;
private bool _createPickups = true;
private bool _createEnvironment = true;
private bool _createPuzzleSlots = true;
private bool _createUIAssetEntries = true;
private bool _createSceneAssetEntries = true;
private bool _createLevelAssetEntries = true;
private bool _createItemAssetEntries = true;
private bool _createBookEntries = true;
private bool _createPuzzleClues = true;
private bool _createPhotos = true;
[MenuItem("Briar Queen/Tools/Assets/Scriptable Object Generator")]
public static void Open()
{
var window = GetWindow<ScriptableObjectGenerator>("ItemDataSO Generator");
window.minSize = new Vector2(700f, 600f);
}
private void OnGUI()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Content Generator", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
"Create or update ItemDataSo assets from enums, assign icons to existing ItemDataSo assets, create/update AssetEntry assets from asset enums, and create/update CodexEntrySO assets from codex enums.",
MessageType.Info);
EditorGUILayout.Space();
DrawItemDataSection();
EditorGUILayout.Space(10f);
DrawAssetEntrySection();
EditorGUILayout.Space(10f);
DrawCodexSection();
}
private void DrawItemDataSection()
{
EditorGUILayout.LabelField("ItemDataSo", EditorStyles.boldLabel);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Folders", EditorStyles.miniBoldLabel);
_itemRootFolder = EditorGUILayout.TextField("Items Root Folder", _itemRootFolder);
_iconFolder = EditorGUILayout.TextField("Icons Folder", _iconFolder);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Create / Update Options", EditorStyles.miniBoldLabel);
_overwriteExisting = EditorGUILayout.Toggle("Overwrite Existing Assets", _overwriteExisting);
_assignIconsDuringCreate = EditorGUILayout.Toggle("Assign Icons During Create", _assignIconsDuringCreate);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Generate Groups", EditorStyles.miniBoldLabel);
_createPickups = EditorGUILayout.Toggle("Pickups", _createPickups);
_createEnvironment = EditorGUILayout.Toggle("Environment", _createEnvironment);
_createPuzzleSlots = EditorGUILayout.Toggle("Puzzle Slots", _createPuzzleSlots);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Icon Assignment Options", EditorStyles.miniBoldLabel);
_overwriteExistingIcons = EditorGUILayout.Toggle("Overwrite Existing Icons", _overwriteExistingIcons);
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Create / Update ItemDataSO Assets", GUILayout.Height(32f)))
CreateOrUpdateItemAssets();
if (GUILayout.Button("Assign Icons To Existing ItemDataSOs", GUILayout.Height(32f)))
AssignIconsToExistingItemAssets();
}
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Reveal Items Folder", GUILayout.Height(28f)))
RevealFolder(_itemRootFolder);
if (GUILayout.Button("Reveal Icons Folder", GUILayout.Height(28f)))
RevealFolder(_iconFolder);
}
}
private void DrawAssetEntrySection()
{
EditorGUILayout.LabelField("AssetEntry", EditorStyles.boldLabel);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Folders", EditorStyles.miniBoldLabel);
_assetEntryRootFolder = EditorGUILayout.TextField("AssetEntry Root Folder", _assetEntryRootFolder);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Generate AssetEntry Groups", EditorStyles.miniBoldLabel);
_createUIAssetEntries = EditorGUILayout.Toggle("UI Entries", _createUIAssetEntries);
_createSceneAssetEntries = EditorGUILayout.Toggle("Scene Entries", _createSceneAssetEntries);
_createLevelAssetEntries = EditorGUILayout.Toggle("Level Entries", _createLevelAssetEntries);
_createItemAssetEntries = EditorGUILayout.Toggle("Item Entries", _createItemAssetEntries);
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Create / Update AssetEntry Assets", GUILayout.Height(32f)))
CreateOrUpdateAssetEntries();
if (GUILayout.Button("Reveal AssetEntry Folder", GUILayout.Height(32f)))
RevealFolder(_assetEntryRootFolder);
}
}
private void DrawCodexSection()
{
EditorGUILayout.LabelField("CodexEntrySO", EditorStyles.boldLabel);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Folders", EditorStyles.miniBoldLabel);
_codexRootFolder = EditorGUILayout.TextField("Codex Root Folder", _codexRootFolder);
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Generate Codex Groups", EditorStyles.miniBoldLabel);
_createBookEntries = EditorGUILayout.Toggle("Book Entries", _createBookEntries);
_createPuzzleClues = EditorGUILayout.Toggle("Puzzle Clues", _createPuzzleClues);
_createPhotos = EditorGUILayout.Toggle("Photos", _createPhotos);
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Create / Update CodexEntrySO Assets", GUILayout.Height(32f)))
CreateOrUpdateCodexEntries();
if (GUILayout.Button("Reveal Codex Folder", GUILayout.Height(32f)))
RevealFolder(_codexRootFolder);
}
}
private void CreateOrUpdateItemAssets()
{
if (!IsValidAssetsPath(_itemRootFolder))
{
Debug.LogError("[Content Generator] Items Root Folder must start with 'Assets'.");
return;
}
EnsureFolderExists(_itemRootFolder);
int created = 0;
int updated = 0;
int skipped = 0;
if (_createPickups)
{
ProcessEnum<ItemKey, ItemDataSo>(
rootFolder: _itemRootFolder,
folderName: "Pickups",
noneValue: ItemKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<ItemDataSo>(path),
createAsset: () => CreateInstance<ItemDataSo>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_idType", (int)ItemDataSo.ItemIdType.Pickup);
SetEnumField(so, "_itemKey", (int)key);
SetEnumField(so, "_environmentKey", (int)EnvironmentKey.None);
SetEnumField(so, "_puzzleSlotKey", (int)PuzzleSlotKey.None);
SetStringFieldIfEmpty(so, "_itemName", ObjectNames.NicifyVariableName(key.ToString()));
if (_assignIconsDuringCreate)
AssignIcon(so, key.ToString(), _overwriteExistingIcons);
},
ref created,
ref updated,
ref skipped);
}
if (_createEnvironment)
{
ProcessEnum<EnvironmentKey, ItemDataSo>(
rootFolder: _itemRootFolder,
folderName: "Environment",
noneValue: EnvironmentKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<ItemDataSo>(path),
createAsset: () => CreateInstance<ItemDataSo>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_idType", (int)ItemDataSo.ItemIdType.Environment);
SetEnumField(so, "_itemKey", (int)ItemKey.None);
SetEnumField(so, "_environmentKey", (int)key);
SetEnumField(so, "_puzzleSlotKey", (int)PuzzleSlotKey.None);
SetStringFieldIfEmpty(so, "_itemName", ObjectNames.NicifyVariableName(key.ToString()));
if (_assignIconsDuringCreate)
AssignIcon(so, key.ToString(), _overwriteExistingIcons);
},
ref created,
ref updated,
ref skipped);
}
if (_createPuzzleSlots)
{
ProcessEnum<PuzzleSlotKey, ItemDataSo>(
rootFolder: _itemRootFolder,
folderName: "PuzzleSlots",
noneValue: PuzzleSlotKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<ItemDataSo>(path),
createAsset: () => CreateInstance<ItemDataSo>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_idType", (int)ItemDataSo.ItemIdType.PuzzleSlot);
SetEnumField(so, "_itemKey", (int)ItemKey.None);
SetEnumField(so, "_environmentKey", (int)EnvironmentKey.None);
SetEnumField(so, "_puzzleSlotKey", (int)key);
SetStringFieldIfEmpty(so, "_itemName", ObjectNames.NicifyVariableName(key.ToString()));
if (_assignIconsDuringCreate)
AssignIcon(so, key.ToString(), _overwriteExistingIcons);
},
ref created,
ref updated,
ref skipped);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[Content Generator] ItemDataSO generation complete. Created: {created}, Updated: {updated}, Skipped: {skipped}");
}
private void AssignIconsToExistingItemAssets()
{
if (!IsValidAssetsPath(_iconFolder))
{
Debug.LogError("[Content Generator] Icons Folder must start with 'Assets'.");
return;
}
if (!AssetDatabase.IsValidFolder(_iconFolder))
{
Debug.LogError($"[Content Generator] Icons folder not found: {_iconFolder}");
return;
}
string[] guids = AssetDatabase.FindAssets("t:ItemDataSo", new[] { _itemRootFolder });
int assigned = 0;
int skipped = 0;
int missing = 0;
foreach (string guid in guids)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
var asset = AssetDatabase.LoadAssetAtPath<ItemDataSo>(assetPath);
if (asset == null)
continue;
var so = new SerializedObject(asset);
so.Update();
bool changed = AssignIcon(so, asset.name, _overwriteExistingIcons);
if (changed)
{
so.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(asset);
assigned++;
}
else
{
var iconProp = so.FindProperty("_icon");
if (iconProp != null && iconProp.objectReferenceValue != null && !_overwriteExistingIcons)
skipped++;
else
missing++;
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[Content Generator] Icon assignment complete. Assigned: {assigned}, Skipped: {skipped}, Missing Icons: {missing}");
}
private void CreateOrUpdateAssetEntries()
{
if (!IsValidAssetsPath(_assetEntryRootFolder))
{
Debug.LogError("[Content Generator] AssetEntry Root Folder must start with 'Assets'.");
return;
}
EnsureFolderExists(_assetEntryRootFolder);
int created = 0;
int updated = 0;
int skipped = 0;
if (_createUIAssetEntries)
{
ProcessEnum<UIKey, AssetEntry>(
rootFolder: _assetEntryRootFolder,
folderName: "UI",
noneValue: UIKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<AssetEntry>(path),
createAsset: () => CreateInstance<AssetEntry>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_entryType", (int)AssetEntry.AssetEntryType.UI);
SetEnumField(so, "_uiKey", (int)key);
SetEnumField(so, "_sceneKey", (int)SceneKey.None);
SetEnumField(so, "_levelKey", (int)LevelKey.None);
SetEnumField(so, "_itemKey", (int)AssetItemKey.None);
},
ref created,
ref updated,
ref skipped);
}
if (_createSceneAssetEntries)
{
ProcessEnum<SceneKey, AssetEntry>(
rootFolder: _assetEntryRootFolder,
folderName: "Scenes",
noneValue: SceneKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<AssetEntry>(path),
createAsset: () => CreateInstance<AssetEntry>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_entryType", (int)AssetEntry.AssetEntryType.Scene);
SetEnumField(so, "_uiKey", (int)UIKey.None);
SetEnumField(so, "_sceneKey", (int)key);
SetEnumField(so, "_levelKey", (int)LevelKey.None);
SetEnumField(so, "_itemKey", (int)AssetItemKey.None);
},
ref created,
ref updated,
ref skipped);
}
if (_createLevelAssetEntries)
{
ProcessEnum<LevelKey, AssetEntry>(
rootFolder: _assetEntryRootFolder,
folderName: "Levels",
noneValue: LevelKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<AssetEntry>(path),
createAsset: () => CreateInstance<AssetEntry>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_entryType", (int)AssetEntry.AssetEntryType.Level);
SetEnumField(so, "_uiKey", (int)UIKey.None);
SetEnumField(so, "_sceneKey", (int)SceneKey.None);
SetEnumField(so, "_levelKey", (int)key);
SetEnumField(so, "_itemKey", (int)AssetItemKey.None);
},
ref created,
ref updated,
ref skipped);
}
if (_createItemAssetEntries)
{
ProcessEnum<AssetItemKey, AssetEntry>(
rootFolder: _assetEntryRootFolder,
folderName: "Items",
noneValue: AssetItemKey.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<AssetEntry>(path),
createAsset: () => CreateInstance<AssetEntry>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_entryType", (int)AssetEntry.AssetEntryType.Item);
SetEnumField(so, "_uiKey", (int)UIKey.None);
SetEnumField(so, "_sceneKey", (int)SceneKey.None);
SetEnumField(so, "_levelKey", (int)LevelKey.None);
SetEnumField(so, "_itemKey", (int)key);
},
ref created,
ref updated,
ref skipped);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[Content Generator] AssetEntry generation complete. Created: {created}, Updated: {updated}, Skipped: {skipped}");
}
private void CreateOrUpdateCodexEntries()
{
if (!IsValidAssetsPath(_codexRootFolder))
{
Debug.LogError("[Content Generator] Codex Root Folder must start with 'Assets'.");
return;
}
EnsureFolderExists(_codexRootFolder);
int created = 0;
int updated = 0;
int skipped = 0;
if (_createBookEntries)
{
ProcessEnum<BookEntryID, CodexEntrySo>(
rootFolder: _codexRootFolder,
folderName: "Books",
noneValue: BookEntryID.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<CodexEntrySo>(path),
createAsset: () => CreateInstance<CodexEntrySo>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_codexType", (int)CodexType.BookEntry);
SetEnumField(so, "_bookEntryID", (int)key);
SetEnumField(so, "_clueEntryID", (int)ClueEntryID.None);
SetEnumField(so, "_photoEntryID", (int)PhotoEntryID.None);
SetStringFieldIfEmpty(so, "_title", ObjectNames.NicifyVariableName(key.ToString()));
},
ref created,
ref updated,
ref skipped);
}
if (_createPuzzleClues)
{
ProcessEnum<ClueEntryID, CodexEntrySo>(
rootFolder: _codexRootFolder,
folderName: "Clues",
noneValue: ClueEntryID.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<CodexEntrySo>(path),
createAsset: () => CreateInstance<CodexEntrySo>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_codexType", (int)CodexType.PuzzleClue);
SetEnumField(so, "_bookEntryID", (int)BookEntryID.None);
SetEnumField(so, "_clueEntryID", (int)key);
SetEnumField(so, "_photoEntryID", (int)PhotoEntryID.None);
SetStringFieldIfEmpty(so, "_title", ObjectNames.NicifyVariableName(key.ToString()));
},
ref created,
ref updated,
ref skipped);
}
if (_createPhotos)
{
ProcessEnum<PhotoEntryID, CodexEntrySo>(
rootFolder: _codexRootFolder,
folderName: "Photos",
noneValue: PhotoEntryID.None,
assetExtensionName: key => key.ToString(),
loadExistingAsset: path => AssetDatabase.LoadAssetAtPath<CodexEntrySo>(path),
createAsset: () => CreateInstance<CodexEntrySo>(),
applyValues: (so, key) =>
{
SetEnumField(so, "_codexType", (int)CodexType.Photo);
SetEnumField(so, "_bookEntryID", (int)BookEntryID.None);
SetEnumField(so, "_clueEntryID", (int)ClueEntryID.None);
SetEnumField(so, "_photoEntryID", (int)key);
SetStringFieldIfEmpty(so, "_title", ObjectNames.NicifyVariableName(key.ToString()));
},
ref created,
ref updated,
ref skipped);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[Content Generator] CodexEntrySO generation complete. Created: {created}, Updated: {updated}, Skipped: {skipped}");
}
private void ProcessEnum<TEnum, TAsset>(
string rootFolder,
string folderName,
TEnum noneValue,
Func<TEnum, string> assetExtensionName,
Func<string, TAsset> loadExistingAsset,
Func<TAsset> createAsset,
Action<SerializedObject, TEnum> applyValues,
ref int created,
ref int updated,
ref int skipped)
where TEnum : struct, Enum
where TAsset : UnityEngine.Object
{
string folderPath = $"{rootFolder}/{folderName}";
EnsureFolderExists(folderPath);
foreach (TEnum key in Enum.GetValues(typeof(TEnum)))
{
if (EqualityComparer<TEnum>.Default.Equals(key, noneValue))
continue;
string assetName = assetExtensionName(key);
string assetPath = $"{folderPath}/{assetName}.asset";
var existing = loadExistingAsset(assetPath);
if (existing != null && !_overwriteExisting)
{
skipped++;
continue;
}
TAsset asset;
bool isNew = false;
if (existing == null)
{
asset = createAsset();
AssetDatabase.CreateAsset(asset, assetPath);
isNew = true;
}
else
{
asset = existing;
}
var so = new SerializedObject(asset);
so.Update();
applyValues(so, key);
so.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(asset);
if (isNew)
created++;
else
updated++;
}
}
private bool AssignIcon(SerializedObject so, string assetOrEnumName, bool overwriteExisting)
{
var iconProp = so.FindProperty("_icon");
if (iconProp == null)
return false;
if (!overwriteExisting && iconProp.objectReferenceValue != null)
return false;
Sprite sprite = FindIconByName(assetOrEnumName);
if (sprite == null)
return false;
iconProp.objectReferenceValue = sprite;
return true;
}
private Sprite FindIconByName(string iconName)
{
if (!AssetDatabase.IsValidFolder(_iconFolder))
return null;
string[] guids = AssetDatabase.FindAssets($"{iconName} t:Sprite", new[] { _iconFolder });
if (guids == null || guids.Length == 0)
return null;
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
return AssetDatabase.LoadAssetAtPath<Sprite>(path);
}
private static void SetEnumField(SerializedObject so, string propertyName, int enumValue)
{
var prop = so.FindProperty(propertyName);
if (prop != null)
prop.enumValueIndex = enumValue;
}
private static void SetStringFieldIfEmpty(SerializedObject so, string propertyName, string value)
{
var prop = so.FindProperty(propertyName);
if (prop == null)
return;
if (string.IsNullOrWhiteSpace(prop.stringValue))
prop.stringValue = value;
}
private static bool IsValidAssetsPath(string path)
{
return !string.IsNullOrWhiteSpace(path) && path.StartsWith("Assets", StringComparison.Ordinal);
}
private static void EnsureFolderExists(string fullPath)
{
string[] parts = fullPath.Split('/');
if (parts.Length == 0 || parts[0] != "Assets")
throw new InvalidOperationException($"Invalid Unity folder path: {fullPath}");
string current = "Assets";
for (int i = 1; i < parts.Length; i++)
{
string next = $"{current}/{parts[i]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(current, parts[i]);
current = next;
}
}
private static void RevealFolder(string folderPath)
{
if (!IsValidAssetsPath(folderPath))
return;
EnsureFolderExists(folderPath);
UnityEngine.Object folder = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(folderPath);
if (folder != null)
{
Selection.activeObject = folder;
EditorGUIUtility.PingObject(folder);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b81754fd377748c6a539d9077fa199b5
timeCreated: 1773597607

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6af79de96118949af9eacd7d09a95428
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Scripts/Framework/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ff2ed8f2f4124c45aad87cacd37abf0d
timeCreated: 1769705501

View File

@@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.Threading;
using BriarQueen.Framework.Assets.Components;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using VContainer;
using VContainer.Unity;
namespace BriarQueen.Framework.Assets
{
public class AddressableManager : IDisposable
{
private readonly Dictionary<object, AsyncOperationHandle> _assetHandles = new();
private readonly Dictionary<GameObject, AsyncOperationHandle> _instanceHandles = new();
private readonly IObjectResolver _lifetimeContainer;
private readonly object _lock = new();
[Inject]
public AddressableManager(IObjectResolver lifetimeContainer)
{
_lifetimeContainer = lifetimeContainer;
}
public void Dispose()
{
lock (_lock)
{
foreach (var handle in _instanceHandles.Values)
if (handle.IsValid())
Addressables.ReleaseInstance(handle);
_instanceHandles.Clear();
foreach (var handle in _assetHandles.Values)
if (handle.IsValid())
Addressables.Release(handle);
_assetHandles.Clear();
}
}
public async UniTask<T> LoadAssetAsync<T>(AssetReference reference) where T : class
{
var handle = Addressables.LoadAssetAsync<T>(reference);
try
{
await handle;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
lock (_lock)
{
_assetHandles[handle.Result] = handle;
}
return handle.Result;
}
Debug.LogError($"[AddressableManager] Failed to load asset: {reference.RuntimeKey}");
return null;
}
catch (OperationCanceledException)
{
if (handle.IsValid()) Addressables.Release(handle);
throw;
}
}
public void ReleaseAsset<T>(T asset) where T : class
{
if (asset == null) return;
lock (_lock)
{
if (_assetHandles.TryGetValue(asset, out var handle))
{
if (handle.IsValid()) Addressables.Release(handle);
_assetHandles.Remove(asset);
}
}
}
public async UniTask<AsyncOperationHandle<SceneInstance>> LoadSceneAsync(
AssetReference assetReference,
LoadSceneMode loadSceneMode = LoadSceneMode.Additive,
CancellationToken cancellationToken = default,
IProgress<float> progress = null,
bool autoLoad = true
)
{
var handle = Addressables.LoadSceneAsync(assetReference, loadSceneMode, autoLoad);
await handle.ToUniTask(progress, cancellationToken: cancellationToken);
return handle;
}
public async UniTask UnloadSceneAsync(AsyncOperationHandle<SceneInstance> sceneHandle)
{
if (sceneHandle.IsValid()) await Addressables.UnloadSceneAsync(sceneHandle);
}
public async UniTask<GameObject> InstantiateAsync(
AssetReference reference,
Vector3 position = default,
Quaternion rotation = default,
Transform parent = null,
IObjectResolver scope = null,
CancellationToken cancellationToken = default
)
{
var handle = Addressables.InstantiateAsync(reference, position, rotation, parent);
try
{
await handle.WithCancellation(cancellationToken);
if (handle.Status != AsyncOperationStatus.Succeeded)
{
Debug.LogError($"[AddressableManager] Failed to instantiate asset: {reference.RuntimeKey}");
return null;
}
var go = handle.Result;
lock (_lock)
{
_instanceHandles[go] = handle;
}
var prefabScope = go.GetComponent<LifetimeScope>();
var injectionScope = scope ?? prefabScope?.Container ?? _lifetimeContainer;
injectionScope.InjectGameObject(go);
var notify = go.GetComponent<NotifyOnDestruction>();
if (!notify)
{
notify = go.AddComponent<NotifyOnDestruction>();
injectionScope.Inject(notify);
}
void OnInstanceDestroyed()
{
TryReleaseInstance(go);
notify.OnDestroyedCalled -= OnInstanceDestroyed;
}
notify.OnDestroyedCalled += OnInstanceDestroyed;
return go;
}
catch (OperationCanceledException)
{
if (handle.IsValid()) Addressables.ReleaseInstance(handle);
throw;
}
}
public bool TryReleaseInstance(GameObject instance)
{
if (instance == null)
return false;
Debug.Log($"[AddressableManager] Trying to release instance: {instance}");
lock (_lock)
{
if (_instanceHandles.TryGetValue(instance, out var handle))
{
if (handle.IsValid()) Addressables.ReleaseInstance(handle);
_instanceHandles.Remove(instance);
return true;
}
}
return false;
}
public void ReleaseInstance(GameObject instance)
{
if (instance == null) return;
lock (_lock)
{
if (_instanceHandles.TryGetValue(instance, out var handle))
{
if (handle.IsValid()) Addressables.ReleaseInstance(handle);
_instanceHandles.Remove(instance);
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e1db17fcf6194082baad6949fa448ca0
timeCreated: 1769705501

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ff405febbf4c423ea26f3a0313bef4d5
timeCreated: 1773836297

View File

@@ -0,0 +1,34 @@
using System;
using BriarQueen.Framework.Services.Destruction;
using Cysharp.Threading.Tasks;
using UnityEngine;
using VContainer;
namespace BriarQueen.Framework.Assets.Components
{
public class NotifyOnDestruction : MonoBehaviour, IDestructible
{
private DestructionService _destructionService;
public UniTask OnPreDestroy()
{
OnPreDestroyCalled?.Invoke();
return UniTask.CompletedTask; // No async operation needed, just a notification
}
public UniTask OnDestroyed()
{
OnDestroyedCalled?.Invoke();
return UniTask.CompletedTask; // No async operation needed, just a notification
}
[Inject]
public void Construct(DestructionService destructionService)
{
_destructionService = destructionService;
}
public event Action OnPreDestroyCalled;
public event Action OnDestroyedCalled;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e2aae9ec81f949ffbc5de664b65b46b3
timeCreated: 1769705628

View File

@@ -0,0 +1,6 @@
namespace BriarQueen.Framework.Assets
{
public interface IAssetProvider
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 84550fd141464293b5d6680bdb50bf93
timeCreated: 1769705539

View File

@@ -0,0 +1,24 @@
{
"name": "BriarQueen.Framework",
"rootNamespace": "BriarQueen",
"references": [
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:80ecb87cae9c44d19824e70ea7229748",
"GUID:bdf0eff65032c4178bf18aa9c96b1c70",
"GUID:9e24947de15b9834991c9d8411ea37cf",
"GUID:593a5b492d29ac6448b1ebf7f035ef33",
"GUID:84651a3751eca9349aac36a66bba901b",
"GUID:75469ad4d38634e559750d17036d5f7c",
"GUID:776d03a35f1b52c4a9aed9f56d7b4229"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ac1be664c635c449eb9f3f52cf5c97f5
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 842950604aa54cb8a98ece9875c94bfe
timeCreated: 1773843729

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6d5109c39334a33a1ec140fed9aa971
timeCreated: 1769705112

Binary file not shown.

View File

@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using BriarQueen.Framework.Events.System;
using UnityEngine;
using VContainer.Unity;
namespace BriarQueen.Framework.Coordinators.Events
{
/// <summary>
/// A type-safe, queued event bus for managing gameplay events.
/// </summary>
public class EventCoordinator : IDisposable, ITickable, IInitializable
{
private const int MAX_EVENTS_PER_FRAME = 500;
public static bool EnableDebugLogs = false;
private readonly Queue<(Type type, IEvent data)> _eventQueue = new();
private readonly object _queueLock = new();
private readonly Dictionary<Type, Dictionary<object, Action<IEvent>>> _typedEventListeners = new();
private bool _isProcessingQueue;
public bool Initialized { get; private set; }
public void Initialize()
{
Initialized = true;
}
public void Dispose()
{
lock (_queueLock)
{
_typedEventListeners.Clear();
_eventQueue.Clear();
}
}
public void Tick()
{
ProcessQueue();
}
public void Subscribe<T>(Action<T> listener) where T : IEvent
{
if (listener == null) return;
var eventType = typeof(T);
if (!_typedEventListeners.TryGetValue(eventType, out var listeners))
{
listeners = new Dictionary<object, Action<IEvent>>();
_typedEventListeners[eventType] = listeners;
}
if (listeners.ContainsKey(listener)) return;
Action<IEvent> wrapper = e => listener((T)e);
listeners[listener] = wrapper;
#if UNITY_EDITOR
Debug.Log(
$"[EventManager] Subscribed '{eventType.Name}' to '{listener.Method.Name}' from '{listener.Target}'");
#endif
}
public void Unsubscribe<T>(Action<T> listener) where T : IEvent
{
if (listener == null) return;
var eventType = typeof(T);
if (_typedEventListeners.TryGetValue(eventType, out var listeners))
{
listeners.Remove(listener);
if (listeners.Count == 0)
_typedEventListeners.Remove(eventType);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Publish<T>(T eventData) where T : IEvent
{
if (eventData == null) return;
lock (_queueLock)
{
_eventQueue.Enqueue((typeof(T), eventData));
}
if (EnableDebugLogs)
Debug.Log($"[EventManager] Enqueued '{typeof(T).Name}'");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PublishImmediate<T>(T eventData) where T : IEvent
{
if (eventData == null) return;
var type = typeof(T);
if (_typedEventListeners.TryGetValue(type, out var listeners) && listeners.Count > 0)
{
var snapshot = new Action<IEvent>[listeners.Count];
listeners.Values.CopyTo(snapshot, 0);
for (var i = 0; i < snapshot.Length; i++)
{
var listener = snapshot[i];
try
{
listener?.Invoke(eventData);
}
catch (Exception ex)
{
Debug.LogError($"[EventManager] Exception in '{type.Name}' listener: {ex}");
}
}
}
if (EnableDebugLogs)
Debug.Log($"[EventManager] Fired '{type.Name}' (Immediate)");
}
private void ProcessQueue()
{
if (_isProcessingQueue) return;
_isProcessingQueue = true;
try
{
var processed = 0;
while (true)
{
(Type type, IEvent data) evt;
lock (_queueLock)
{
if (_eventQueue.Count == 0) break;
evt = _eventQueue.Dequeue();
}
processed++;
if (processed > MAX_EVENTS_PER_FRAME)
{
Debug.LogWarning(
"[EventManager] Max event processing limit reached. Possible infinite publish loop.");
break;
}
if (_typedEventListeners.TryGetValue(evt.type, out var listeners) && listeners.Count > 0)
{
var snapshot = new Action<IEvent>[listeners.Count];
listeners.Values.CopyTo(snapshot, 0);
for (var i = 0; i < snapshot.Length; i++)
{
var listener = snapshot[i];
try
{
listener?.Invoke(evt.data);
}
catch (Exception ex)
{
Debug.LogError(
$"[EventManager] Exception in '{evt.type.Name}' listener during queue processing: {ex}");
}
}
}
if (EnableDebugLogs)
Debug.Log($"[EventManager] Fired '{evt.type.Name}' (Queued)");
}
}
finally
{
_isProcessingQueue = false;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f0594880d694d8e9297c88bbffbce8f
timeCreated: 1769705112

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c6a5d896b814949f891721df67bc4d13
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 83fb6ea2b08724af2bfa88da928ff310
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
using BriarQueen.Framework.Events.System;
using BriarQueen.Framework.Managers.Audio.Data;
namespace BriarQueen.Framework.Events.Audio
{
public record MusicTrackChangedEvent(AudioFileSo Track) : IEvent;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f3b589adbdecc410faad1a94d0e92943

View File

@@ -0,0 +1,6 @@
using BriarQueen.Framework.Events.System;
namespace BriarQueen.Framework.Events.Audio
{
public record VoiceLineFinishedEvent(string VoiceLineID) : IEvent;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 86ad67d94ae87497a867a22b24fcab54

View File

@@ -0,0 +1,6 @@
using BriarQueen.Framework.Events.System;
namespace BriarQueen.Framework.Events.Audio
{
public record VoiceLineStartedEvent(string VoiceLineID) : IEvent;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8f19b51fb48104a5eae452ff3261ce05

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3eca027f79f05492da343f57bf23ec94
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
using BriarQueen.Framework.Events.System;
namespace BriarQueen.Framework.Events.Gameplay
{
public record InventoryChangedEvent : IEvent;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cdfd706f6f29d43ab80578bd9c9906b8

View File

@@ -0,0 +1,7 @@
using BriarQueen.Framework.Events.System;
using BriarQueen.Framework.Managers.Levels.Data;
namespace BriarQueen.Framework.Events.Gameplay
{
public record LevelChangedEvent(BaseLevel Level) : IEvent;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 54697a2ce8b80480cb269432c0402bc8

View File

@@ -0,0 +1,7 @@
using BriarQueen.Framework.Events.System;
using BriarQueen.Framework.Managers.Player.Data;
namespace BriarQueen.Framework.Events.Gameplay
{
public record OnNextItemClickedEvent() : IEvent;
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b741224892c24b6f899614e541f0bebd
timeCreated: 1774433570

View File

@@ -0,0 +1,6 @@
using BriarQueen.Framework.Events.System;
namespace BriarQueen.Framework.Events.Gameplay
{
public record OnNextToolChangedEvent() : IEvent;
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 720ded31a3f945e1b5a11c64099e370a
timeCreated: 1774128263

View File

@@ -0,0 +1,7 @@
using BriarQueen.Framework.Events.System;
using BriarQueen.Framework.Managers.Player.Data;
namespace BriarQueen.Framework.Events.Gameplay
{
public record OnPreviousItemClickedEvent() : IEvent;
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9270b837f3004e398c669ccfefa92a19
timeCreated: 1774433570

Some files were not shown because too many files have changed in this diff Show More