diff --git a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset index 4071a51..ca7a236 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -20,6 +20,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: ed9b80145a89c8a4f8d456971d242c3c + m_Address: Assets/Database/UI/PortraitsHolder.asset + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: 7e98b357dd76a43e191428299c44eef2, type: 2} m_SchemaSet: diff --git a/Assets/Code/Core/MainMenuGameState.cs b/Assets/Code/Core/MainMenuGameState.cs index 1ae422a..c8cda62 100644 --- a/Assets/Code/Core/MainMenuGameState.cs +++ b/Assets/Code/Core/MainMenuGameState.cs @@ -80,11 +80,11 @@ namespace Nox.Core { var perKRegistry = Addressables.LoadAssetAsync("PerksRegistry").WaitForCompletion(); var characterRegistry = Addressables.LoadAssetAsync("CharacterRegistry").WaitForCompletion(); var modifiersRegistry = Addressables.LoadAssetAsync("ModifiersRegistry").WaitForCompletion(); - var partySettings = Addressables.LoadAssetAsync("DefaultPartySettings").WaitForCompletion(); + var portraitsHolder = Addressables.LoadAssetAsync("PortraitsHolder").WaitForCompletion(); var characterSystems = CharacterSystemsFactory.Create(partySettings, characterBaseSettings, perKRegistry, characterRegistry, modifiersRegistry); - mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems); + mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems, portraitsHolder); mainMenuView.Initialize(); mainMenuView.Show(); IsGameStateInitialized = true; diff --git a/Assets/Code/GameState/UI/CharacterCreationReference.cs b/Assets/Code/GameState/UI/CharacterCreationReference.cs index 1fa4f1c..1d4adfb 100644 --- a/Assets/Code/GameState/UI/CharacterCreationReference.cs +++ b/Assets/Code/GameState/UI/CharacterCreationReference.cs @@ -15,6 +15,7 @@ namespace Nox.UI { public TMP_Dropdown raceDropdown; public TMP_Dropdown classDropdown; public TMP_Dropdown perksDropdown; + public TextMeshProUGUI pointsToDistribute; public AttributeReference[] attributeReference; public StatReference[] statReference; diff --git a/Assets/Code/GameState/UI/CharacterCreationView.cs b/Assets/Code/GameState/UI/CharacterCreationView.cs index 95a4c9d..975c6df 100644 --- a/Assets/Code/GameState/UI/CharacterCreationView.cs +++ b/Assets/Code/GameState/UI/CharacterCreationView.cs @@ -18,25 +18,27 @@ namespace Nox.UI { private readonly GameDataState gameDataState; private readonly PartySettings partySettings; private readonly ICharacterSystems characterSystems; + private readonly PortraitsHolder portraitsHolder; private List characterCreationRequests; private Action canStartCheck; private GameLogView gameLogView; private InGameLogger inGameLogger; - public CharacterCreationView( - CharacterCreationReference characterCreationReference, + public CharacterCreationView(CharacterCreationReference characterCreationReference, MenuGameStateData menuGameStateData, ISaveSystem saveSystem, GameDataState gameDataState, PartySettings partySettings, - ICharacterSystems characterSystems) { + ICharacterSystems characterSystems, + PortraitsHolder portraitsHolder) { SaveSystem = saveSystem; this.characterCreationReference = characterCreationReference; this.menuGameStateData = menuGameStateData; this.gameDataState = gameDataState; this.partySettings = partySettings; this.characterSystems = characterSystems; + this.portraitsHolder = portraitsHolder; } public void Initialize() { @@ -89,7 +91,7 @@ namespace Nox.UI { gameDataState.ActiveParty = party; inGameLogger.Log("Character Creation Results:"); - inGameLogger.Log($"Protagonist: {party.Protagonist.Name}", "FFBF00"); + inGameLogger.Log($"Protagonist: {party.Protagonist.Name}", "#FFBF00"); inGameLogger.Log($"Protagonist Race: {party.Protagonist.Race}"); inGameLogger.Log($"Protagonist Class: {party.Protagonist.Class}"); inGameLogger.Log($"Companions: {party.Companions.Count}"); diff --git a/Assets/Code/GameState/UI/PortraitsHolder.cs b/Assets/Code/GameState/UI/PortraitsHolder.cs new file mode 100644 index 0000000..8f36bb8 --- /dev/null +++ b/Assets/Code/GameState/UI/PortraitsHolder.cs @@ -0,0 +1,6 @@ +using UnityEngine; + +[CreateAssetMenu(fileName = "PortraitsHolder", menuName = "Nox/Database/UI/PortraitsHolder")] +public class PortraitsHolder : ScriptableObject { + public Sprite[] portraits; +} diff --git a/Assets/Code/GameState/UI/PortraitsHolder.cs.meta b/Assets/Code/GameState/UI/PortraitsHolder.cs.meta new file mode 100644 index 0000000..bf14b12 --- /dev/null +++ b/Assets/Code/GameState/UI/PortraitsHolder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1a49ac7886b19d34e9ecb10b8543bf9f \ No newline at end of file diff --git a/Assets/Code/SplashMainMenuUI/MainMenuView.cs b/Assets/Code/SplashMainMenuUI/MainMenuView.cs index 50a11d8..d57d06e 100644 --- a/Assets/Code/SplashMainMenuUI/MainMenuView.cs +++ b/Assets/Code/SplashMainMenuUI/MainMenuView.cs @@ -17,25 +17,26 @@ namespace Nox.UI { private readonly GameDataState gameDataState; private readonly PartySettings partySettings; private readonly ICharacterSystems characterSystems; + private readonly PortraitsHolder portraitsHolder; private MainMenuReference mainMenuReference; private CharacterCreationReference characterCreationReference; private CharacterCreationView characterCreationView; private AsyncOperationHandle charCreationHandle; - public MainMenuView( - MenuPrefabsContainer menuPrefabsContainer, + public MainMenuView(MenuPrefabsContainer menuPrefabsContainer, MenuGameStateData menuGameStateData, ISaveSystem saveSystem, GameDataState gameDataState, PartySettings partySettings, - ICharacterSystems characterSystems - ) { + ICharacterSystems characterSystems, + PortraitsHolder portraitsHolder) { this.menuPrefabsContainer = menuPrefabsContainer; this.menuGameStateData = menuGameStateData; this.saveSystem = saveSystem; this.gameDataState = gameDataState; this.partySettings = partySettings; this.characterSystems = characterSystems; + this.portraitsHolder = portraitsHolder; } public void Initialize() { if(!mainMenuReference) { @@ -64,7 +65,7 @@ namespace Nox.UI { charCreationHandle = Addressables.InstantiateAsync(menuPrefabsContainer.characterCreationReference); var result = charCreationHandle.WaitForCompletion(); characterCreationReference =result.GetComponent(); - characterCreationView = new CharacterCreationView(characterCreationReference, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems); + characterCreationView = new CharacterCreationView(characterCreationReference, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems, portraitsHolder); characterCreationView.Initialize(); characterCreationView.Show(); } diff --git a/Assets/Code/SplashMainMenuUI/MenuPrefabsContainer.cs b/Assets/Code/SplashMainMenuUI/MenuPrefabsContainer.cs index ba98fb9..f61815f 100644 --- a/Assets/Code/SplashMainMenuUI/MenuPrefabsContainer.cs +++ b/Assets/Code/SplashMainMenuUI/MenuPrefabsContainer.cs @@ -6,5 +6,6 @@ namespace Nox.UI { public class MenuPrefabsContainer : ScriptableObject { public AssetReference mainMenuReference; public AssetReference characterCreationReference; + public AssetReferenceT portraitsHolder; } } diff --git a/Assets/Database/UI/MenuPrefabsContainer.asset b/Assets/Database/UI/MenuPrefabsContainer.asset index abc4a7d..fe84782 100644 --- a/Assets/Database/UI/MenuPrefabsContainer.asset +++ b/Assets/Database/UI/MenuPrefabsContainer.asset @@ -24,3 +24,9 @@ MonoBehaviour: m_SubObjectType: m_SubObjectGUID: m_EditorAssetChanged: 0 + portraitsHolder: + m_AssetGUID: ed9b80145a89c8a4f8d456971d242c3c + m_SubObjectName: + m_SubObjectType: + m_SubObjectGUID: + m_EditorAssetChanged: 0 diff --git a/Assets/Database/UI/PortraitsHolder.asset b/Assets/Database/UI/PortraitsHolder.asset new file mode 100644 index 0000000..c029032 --- /dev/null +++ b/Assets/Database/UI/PortraitsHolder.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1a49ac7886b19d34e9ecb10b8543bf9f, type: 3} + m_Name: PortraitsHolder + m_EditorClassIdentifier: Assembly-CSharp::PortraitsHolder + portraits: + - {fileID: 21300000, guid: 865c9315a4b7fa54aa16e64aaef9e756, type: 3} + - {fileID: 21300000, guid: 0664f64568edf244c8e86bbdfb38f011, type: 3} + - {fileID: 21300000, guid: 6cb6255ee69dbf04abe2eac591388adf, type: 3} + - {fileID: 21300000, guid: 83e04af9321af7c49b92e9dfb1c8bbea, type: 3} + - {fileID: 21300000, guid: 21833c6afc1e0b047a3c49dc9ea35996, type: 3} + - {fileID: 21300000, guid: deae0f160a4f0314486d83fe7313cfcd, type: 3} diff --git a/Assets/Database/UI/PortraitsHolder.asset.meta b/Assets/Database/UI/PortraitsHolder.asset.meta new file mode 100644 index 0000000..3f3049f --- /dev/null +++ b/Assets/Database/UI/PortraitsHolder.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed9b80145a89c8a4f8d456971d242c3c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/UI/CharacterCreationReference.prefab b/Assets/Prefabs/UI/CharacterCreationReference.prefab index 952efc9..03d2adc 100644 --- a/Assets/Prefabs/UI/CharacterCreationReference.prefab +++ b/Assets/Prefabs/UI/CharacterCreationReference.prefab @@ -450,6 +450,7 @@ MonoBehaviour: raceDropdown: {fileID: 6943294572889919698} classDropdown: {fileID: 8872968879584269266} perksDropdown: {fileID: 7398729793764354283} + pointsToDistribute: {fileID: 3719854971230117289} attributeReference: - {fileID: 3269464522976429742} - {fileID: 3067308370580970615} @@ -591,6 +592,7 @@ RectTransform: - {fileID: 646253666791737678} - {fileID: 1409454910803364430} - {fileID: 1009347711478428023} + - {fileID: 5921309381841904503} - {fileID: 8367588536448104447} - {fileID: 3591566853696425169} m_Father: {fileID: 2577996418906990456} @@ -1593,6 +1595,119 @@ RectTransform: m_CorrespondingSourceObject: {fileID: 4856263379376715185, guid: fdfe972137f8acf48b03a4b93554236c, type: 3} m_PrefabInstance: {fileID: 3089194286178984560} m_PrefabAsset: {fileID: 0} +--- !u!1001 &3347958233595658286 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 8277464935197475459} + m_Modifications: + - target: {fileID: 682408470658022631, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_Name + value: DistributePointsHolder + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_AnchorMax.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_AnchorMax.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_AnchorMin.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_AnchorMin.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_SizeDelta.x + value: 585.26 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_SizeDelta.y + value: 40.655 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_AnchoredPosition.x + value: 44.999 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_AnchoredPosition.y + value: 124.93 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} +--- !u!114 &3719854971230117289 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 2155467361723096455, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + m_PrefabInstance: {fileID: 3347958233595658286} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI +--- !u!224 &5921309381841904503 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 8960722644052714329, guid: dd15953ddb58a6b4da3ec6a5aa2de4e4, type: 3} + m_PrefabInstance: {fileID: 3347958233595658286} + m_PrefabAsset: {fileID: 0} --- !u!1001 &4846874011051483482 PrefabInstance: m_ObjectHideFlags: 0 @@ -1857,6 +1972,38 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 2688140319784364735, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2688140319784364735, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4070132176653801724, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4070132176653801724, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] @@ -2311,11 +2458,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 391761576086302107, guid: 3a970b9bece81ba4e9c3518edd20bc15, type: 3} propertyPath: m_AnchoredPosition.x - value: 40.001 + value: 45.00099 objectReference: {fileID: 0} - target: {fileID: 391761576086302107, guid: 3a970b9bece81ba4e9c3518edd20bc15, type: 3} propertyPath: m_AnchoredPosition.y - value: -68 + value: -112 objectReference: {fileID: 0} - target: {fileID: 391761576086302107, guid: 3a970b9bece81ba4e9c3518edd20bc15, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -2865,6 +3012,10 @@ PrefabInstance: propertyPath: m_Layer value: 5 objectReference: {fileID: 0} + - target: {fileID: 3693621164834956602, guid: 0e3ec9c120c21d0418fa821cb0d797e7, type: 3} + propertyPath: m_fontSize + value: 29.85 + objectReference: {fileID: 0} - target: {fileID: 4995742142680764687, guid: 0e3ec9c120c21d0418fa821cb0d797e7, type: 3} propertyPath: m_Layer value: 5 @@ -2903,7 +3054,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5386841602357123115, guid: 0e3ec9c120c21d0418fa821cb0d797e7, type: 3} propertyPath: m_SizeDelta.y - value: 231.776 + value: 174.965 objectReference: {fileID: 0} - target: {fileID: 5386841602357123115, guid: 0e3ec9c120c21d0418fa821cb0d797e7, type: 3} propertyPath: m_LocalPosition.x @@ -2939,7 +3090,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5386841602357123115, guid: 0e3ec9c120c21d0418fa821cb0d797e7, type: 3} propertyPath: m_AnchoredPosition.y - value: -423 + value: -416.08 objectReference: {fileID: 0} - target: {fileID: 5386841602357123115, guid: 0e3ec9c120c21d0418fa821cb0d797e7, type: 3} propertyPath: m_LocalEulerAnglesHint.x diff --git a/Assets/Prefabs/UI/DistributePointsHolder.prefab b/Assets/Prefabs/UI/DistributePointsHolder.prefab new file mode 100644 index 0000000..d7a178b --- /dev/null +++ b/Assets/Prefabs/UI/DistributePointsHolder.prefab @@ -0,0 +1,315 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &682408470658022631 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8960722644052714329} + m_Layer: 5 + m_Name: DistributePointsHolder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8960722644052714329 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 682408470658022631} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2765236188621606895} + - {fileID: 6845890768576554752} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 44.999, y: 124.93} + m_SizeDelta: {x: 585.26, y: 40.655} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &6734062555894984962 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2765236188621606895} + - component: {fileID: 2035431644685793111} + - component: {fileID: 1301770233523265987} + m_Layer: 5 + m_Name: Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2765236188621606895 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6734062555894984962} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8960722644052714329} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -90.396, y: 0.000090122} + m_SizeDelta: {x: 404.47, y: 40.655} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &2035431644685793111 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6734062555894984962} + m_CullTransparentMesh: 1 +--- !u!114 &1301770233523265987 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6734062555894984962} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Points Left To Distribute + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2} + m_sharedMaterial: {fileID: 9074173216178389243, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4285048005 + m_fontColor: {r: 0.7725491, g: 0.6431373, b: 0.40784317, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 30 + m_fontSizeBase: 36 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 18 + m_fontSizeMax: 30 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &8971464948831131366 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6845890768576554752} + - component: {fileID: 4248153024606371341} + - component: {fileID: 2155467361723096455} + m_Layer: 5 + m_Name: Points + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6845890768576554752 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8971464948831131366} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8960722644052714329} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 202.23, y: 0.00016283989} + m_SizeDelta: {x: 180.79, y: 40.655} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4248153024606371341 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8971464948831131366} + m_CullTransparentMesh: 1 +--- !u!114 &2155467361723096455 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8971464948831131366} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: '-1 + +' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2} + m_sharedMaterial: {fileID: 9074173216178389243, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4290441215 + m_fontColor: {r: 1, g: 0.9357055, b: 0.7311321, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 32.3 + m_fontSizeBase: 33.41 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 18 + m_fontSizeMax: 32.3 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} diff --git a/Assets/Prefabs/UI/DistributePointsHolder.prefab.meta b/Assets/Prefabs/UI/DistributePointsHolder.prefab.meta new file mode 100644 index 0000000..7d71c01 --- /dev/null +++ b/Assets/Prefabs/UI/DistributePointsHolder.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dd15953ddb58a6b4da3ec6a5aa2de4e4 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/UI/Stat.prefab b/Assets/Prefabs/UI/Stat.prefab index 82e5c34..8387557 100644 --- a/Assets/Prefabs/UI/Stat.prefab +++ b/Assets/Prefabs/UI/Stat.prefab @@ -34,8 +34,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: -24.042} - m_SizeDelta: {x: 585.26, y: 48.083} + m_AnchoredPosition: {x: 0, y: -13.5317} + m_SizeDelta: {x: 585.26, y: 27.0624} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &3160804064368974612 CanvasRenderer: @@ -92,7 +92,7 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 41.29 + m_fontSize: 27.05 m_fontSizeBase: 36 m_fontWeight: 400 m_enableAutoSizing: 1 @@ -171,8 +171,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.0000089605, y: -24.042} - m_SizeDelta: {x: 585.26, y: 48.083} + m_AnchoredPosition: {x: 0.0000089605, y: -14.933} + m_SizeDelta: {x: 585.26, y: 29.866} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &2863543555153007270 CanvasRenderer: @@ -246,8 +246,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 24.041986} - m_SizeDelta: {x: 585.26, y: 48.084} + m_AnchoredPosition: {x: 0, y: 14.933} + m_SizeDelta: {x: 585.26, y: 29.866} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &3307275238057276249 CanvasRenderer: @@ -304,7 +304,7 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 39.7 + m_fontSize: 29.85 m_fontSizeBase: 36 m_fontWeight: 400 m_enableAutoSizing: 1 @@ -385,8 +385,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.0000021458, y: 67.806} - m_SizeDelta: {x: 585.26, y: 96.168} + m_AnchoredPosition: {x: -0.0000021458, y: 86.023834} + m_SizeDelta: {x: 585.26, y: 59.7323} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &5615690787398563521 MonoBehaviour: diff --git a/Packages/com.jovian.ingame-logging/Runtime/InGameLogger.cs b/Packages/com.jovian.ingame-logging/Runtime/InGameLogger.cs index ad6893f..2bf0e63 100644 --- a/Packages/com.jovian.ingame-logging/Runtime/InGameLogger.cs +++ b/Packages/com.jovian.ingame-logging/Runtime/InGameLogger.cs @@ -17,7 +17,8 @@ namespace Jovian.InGameLogging { [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Log(string message, string hexColor) { - store.Add(channel, $"{message}"); + var prefix = hexColor.Length > 0 && hexColor[0] == '#' ? "" : "#"; + store.Add(channel, $"{message}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Packages/com.jovian.savesystem/Editor/Jovian.SaveSystem.Editor.asmdef b/Packages/com.jovian.savesystem/Editor/Jovian.SaveSystem.Editor.asmdef index 47354b3..2bb2e15 100644 --- a/Packages/com.jovian.savesystem/Editor/Jovian.SaveSystem.Editor.asmdef +++ b/Packages/com.jovian.savesystem/Editor/Jovian.SaveSystem.Editor.asmdef @@ -9,8 +9,10 @@ ], "excludePlatforms": [], "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], + "overrideReferences": true, + "precompiledReferences": [ + "Newtonsoft.Json.dll" + ], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], diff --git a/Packages/com.jovian.savesystem/Editor/SaveDataViewerWindow.cs b/Packages/com.jovian.savesystem/Editor/SaveDataViewerWindow.cs new file mode 100644 index 0000000..ef4770c --- /dev/null +++ b/Packages/com.jovian.savesystem/Editor/SaveDataViewerWindow.cs @@ -0,0 +1,351 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using UnityEditor; +using UnityEngine; + +namespace Jovian.SaveSystem.Editor { + public class SaveDataViewerWindow : EditorWindow { + private Vector2 slotListScroll; + private Vector2 dataScroll; + + private List sessions = new(); + private SlotEntry selectedSlot; + private string savePath; + + // Parsed data for the selected slot + private SaveEnvelope loadedEnvelope; + private List payloadFields = new(); + + private GUIStyle labelStyle; + private GUIStyle valueStyle; + private GUIStyle headerStyle; + private GUIStyle slotButtonStyle; + private GUIStyle noDataStyle; + private GUIStyle metaKeyStyle; + private GUIStyle metaValueStyle; + private bool stylesInitialized; + + [MenuItem("Jovian/Save System/Save Data Viewer")] + public static void ShowWindow() { + var window = GetWindow("Save Data Viewer"); + window.minSize = new Vector2(550, 450); + window.Show(); + } + + private void OnEnable() { + ScanSaves(); + } + + private void InitStyles() { + if(stylesInitialized) { + return; + } + + headerStyle = new GUIStyle(EditorStyles.boldLabel) { + fontSize = 12 + }; + + labelStyle = new GUIStyle(EditorStyles.label) { + fontStyle = FontStyle.Bold, + normal = { textColor = new Color(0.55f, 0.8f, 1f) } + }; + + valueStyle = new GUIStyle(EditorStyles.label) { + wordWrap = true + }; + + slotButtonStyle = new GUIStyle(EditorStyles.toolbarButton) { + alignment = TextAnchor.MiddleLeft, + fixedHeight = 22 + }; + + noDataStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel) { + fontSize = 12, + wordWrap = true + }; + + metaKeyStyle = new GUIStyle(EditorStyles.miniLabel) { + fontStyle = FontStyle.Bold, + fixedWidth = 100 + }; + + metaValueStyle = new GUIStyle(EditorStyles.miniLabel); + + stylesInitialized = true; + } + + private void OnGUI() { + InitStyles(); + DrawToolbar(); + + if(sessions.Count == 0) { + EditorGUILayout.LabelField("No save files found.\nPlay the game and save to generate data.", + noDataStyle, GUILayout.ExpandHeight(true)); + return; + } + + DrawSlotList(); + DrawSaveData(); + } + + private void DrawToolbar() { + EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); + GUILayout.FlexibleSpace(); + if(GUILayout.Button("Refresh", EditorStyles.toolbarButton, GUILayout.Width(60))) { + ScanSaves(); + } + EditorGUILayout.EndHorizontal(); + } + + private void DrawSlotList() { + EditorGUILayout.LabelField("Save Slots", headerStyle); + slotListScroll = EditorGUILayout.BeginScrollView(slotListScroll, GUILayout.MaxHeight(200)); + + foreach(var session in sessions) { + session.foldout = EditorGUILayout.Foldout(session.foldout, session.displayName, true); + if(!session.foldout) { + continue; + } + + EditorGUI.indentLevel++; + foreach(var slot in session.slots) { + var isSelected = selectedSlot != null && selectedSlot.filePath == slot.filePath; + var style = new GUIStyle(slotButtonStyle); + if(isSelected) { + style.fontStyle = FontStyle.Bold; + } + + if(GUILayout.Button($" {slot.displayName}", style)) { + LoadSlot(slot); + } + } + EditorGUI.indentLevel--; + } + + EditorGUILayout.EndScrollView(); + DrawSeparator(); + } + + private void DrawSaveData() { + if(selectedSlot == null) { + EditorGUILayout.LabelField("Select a save slot to view its data.", noDataStyle, GUILayout.ExpandHeight(true)); + return; + } + + if(loadedEnvelope == null) { + EditorGUILayout.LabelField("Failed to load save data.", noDataStyle, GUILayout.ExpandHeight(true)); + return; + } + + // Envelope metadata + EditorGUILayout.LabelField("Envelope", headerStyle); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Version:", metaKeyStyle); + EditorGUILayout.LabelField(loadedEnvelope.version.ToString(), metaValueStyle); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Slot Type:", metaKeyStyle); + EditorGUILayout.LabelField(loadedEnvelope.slotType.ToString(), metaValueStyle); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Timestamp:", metaKeyStyle); + var timestamp = DateTimeOffset.FromUnixTimeMilliseconds(loadedEnvelope.timestampUtc).LocalDateTime; + EditorGUILayout.LabelField(timestamp.ToString("yyyy-MM-dd HH:mm:ss"), metaValueStyle); + EditorGUILayout.EndHorizontal(); + + DrawSeparator(); + EditorGUILayout.Space(4); + EditorGUILayout.LabelField("Payload", headerStyle); + + if(payloadFields.Count == 0) { + EditorGUILayout.LabelField("(empty payload)", noDataStyle); + return; + } + + dataScroll = EditorGUILayout.BeginScrollView(dataScroll, GUILayout.ExpandHeight(true)); + + foreach(var field in payloadFields) { + DrawPayloadField(field, 0); + } + + EditorGUILayout.EndScrollView(); + } + + private void DrawPayloadField(PayloadField field, int depth) { + if(field.children != null && field.children.Count > 0) { + EditorGUI.indentLevel = depth; + field.foldout = EditorGUILayout.Foldout(field.foldout, field.key, true, EditorStyles.foldoutHeader); + if(field.foldout) { + foreach(var child in field.children) { + DrawPayloadField(child, depth + 1); + } + } + } + else { + EditorGUI.indentLevel = depth; + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(field.key, labelStyle, GUILayout.Width(180)); + EditorGUILayout.LabelField(field.value, valueStyle); + EditorGUILayout.EndHorizontal(); + } + } + + private void DrawSeparator() { + var rect = EditorGUILayout.GetControlRect(false, 1); + EditorGUI.DrawRect(rect, new Color(0.3f, 0.3f, 0.3f)); + } + + private void ScanSaves() { + sessions.Clear(); + selectedSlot = null; + loadedEnvelope = null; + payloadFields.Clear(); + + var settings = SaveSystemSettings.Load(); + savePath = Path.Combine(Application.persistentDataPath, settings.saveDirectoryName); + var indexPath = Path.Combine(savePath, "index.json"); + + if(!File.Exists(indexPath)) { + return; + } + + try { + var indexJson = File.ReadAllText(indexPath); + var index = JsonConvert.DeserializeObject(indexJson); + if(index?.sessions == null || index.slots == null) { + return; + } + + var slotsBySession = new Dictionary>(); + foreach(var slot in index.slots) { + if(!slotsBySession.ContainsKey(slot.sessionId)) { + slotsBySession[slot.sessionId] = new List(); + } + slotsBySession[slot.sessionId].Add(new SlotEntry { + sessionId = slot.sessionId, + displayName = slot.DisplayLabel + " " + DateTimeOffset.FromUnixTimeMilliseconds(slot.timestampUtc).LocalDateTime.ToString("HH:mm"), + filePath = slot.filePath, + timestampUtc = slot.timestampUtc + }); + } + + foreach(var session in index.sessions) { + if(!slotsBySession.TryGetValue(session.sessionId, out var slots)) { + continue; + } + slots.Sort((a, b) => b.timestampUtc.CompareTo(a.timestampUtc)); + sessions.Add(new SessionGroup { + sessionId = session.sessionId, + displayName = $"Session ({DateTimeOffset.FromUnixTimeMilliseconds(session.lastSaveDateUtc).LocalDateTime:yyyy-MM-dd HH:mm})", + foldout = true, + slots = slots + }); + } + + sessions.Sort((a, b) => string.Compare(b.sessionId, a.sessionId, StringComparison.Ordinal)); + } + catch(Exception e) { + Debug.LogError($"[SaveDataViewer] Failed to read save index: {e.Message}"); + } + } + + private void LoadSlot(SlotEntry slot) { + selectedSlot = slot; + loadedEnvelope = null; + payloadFields.Clear(); + + var fullPath = Path.Combine(savePath, slot.filePath); + if(!File.Exists(fullPath)) { + Debug.LogWarning($"[SaveDataViewer] Save file not found: {fullPath}"); + return; + } + + try { + var bytes = File.ReadAllBytes(fullPath); + var serializer = new JsonSaveSerializer(); + loadedEnvelope = serializer.Deserialize(bytes); + + if(loadedEnvelope?.payload == null) { + return; + } + + payloadFields = ParseToken(loadedEnvelope.payload); + } + catch(Exception e) { + Debug.LogError($"[SaveDataViewer] Failed to load save: {e.Message}"); + } + } + + private static List ParseToken(JToken token) { + var fields = new List(); + + if(token is JObject obj) { + foreach(var property in obj.Properties()) { + fields.Add(ParseProperty(property.Name, property.Value)); + } + } + else if(token is JArray arr) { + for(var i = 0; i < arr.Count; i++) { + fields.Add(ParseProperty($"[{i}]", arr[i])); + } + } + + return fields; + } + + private static PayloadField ParseProperty(string key, JToken value) { + switch(value.Type) { + case JTokenType.Object: { + var children = ParseToken(value); + return new PayloadField { key = key, value = $"{{{children.Count} fields}}", children = children, foldout = false }; + } + case JTokenType.Array: { + var arr = (JArray)value; + var children = new List(); + for(var i = 0; i < arr.Count; i++) { + children.Add(ParseProperty($"[{i}]", arr[i])); + } + return new PayloadField { key = $"{key} ({arr.Count})", value = $"[{arr.Count} items]", children = children, foldout = false }; + } + case JTokenType.Null: + return new PayloadField { key = key, value = "null" }; + default: + return new PayloadField { key = key, value = value.ToString() }; + } + } + + // Internal types + + [Serializable] + private sealed class SaveIndex { + public List sessions; + public List slots; + } + + private sealed class SessionGroup { + public string sessionId; + public string displayName; + public bool foldout; + public List slots; + } + + private sealed class SlotEntry { + public string sessionId; + public string displayName; + public string filePath; + public long timestampUtc; + } + + private sealed class PayloadField { + public string key; + public string value; + public List children; + public bool foldout; + } + } +} diff --git a/Packages/com.jovian.savesystem/Editor/SaveDataViewerWindow.cs.meta b/Packages/com.jovian.savesystem/Editor/SaveDataViewerWindow.cs.meta new file mode 100644 index 0000000..9ca899a --- /dev/null +++ b/Packages/com.jovian.savesystem/Editor/SaveDataViewerWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 08949eb1c0741e34b89fe2be65c1325b \ No newline at end of file