using System; using System.Collections; using System.Collections.Generic; using System.Text; using BlockSpace.Account; using BlockSpace.Worlds; using UnityEngine; namespace BlockSpace.Voxels { [DisallowMultipleComponent] public sealed class InventoryController : MonoBehaviour { private enum InventoryMode { Avatars = 0, Creations = 1 } private const float ButtonPressCooldown = 0.2f; private const float ButtonShrinkDuration = 0.08f; private const float ButtonSettleDuration = 0.12f; private const float ButtonPressedScale = 0.84f; private const float ButtonOvershootScale = 1.04f; private const string ButtonThockResourcePath = "Thock"; private const float AvatarVoxelSizeScale = 0.5f; private const float CreationPreviewMaxSize = 3f; [SerializeField] private VoxelGroupManager groupManager; [SerializeField] private VRVoxelBrush voxelBrush; [SerializeField] private string backendBaseUrl = "https://blockspacebackend-production.up.railway.app"; [Header("Buttons")] [SerializeField] private Collider avatarsButton; [SerializeField] private Collider creationsButton; [SerializeField] private Collider backButton; [SerializeField] private Collider nextButton; [SerializeField] private Collider saveButton; [SerializeField] private Collider loadButton; [Header("Previews")] [SerializeField] private Transform creationPreviewRoot; [SerializeField] private Transform previewHead; [SerializeField] private Transform previewLeft; [SerializeField] private Transform previewRight; [SerializeField] private AudioClip buttonThockClip; [SerializeField] private bool debugLogs; private readonly Dictionary buttonBaseScales = new Dictionary(); private readonly Dictionary buttonAnimationCoroutines = new Dictionary(); private readonly Dictionary transformCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private InventoryMode mode; private readonly List ids = new List(); private int slotIndex; private string equippedAvatarId = string.Empty; private bool slotsDirty = true; private float nextButtonPressTime; private bool avatarsInsidePrev; private bool creationsInsidePrev; private bool backInsidePrev; private bool nextInsidePrev; private bool saveInsidePrev; private bool loadInsidePrev; private VoxelGroup previewCreationGroup; private readonly List previewCreationGroups = new List(); private VoxelGroup previewHeadGroup; private VoxelGroup previewLeftGroup; private VoxelGroup previewRightGroup; private bool warnedMissingAvatarPreviewRoots; private bool warnedMissingCreationPreviewRoot; private InventoryBackendClient client; private int previewRequestToken; public void Configure(VoxelGroupManager manager, VRVoxelBrush brush) { if (manager != null) { groupManager = manager; } if (brush != null) { voxelBrush = brush; } ResolveSceneReferences(); EnsureButtonAudioAssigned(); EnsurePreviewGroups(); client = new InventoryBackendClient(backendBaseUrl); slotsDirty = true; } private void Awake() { ResolveSceneReferences(); EnsureButtonAudioAssigned(); EnsurePreviewGroups(); client = new InventoryBackendClient(backendBaseUrl); } private void OnDisable() { StopAllCoroutines(); ResetAllButtonScales(); } private void Update() { ResolveSceneReferences(); EnsurePreviewGroups(); if (!(groupManager == null)) { HandleButton(avatarsButton, ref avatarsInsidePrev, delegate { SwitchMode(InventoryMode.Avatars); }); HandleButton(creationsButton, ref creationsInsidePrev, delegate { SwitchMode(InventoryMode.Creations); }); HandleButton(backButton, ref backInsidePrev, PrevSlot); HandleButton(nextButton, ref nextInsidePrev, NextSlot); HandleButton(saveButton, ref saveInsidePrev, SaveCurrentSlot); HandleButton(loadButton, ref loadInsidePrev, LoadCurrentSlot); if (slotsDirty) { slotsDirty = false; StartCoroutine(RefreshSlots()); } } } private void SwitchMode(InventoryMode newMode) { if (mode != newMode) { mode = newMode; slotIndex = 0; if (mode == InventoryMode.Avatars) { ApplyEmptyCreationPreview(); } else { ApplyEmptyAvatarPreview(); } slotsDirty = true; } } private void PrevSlot() { if (ids.Count <= 0) { slotIndex = 0; return; } slotIndex = (slotIndex - 1 + ids.Count) % ids.Count; UpdatePreviewsForSelectedSlot(); } private void NextSlot() { if (ids.Count <= 0) { slotIndex = 0; return; } slotIndex = (slotIndex + 1) % ids.Count; UpdatePreviewsForSelectedSlot(); } private void SaveCurrentSlot() { string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId(); if (string.IsNullOrEmpty(userFolderId) || client == null || (mode == InventoryMode.Creations && (groupManager == null || !groupManager.CreatorMode || groupManager.EditingLocked))) { return; } string orCreateSelectedId = GetOrCreateSelectedId(); if (!string.IsNullOrEmpty(orCreateSelectedId)) { if (mode == InventoryMode.Avatars) { StartCoroutine(SaveAvatarSlot(userFolderId, orCreateSelectedId)); } else { StartCoroutine(SaveCreationSlot(userFolderId, orCreateSelectedId)); } } } private void LoadCurrentSlot() { string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId(); if (!string.IsNullOrEmpty(userFolderId) && client != null && (mode != InventoryMode.Creations || (!(groupManager == null) && groupManager.CreatorMode && !groupManager.EditingLocked)) && TryGetSelectedId(out var id) && !string.IsNullOrEmpty(id)) { if (mode == InventoryMode.Avatars) { StartCoroutine(LoadAndEquipAvatar(userFolderId, id)); } else { StartCoroutine(LoadCreationIntoWorld(userFolderId, id)); } } } private IEnumerator RefreshSlots() { string playerId = BlockSpaceAccountPrefs.GetUserFolderId(); if (string.IsNullOrEmpty(playerId) || client == null) { ids.Clear(); yield break; } InventoryBackendClient.ListResponse response = null; if (mode == InventoryMode.Avatars) { yield return client.ListAvatars(playerId, delegate(InventoryBackendClient.ListResponse r) { response = r; }); } else { yield return client.ListCreations(playerId, delegate(InventoryBackendClient.ListResponse r) { response = r; }); } ids.Clear(); if (response != null && response.ok && response.ids != null) { ids.AddRange(response.ids); } ids.Add(string.Empty); equippedAvatarId = ((response != null) ? (response.equippedId ?? string.Empty) : string.Empty); if (debugLogs) { Debug.Log($"InventoryController: mode={mode} playerIdLen={playerId.Length} ids={ids.Count} equipped={equippedAvatarId}"); } slotIndex = Mathf.Clamp(slotIndex, 0, Mathf.Max(0, ids.Count - 1)); UpdatePreviewsForSelectedSlot(); if (mode != InventoryMode.Avatars || !BlockSpaceAccountPrefs.IsLinked) { yield break; } bool flag = false; for (int num = 0; num < ids.Count; num++) { if (!string.IsNullOrEmpty(ids[num])) { flag = true; break; } } if (flag || !AvatarVoxelStorage.TryLoadLocal(out var localPayload) || !AvatarPayloadHasVoxels(localPayload)) { yield break; } string newId = MakeNewId(); InventoryBackendClient.PutResponse put = null; yield return client.UploadAvatar(playerId, newId, AvatarVoxelStorage.Serialize(localPayload), delegate(InventoryBackendClient.PutResponse r) { put = r; }); if (put != null && put.ok && put.saved) { InventoryBackendClient.EquipResponse equip = null; yield return client.EquipAvatar(playerId, newId, delegate(InventoryBackendClient.EquipResponse r) { equip = r; }); PhotonVoxelAvatar localOwnedInstance = PhotonVoxelAvatar.LocalOwnedInstance; if (localOwnedInstance != null) { localOwnedInstance.ApplyAndBroadcastAvatar(localPayload); } if (PlayerPrefs.HasKey("BlockSpace.Avatar.Payload")) { PlayerPrefs.DeleteKey("BlockSpace.Avatar.Payload"); PlayerPrefs.Save(); } slotsDirty = true; } } private static bool AvatarPayloadHasVoxels(AvatarVoxelPayload payload) { if (payload == null) { return false; } if (!GroupDataHasVoxels(payload.headBody) && !GroupDataHasVoxels(payload.left)) { return GroupDataHasVoxels(payload.right); } return true; } private static bool AvatarPayloadHasAnyBase64(AvatarVoxelPayload payload) { if (payload == null) { return false; } if (string.IsNullOrEmpty(payload.headBody?.voxelsBase64) && string.IsNullOrEmpty(payload.left?.voxelsBase64)) { return !string.IsNullOrEmpty(payload.right?.voxelsBase64); } return true; } private static int AvatarPayloadBase64TotalLength(AvatarVoxelPayload payload) { if (payload == null) { return 0; } return (payload.headBody?.voxelsBase64 ?? string.Empty).Length + (payload.left?.voxelsBase64 ?? string.Empty).Length + (payload.right?.voxelsBase64 ?? string.Empty).Length; } private static bool GroupDataHasVoxels(AvatarVoxelGroupData group) { if (group == null) { return false; } if (group.sizeX <= 0 || group.sizeY <= 0 || group.sizeZ <= 0) { return false; } return true; } private IEnumerator SaveAvatarSlot(string playerId, string avatarId) { AvatarCustomizerController avatarCustomizerController = UnityEngine.Object.FindObjectOfType(); PhotonVoxelAvatar runtime = PhotonVoxelAvatar.LocalOwnedInstance; AvatarVoxelPayload editorPayload = null; VoxelGroup headBody = null; VoxelGroup left = null; VoxelGroup right = null; if (avatarCustomizerController != null && avatarCustomizerController.TryGetEditorGroups(out headBody, out left, out right)) { editorPayload = AvatarVoxelStorage.CapturePayload(headBody, left, right); } AvatarVoxelPayload avatarVoxelPayload = ((runtime != null) ? runtime.CaptureRuntimePayload() : null); AvatarVoxelPayload payload; AvatarVoxelPayload localPayload = (AvatarVoxelStorage.TryLoadLocal(out payload) ? payload : null); AvatarVoxelPayload payload2 = (AvatarPayloadHasAnyBase64(editorPayload) ? editorPayload : (AvatarPayloadHasAnyBase64(avatarVoxelPayload) ? avatarVoxelPayload : (AvatarPayloadHasAnyBase64(localPayload) ? localPayload : (AvatarPayloadHasVoxels(editorPayload) ? editorPayload : (AvatarPayloadHasVoxels(avatarVoxelPayload) ? avatarVoxelPayload : (AvatarPayloadHasVoxels(localPayload) ? localPayload : (editorPayload ?? avatarVoxelPayload ?? localPayload ?? AvatarVoxelStorage.CreateEmptyPayload()))))))); if (runtime != null && AvatarPayloadBase64TotalLength(payload2) <= 0 && !runtime.HasRuntimeGroups) { float waited = 0f; while (waited < 1.5f && !runtime.HasRuntimeGroups) { waited += 0.1f; yield return new WaitForSeconds(0.1f); } avatarVoxelPayload = runtime.CaptureRuntimePayload(); if (AvatarPayloadHasAnyBase64(avatarVoxelPayload)) { payload2 = avatarVoxelPayload; } else if (AvatarPayloadHasAnyBase64(localPayload)) { payload2 = localPayload; } } string text = AvatarVoxelStorage.Serialize(payload2); if (text == "{}" || text.Length <= 4 || AvatarPayloadBase64TotalLength(payload2) <= 0) { Debug.LogWarning($"InventoryController: refusing to upload empty avatar payload slot={avatarId} jsonLen={text.Length} base64Total={AvatarPayloadBase64TotalLength(payload2)}"); yield break; } if (debugLogs) { int num = ((headBody != null && headBody.voxels != null) ? headBody.voxels.Length : 0); int num2 = ((left != null && left.voxels != null) ? left.voxels.Length : 0); int num3 = ((right != null && right.voxels != null) ? right.voxels.Length : 0); int num4 = Mathf.Max(0, payload2.headBody?.sizeX ?? 0) * Mathf.Max(0, payload2.headBody?.sizeY ?? 0) * Mathf.Max(0, payload2.headBody?.sizeZ ?? 0); int num5 = Mathf.Max(0, payload2.left?.sizeX ?? 0) * Mathf.Max(0, payload2.left?.sizeY ?? 0) * Mathf.Max(0, payload2.left?.sizeZ ?? 0); int num6 = Mathf.Max(0, payload2.right?.sizeX ?? 0) * Mathf.Max(0, payload2.right?.sizeY ?? 0) * Mathf.Max(0, payload2.right?.sizeZ ?? 0); int length = (payload2.headBody?.voxelsBase64 ?? string.Empty).Length; int num7 = AvatarPayloadBase64TotalLength(payload2); string text2 = ((payload2 == editorPayload) ? "editor" : ((payload2 == avatarVoxelPayload) ? "runtime" : ((payload2 == localPayload) ? "local" : "other"))); Debug.Log($"InventoryController: saving avatar slot={avatarId} chosen={text2} editor(head={num},left={num2},right={num3}) payloadExpected(head={num4},left={num5},right={num6}) headB64Len={length} b64Total={num7} jsonLen={text.Length}"); } AvatarVoxelStorage.SaveLocal(payload2); InventoryBackendClient.PutResponse put = null; yield return client.UploadAvatar(playerId, avatarId, text, delegate(InventoryBackendClient.PutResponse r) { put = r; }); if (put != null && put.ok && put.saved) { slotsDirty = true; if (debugLogs) { string roundTrip = string.Empty; yield return client.DownloadAvatar(playerId, avatarId, delegate(string t) { roundTrip = t; }); int num8 = ((roundTrip != null) ? Encoding.UTF8.GetByteCount(roundTrip) : 0); int num9 = 0; if (!string.IsNullOrEmpty(roundTrip) && AvatarVoxelStorage.TryDeserialize(roundTrip, out var payload3)) { num9 = (payload3.headBody?.voxelsBase64 ?? string.Empty).Length; } Debug.Log($"InventoryController: avatar roundtrip bytes={num8} headB64Len={num9}"); } } else if (debugLogs) { Debug.LogWarning(string.Format("InventoryController: UploadAvatar failed ok={0} saved={1} err={2}", put != null && put.ok, put != null && put.saved, (put != null) ? put.error : "null")); } } private IEnumerator LoadAndEquipAvatar(string playerId, string avatarId) { string json = string.Empty; yield return client.DownloadAvatar(playerId, avatarId, delegate(string t) { json = t; }); if (!string.IsNullOrWhiteSpace(json) && AvatarVoxelStorage.TryDeserialize(json, out var payload)) { AvatarCustomizerController avatarCustomizerController = UnityEngine.Object.FindObjectOfType(); if (avatarCustomizerController != null && avatarCustomizerController.TryGetEditorGroups(out var headBody, out var left, out var right)) { AvatarVoxelStorage.ApplyPayload(payload, headBody, left, right, groupManager, useLocalGridTransform: true); } PhotonVoxelAvatar localOwnedInstance = PhotonVoxelAvatar.LocalOwnedInstance; if (localOwnedInstance != null) { localOwnedInstance.ApplyAndBroadcastAvatar(payload); } InventoryBackendClient.EquipResponse equip = null; yield return client.EquipAvatar(playerId, avatarId, delegate(InventoryBackendClient.EquipResponse r) { equip = r; }); slotsDirty = true; } } private IEnumerator SaveCreationSlot(string playerId, string creationId) { VRGroupSelector instance = VRGroupSelector.Instance; List list = ((instance != null) ? instance.GetSelectedGroupsSnapshot() : new List()); NodeGraphManager instance2 = NodeGraphManager.Instance; List list2 = ((instance2 != null) ? instance2.GetSelectedNodesSnapshot() : new List()); bool num = list != null && list.Count > 0; bool flag = list2 != null && list2.Count > 0; if (!num && !flag) { yield break; } HashSet hashSet = null; if (flag) { hashSet = new HashSet(); for (int i = 0; i < list2.Count; i++) { if (list2[i] != null) { hashSet.Add(list2[i].NodeId); } } } byte[] gzipBytes = WorldGroupsCodec.EncodeGzip(list, (instance2 != null) ? instance2.BuildWorldNodeRecords(hashSet) : null, (instance2 != null) ? instance2.BuildWorldWireRecords(hashSet, BuildGroupSaveIndexMap(list)) : null); InventoryBackendClient.PutResponse put = null; yield return client.UploadCreation(playerId, creationId, gzipBytes, delegate(InventoryBackendClient.PutResponse r) { put = r; }); if (put != null && put.ok && put.saved) { slotsDirty = true; } } private IEnumerator LoadCreationIntoWorld(string playerId, string creationId) { byte[] gzip = null; yield return client.DownloadCreation(playerId, creationId, delegate(byte[] b) { gzip = b; }); if (gzip == null) { gzip = Array.Empty(); } if (!WorldGroupsCodec.TryDecodeGzip(gzip, out WorldGroupsCodec.WorldSaveData saveData) || groupManager == null || !TryGetSpawnGridPosition(out var gridPosition)) { yield break; } List list = saveData.groupRecords ?? new List(); List list2 = saveData.nodeRecords ?? new List(); if (list.Count == 0 && list2.Count == 0) { yield break; } float voxelSize = groupManager.VoxelSize; Vector3 vector = ((list.Count > 0) ? new Vector3((float)list[0].position.x * voxelSize, (float)list[0].position.y * voxelSize, (float)list[0].position.z * voxelSize) : list2[0].position); for (int num = 1; num < list.Count; num++) { Vector3 rhs = new Vector3((float)list[num].position.x * voxelSize, (float)list[num].position.y * voxelSize, (float)list[num].position.z * voxelSize); vector = Vector3.Min(vector, rhs); } for (int num2 = 0; num2 < list2.Count; num2++) { vector = Vector3.Min(vector, list2[num2].position); } Dictionary dictionary = new Dictionary(); for (int num3 = 0; num3 < list.Count; num3++) { WorldGroupsCodec.WorldGroupRecord worldGroupRecord = list[num3]; VoxelGroupManager.SavedGroupData data = new VoxelGroupManager.SavedGroupData { position = worldGroupRecord.position, packedRotation = worldGroupRecord.packedRotation, size = worldGroupRecord.size, voxels = worldGroupRecord.voxels }; Vector3 vector2 = new Vector3((float)worldGroupRecord.position.x * voxelSize, (float)worldGroupRecord.position.y * voxelSize, (float)worldGroupRecord.position.z * voxelSize); Vector3Int vector3Int = new Vector3Int(Mathf.RoundToInt((vector2.x - vector.x) / voxelSize), Mathf.RoundToInt((vector2.y - vector.y) / voxelSize), Mathf.RoundToInt((vector2.z - vector.z) / voxelSize)); VoxelGroup voxelGroup = groupManager.SpawnSavedGroup(data, gridPosition + vector3Int); if (voxelGroup != null) { dictionary[num3] = voxelGroup.id; } } if (list2.Count > 0) { NodeGraphManager instance = NodeGraphManager.Instance; if (instance != null) { instance.ApplySavedGraph(positionOffset: new Vector3((float)gridPosition.x * voxelSize, (float)gridPosition.y * voxelSize, (float)gridPosition.z * voxelSize) - vector, nodeRecords: list2, wireRecords: saveData.wireRecords, clearExisting: false, broadcast: true, savedGroupIndexToRuntimeId: dictionary); } } } private static Dictionary BuildGroupSaveIndexMap(IList groups) { Dictionary dictionary = new Dictionary(); if (groups == null) { return dictionary; } for (int i = 0; i < groups.Count; i++) { VoxelGroup voxelGroup = groups[i]; if (voxelGroup != null) { dictionary[voxelGroup.id] = i; } } return dictionary; } private void UpdatePreviewsForSelectedSlot() { previewRequestToken++; if (mode == InventoryMode.Avatars) { ApplyEmptyAvatarPreview(); StartCoroutine(UpdateAvatarPreviews()); } else { ApplyEmptyCreationPreview(); StartCoroutine(UpdateCreationPreview()); } } private IEnumerator UpdateAvatarPreviews() { EnsurePreviewGroups(); int token = previewRequestToken; string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId(); if (string.IsNullOrEmpty(userFolderId) || client == null) { yield break; } if (!TryGetSelectedId(out var id) || string.IsNullOrEmpty(id)) { ApplyEmptyAvatarPreview(); yield break; } ApplyEmptyAvatarPreview(); string json = string.Empty; yield return client.DownloadAvatar(userFolderId, id, delegate(string t) { json = t; }); if (token != previewRequestToken) { yield break; } if (string.IsNullOrWhiteSpace(json) || !AvatarVoxelStorage.TryDeserialize(json, out var payload)) { if (debugLogs) { string text = json ?? string.Empty; if (text.Length > 120) { text = text.Substring(0, 120); } Debug.LogWarning($"InventoryController: avatar download/parse failed id={id} jsonLen={(json ?? string.Empty).Length} snippet={text}"); } ApplyEmptyAvatarPreview(); yield break; } if (debugLogs) { int num = Mathf.Max(0, payload.headBody?.sizeX ?? 0) * Mathf.Max(0, payload.headBody?.sizeY ?? 0) * Mathf.Max(0, payload.headBody?.sizeZ ?? 0); int num2 = Mathf.Max(0, payload.left?.sizeX ?? 0) * Mathf.Max(0, payload.left?.sizeY ?? 0) * Mathf.Max(0, payload.left?.sizeZ ?? 0); int num3 = Mathf.Max(0, payload.right?.sizeX ?? 0) * Mathf.Max(0, payload.right?.sizeY ?? 0) * Mathf.Max(0, payload.right?.sizeZ ?? 0); Debug.Log($"InventoryController: avatarId={id} headExpected={num} leftExpected={num2} rightExpected={num3} headB64Len={(payload.headBody?.voxelsBase64 ?? string.Empty).Length}"); } ApplyAvatarPreviewPayload(payload); } private IEnumerator UpdateCreationPreview() { EnsurePreviewGroups(); int token = previewRequestToken; string userFolderId = BlockSpaceAccountPrefs.GetUserFolderId(); if (string.IsNullOrEmpty(userFolderId) || client == null) { yield break; } if (!TryGetSelectedId(out var id) || string.IsNullOrEmpty(id)) { ApplyEmptyCreationPreview(); yield break; } ApplyEmptyCreationPreview(); byte[] gzip = null; yield return client.DownloadCreation(userFolderId, id, delegate(byte[] b) { gzip = b; }); if (token == previewRequestToken) { if (gzip == null) { gzip = Array.Empty(); } if (!WorldGroupsCodec.TryDecodeGzip(gzip, out WorldGroupsCodec.WorldSaveData saveData) || saveData.groupRecords == null || saveData.groupRecords.Count == 0) { ApplyEmptyCreationPreview(); } else { ApplyCreationPreviewRecords(saveData.groupRecords); } } } private void ApplyCreationPreviewRecords(List records) { if (creationPreviewRoot == null || groupManager == null) { return; } EnsurePreviewGroups(); Vector3Int vector3Int = records[0].position; Vector3Int vector3Int2 = records[0].position + records[0].size; for (int i = 1; i < records.Count; i++) { Vector3Int position = records[i].position; Vector3Int rhs = records[i].position + records[i].size; vector3Int = Vector3Int.Min(vector3Int, position); vector3Int2 = Vector3Int.Max(vector3Int2, rhs); } while (previewCreationGroups.Count < records.Count) { int count = previewCreationGroups.Count; GameObject obj = new GameObject($"CreationGroupPreview_{count}"); obj.transform.SetParent(creationPreviewRoot, worldPositionStays: false); VoxelGroup voxelGroup = obj.AddComponent(); voxelGroup.id = (ushort)(62500 + count); voxelGroup.SetOwner(groupManager); voxelGroup.SetUseLocalGridTransform(useLocalGrid: true); voxelGroup.SetColliderGeneration(enabled: false); previewCreationGroups.Add(voxelGroup); } for (int j = records.Count; j < previewCreationGroups.Count; j++) { if (previewCreationGroups[j] != null) { previewCreationGroups[j].OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); previewCreationGroups[j].SetVoxelSizeScale(1f, rebuild: false); previewCreationGroups[j].RebuildMesh(); } } for (int k = 0; k < records.Count; k++) { WorldGroupsCodec.WorldGroupRecord worldGroupRecord = records[k]; VoxelGroup voxelGroup2 = previewCreationGroups[k]; if (!(voxelGroup2 == null)) { Vector3Int gridPosition = worldGroupRecord.position - vector3Int; voxelGroup2.SetUseLocalGridTransform(useLocalGrid: true); voxelGroup2.SetColliderGeneration(enabled: false); voxelGroup2.SetOwner(groupManager); voxelGroup2.OverwriteData(gridPosition, worldGroupRecord.packedRotation, worldGroupRecord.size, worldGroupRecord.voxels, queueRebuild: false); voxelGroup2.SetVoxelSizeScale(1f, rebuild: false); voxelGroup2.RebuildMesh(); } } Vector3 vector = vector3Int2 - vector3Int; float num = ((groupManager != null) ? groupManager.VoxelSize : 0.125f); float num2 = Mathf.Max(vector.x, Mathf.Max(vector.y, vector.z)) * num; float num3 = ((num2 > 0.0001f) ? Mathf.Min(1f, 3f / num2) : 1f); creationPreviewRoot.localScale = Vector3.one * num3; } private void ApplyEmptyCreationPreview() { for (int i = 0; i < previewCreationGroups.Count; i++) { if (previewCreationGroups[i] != null) { previewCreationGroups[i].OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); previewCreationGroups[i].RebuildMesh(); } } if (creationPreviewRoot != null) { creationPreviewRoot.localScale = Vector3.one; } } private void ApplyAvatarPreviewPayload(AvatarVoxelPayload payload) { if (!(groupManager == null)) { EnsurePreviewGroups(); if (previewHeadGroup != null) { AvatarVoxelStorage.ApplyGroupData(previewHeadGroup, payload.headBody, groupManager, useLocalGridTransform: true); } if (previewLeftGroup != null) { AvatarVoxelStorage.ApplyGroupData(previewLeftGroup, payload.left, groupManager, useLocalGridTransform: true); } if (previewRightGroup != null) { AvatarVoxelStorage.ApplyGroupData(previewRightGroup, payload.right, groupManager, useLocalGridTransform: true); } if (previewHeadGroup != null) { previewHeadGroup.SetVoxelSizeScale(0.5f, rebuild: false); previewHeadGroup.RebuildMesh(); } if (previewLeftGroup != null) { previewLeftGroup.SetVoxelSizeScale(0.5f, rebuild: false); previewLeftGroup.RebuildMesh(); } if (previewRightGroup != null) { previewRightGroup.SetVoxelSizeScale(0.5f, rebuild: false); previewRightGroup.RebuildMesh(); } } } private void ApplyEmptyAvatarPreview() { EnsurePreviewGroups(); if (!(previewHeadGroup == null)) { previewHeadGroup.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); previewLeftGroup.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); previewRightGroup.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); previewHeadGroup.RebuildMesh(); previewLeftGroup.RebuildMesh(); previewRightGroup.RebuildMesh(); } } private void ResolveSceneReferences() { if (groupManager == null) { groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : UnityEngine.Object.FindObjectOfType()); } if (voxelBrush == null) { voxelBrush = ((VRVoxelBrush.Instance != null) ? VRVoxelBrush.Instance : UnityEngine.Object.FindObjectOfType()); } if (avatarsButton == null) { avatarsButton = FindCollider("Avatars"); } if (creationsButton == null) { creationsButton = FindCollider("Creations"); } if (backButton == null) { backButton = FindCollider("Back"); } if (nextButton == null) { nextButton = FindCollider("Next"); } if (saveButton == null) { saveButton = FindCollider("Save"); } if (loadButton == null) { loadButton = FindCollider("Load"); } if (creationPreviewRoot == null) { creationPreviewRoot = FindTransform("Preview"); } if (previewRight == null) { previewRight = FindTransform("PrevR"); } if (previewHead == null) { previewHead = FindTransform("PrevH"); } if (previewLeft == null) { previewLeft = FindTransform("PrevL"); } } private Collider FindCollider(string name) { Transform transform = base.transform.Find("UI/" + name); if (transform == null) { GameObject gameObject = GameObject.Find(name); transform = ((gameObject != null) ? gameObject.transform : null); } if (!(transform != null)) { return null; } return transform.GetComponent(); } private Transform FindTransform(string name) { if (string.IsNullOrEmpty(name)) { return null; } if (transformCache.TryGetValue(name, out var value) && value != null) { return value; } Transform transform = base.transform.Find(name); if (transform != null) { transformCache[name] = transform; return transform; } Transform[] componentsInChildren = GetComponentsInChildren(includeInactive: true); foreach (Transform transform2 in componentsInChildren) { if (transform2 != null && string.Equals(transform2.name, name, StringComparison.OrdinalIgnoreCase)) { transformCache[name] = transform2; return transform2; } } GameObject gameObject = GameObject.Find(name); if (gameObject != null) { transformCache[name] = gameObject.transform; return gameObject.transform; } Transform[] array = UnityEngine.Object.FindObjectsOfType(includeInactive: true); foreach (Transform transform3 in array) { if (transform3 != null && string.Equals(transform3.name, name, StringComparison.OrdinalIgnoreCase)) { transformCache[name] = transform3; return transform3; } } return null; } private void EnsureButtonAudioAssigned() { if (!(buttonThockClip != null)) { buttonThockClip = Resources.Load("Thock"); } } private void EnsurePreviewGroups() { if (!(groupManager == null)) { if (creationPreviewRoot == null) { creationPreviewRoot = FindTransform("Preview"); } if (previewRight == null) { previewRight = FindTransform("PrevR"); } if (previewHead == null) { previewHead = FindTransform("PrevH"); } if (previewLeft == null) { previewLeft = FindTransform("PrevL"); } if (creationPreviewRoot != null && previewCreationGroups.Count == 0) { GameObject gameObject = new GameObject("CreationGroupPreview_0"); gameObject.transform.SetParent(creationPreviewRoot, worldPositionStays: false); previewCreationGroup = gameObject.AddComponent(); previewCreationGroup.id = 62500; previewCreationGroup.SetOwner(groupManager); previewCreationGroup.SetUseLocalGridTransform(useLocalGrid: true); previewCreationGroup.SetColliderGeneration(enabled: false); previewCreationGroups.Add(previewCreationGroup); } else if (creationPreviewRoot == null && !warnedMissingCreationPreviewRoot) { warnedMissingCreationPreviewRoot = true; Debug.LogWarning("InventoryController: could not find Creation Preview root named 'Preview'. Creation previews will be hidden until it exists."); } previewHeadGroup = EnsurePreviewGroupOn(previewHead, previewHeadGroup, 62000); previewLeftGroup = EnsurePreviewGroupOn(previewLeft, previewLeftGroup, 62001); previewRightGroup = EnsurePreviewGroupOn(previewRight, previewRightGroup, 62002); if (!warnedMissingAvatarPreviewRoots && (previewHead == null || previewLeft == null || previewRight == null)) { warnedMissingAvatarPreviewRoots = true; Debug.LogWarning("InventoryController: could not find one or more avatar preview roots ('PrevH', 'PrevL', 'PrevR'). Avatar previews will be hidden until they exist."); } } } private VoxelGroup EnsurePreviewGroupOn(Transform root, VoxelGroup existing, ushort id) { if (root == null || groupManager == null) { return existing; } VoxelGroup voxelGroup = ((existing != null) ? existing : root.GetComponent()); if (voxelGroup == null) { voxelGroup = root.gameObject.AddComponent(); } voxelGroup.id = id; voxelGroup.SetOwner(groupManager); voxelGroup.SetUseLocalGridTransform(useLocalGrid: true); voxelGroup.SetColliderGeneration(enabled: false); voxelGroup.SetVoxelSizeScale(0.5f); return voxelGroup; } private void HandleButton(Collider button, ref bool insidePrevious, Action action) { bool flag = IsHandInside(button, (voxelBrush != null) ? voxelBrush.LeftHandAnchor : null) || IsHandInside(button, (voxelBrush != null) ? voxelBrush.RightHandAnchor : null); if (flag && !insidePrevious) { PlayButtonEnterAnimation(button); if (Time.unscaledTime >= nextButtonPressTime) { nextButtonPressTime = Time.unscaledTime + 0.2f; PlayButtonClickFeedback(button); action?.Invoke(); } } insidePrevious = flag; } private static bool IsHandInside(Collider button, Transform hand) { if (button == null || hand == null) { return false; } return button.bounds.Contains(hand.position); } private void PlayButtonEnterAnimation(Collider button) { if (!(button == null)) { Transform transform = button.transform; if (!buttonBaseScales.TryGetValue(transform, out var value)) { value = transform.localScale; buttonBaseScales.Add(transform, value); } if (buttonAnimationCoroutines.TryGetValue(transform, out var value2) && value2 != null) { StopCoroutine(value2); } transform.localScale = value; buttonAnimationCoroutines[transform] = StartCoroutine(AnimateButtonScale(transform, value)); } } private IEnumerator AnimateButtonScale(Transform buttonTransform, Vector3 baseScale) { if (!(buttonTransform == null)) { float startTime = Time.unscaledTime; while (Time.unscaledTime - startTime < 0.08f) { float t = Mathf.Clamp01((Time.unscaledTime - startTime) / 0.08f); buttonTransform.localScale = Vector3.Lerp(baseScale, baseScale * 0.84f, t); yield return null; } startTime = Time.unscaledTime; while (Time.unscaledTime - startTime < 0.12f) { float num = Mathf.Clamp01((Time.unscaledTime - startTime) / 0.12f); float num2 = Mathf.Sin(num * MathF.PI); Vector3 a = Vector3.Lerp(baseScale * 0.84f, baseScale, num); buttonTransform.localScale = Vector3.LerpUnclamped(a, baseScale * 1.04f, num2 * 0.15f); yield return null; } buttonTransform.localScale = baseScale; } } private void PlayButtonClickFeedback(Collider button) { if (!(buttonThockClip == null) && !(button == null)) { AudioSource.PlayClipAtPoint(buttonThockClip, button.bounds.center, 1f); } } private void ResetAllButtonScales() { foreach (KeyValuePair buttonBaseScale in buttonBaseScales) { if (buttonBaseScale.Key != null) { buttonBaseScale.Key.localScale = buttonBaseScale.Value; } } } private bool TryGetSelectedId(out string id) { id = string.Empty; if (slotIndex < 0 || slotIndex >= ids.Count) { return false; } id = ids[slotIndex] ?? string.Empty; return true; } private string GetOrCreateSelectedId() { if (!TryGetSelectedId(out var id)) { return string.Empty; } if (!string.IsNullOrEmpty(id)) { return id; } string text = MakeNewId(); ids[slotIndex] = text; return text; } private static string MakeNewId() { return DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); } private bool TryGetSpawnGridPosition(out Vector3Int gridPosition) { gridPosition = Vector3Int.zero; VRVoxelBrush vRVoxelBrush = ((voxelBrush != null) ? voxelBrush : VRVoxelBrush.Instance); if (vRVoxelBrush != null && vRVoxelBrush.TryGetSpawnGridPosition(out gridPosition)) { return true; } float num = ((groupManager != null) ? groupManager.VoxelSize : 0.125f); Vector3 vector = ((Camera.main != null) ? Camera.main.transform.position : base.transform.position) + ((Camera.main != null) ? Camera.main.transform.forward : base.transform.forward).normalized * 2f; gridPosition = new Vector3Int(Mathf.RoundToInt(vector.x / num), Mathf.RoundToInt(vector.y / num), Mathf.RoundToInt(vector.z / num)); return true; } } }