diff --git a/SaberFactory.Unity/SaberFactory.Unity.csproj b/SaberFactory.Unity/SaberFactory.Unity.csproj index e571aea..81fce93 100644 --- a/SaberFactory.Unity/SaberFactory.Unity.csproj +++ b/SaberFactory.Unity/SaberFactory.Unity.csproj @@ -18,14 +18,14 @@ false bin\Debug\ - DEBUG;TRACE;UNITY + DEBUG;TRACE;UNITY;UNITY_EDITOR true bin\Release\ prompt 4 - TRACE;UNITY + TRACE;UNITY;UNITY_EDITOR True @@ -47,6 +47,9 @@ $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.dll False + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.AudioModule.dll + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll False @@ -56,7 +59,7 @@ False - ..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.JSONSerializeModule.dll + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.JSONSerializeModule.dll @@ -77,12 +80,22 @@ SaberModifierCollection.cs + TransformModifier.cs VisibilityModifier.cs + + SFBurnMarks.cs + + + SFClashEffect.cs + + + HelpAttribute.cs + \ No newline at end of file diff --git a/SaberFactory/Configuration/PluginConfig.cs b/SaberFactory/Configuration/PluginConfig.cs index 3fb9e61..db6ac7f 100644 --- a/SaberFactory/Configuration/PluginConfig.cs +++ b/SaberFactory/Configuration/PluginConfig.cs @@ -1,14 +1,16 @@ using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.CompilerServices; using IPA.Config.Stores; using IPA.Config.Stores.Attributes; using IPA.Config.Stores.Converters; +using JetBrains.Annotations; [assembly: InternalsVisibleTo(GeneratedStore.AssemblyVisibilityTarget)] namespace SaberFactory.Configuration { - internal class PluginConfig + internal class PluginConfig : INotifyPropertyChanged { public bool Enabled { get; set; } = true; @@ -49,6 +51,10 @@ internal class PluginConfig public bool ReloadOnSaberUpdate { get; set; } = false; + public float SwingSoundVolume { get; set; } = 1; + + public bool EnableCustomBurnmarks { get; set; } = true; + // How many threads to spawn when loading all sabers // ! Not used as of right now ! [Ignore] public int LoadingThreads { get; set; } = 2; @@ -94,5 +100,13 @@ public bool IsFavorite(string path) { return Favorites.Contains(path); } + + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } } \ No newline at end of file diff --git a/SaberFactory/DataStore/MainAssetStore.cs b/SaberFactory/DataStore/MainAssetStore.cs index c168ad1..9583fd6 100644 --- a/SaberFactory/DataStore/MainAssetStore.cs +++ b/SaberFactory/DataStore/MainAssetStore.cs @@ -16,7 +16,7 @@ namespace SaberFactory.DataStore /// /// Class for managing store assets ie. parts and custom sabers /// - internal class MainAssetStore : IDisposable, ILoadingTask + public class MainAssetStore : IDisposable, ILoadingTask { public List AdditionalCustomSaberFolders { get; } = new List(); @@ -81,7 +81,7 @@ public async Task GetCompositionByMeta(PreloadMetaData meta) return await this[PathTools.ToRelativePath(meta.AssetMetaPath.Path)]; } - public async Task LoadAllMetaAsync(EAssetTypeConfiguration assetType) + internal async Task LoadAllMetaAsync(EAssetTypeConfiguration assetType) { await LoadAllCustomSaberMetaDataAsync(); } @@ -210,7 +210,7 @@ private async Task LoadAllMetaDataForLoader(AssetBundleLoader loader, bool creat sw.Print(_logger); } - public async Task CreateMetaData(AssetMetaPath assetMetaPath) + internal async Task CreateMetaData(AssetMetaPath assetMetaPath) { var relativePath = assetMetaPath.RelativePath+".meta"; if (_metaData.TryGetValue(relativePath, out _)) diff --git a/SaberFactory/DataStore/StoreAsset.cs b/SaberFactory/DataStore/StoreAsset.cs index 2df8324..3b1e1b3 100644 --- a/SaberFactory/DataStore/StoreAsset.cs +++ b/SaberFactory/DataStore/StoreAsset.cs @@ -7,7 +7,7 @@ namespace SaberFactory.DataStore /// /// Keeps information about the origin of the asset /// - internal class StoreAsset + public class StoreAsset { public readonly AssetBundle AssetBundle; public readonly string Extension; diff --git a/SaberFactory/Directory.Build.targets b/SaberFactory/Directory.Build.targets index 2418059..c1b2f9b 100644 --- a/SaberFactory/Directory.Build.targets +++ b/SaberFactory/Directory.Build.targets @@ -70,7 +70,8 @@ - + + diff --git a/SaberFactory/Editor/Editor.cs b/SaberFactory/Editor/Editor.cs index dbe1fc8..94de9a2 100644 --- a/SaberFactory/Editor/Editor.cs +++ b/SaberFactory/Editor/Editor.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using IPA.Loader; using SaberFactory.Configuration; using SaberFactory.HarmonyPatches; using SaberFactory.Helpers; @@ -10,6 +11,7 @@ using SaberFactory.UI.Lib; using SiraUtil.Logging; using SiraUtil.Tools; +using Tweening; using UnityEngine; using Zenject; @@ -49,6 +51,8 @@ public bool IsSaberInHand private bool _isFirstActivation = true; private bool _isSaberInHand; private SaberInstance _spawnedSaber; + private readonly PluginMetadata _metaData; + private readonly TimeTweeningManager _tweeningManager; private Editor( SiraLog logger, @@ -60,9 +64,13 @@ private Editor( PlayerDataModel playerDataModel, SaberGrabController saberGrabController, MenuSaberProvider menuSaberProvider, - PluginDirectories pluginDirs) + PluginDirectories pluginDirs, + [Inject(Id = nameof(SaberFactory))]PluginMetadata metadata, + TimeTweeningManager tweeningManager) { _logger = logger; + _metaData = metadata; + _tweeningManager = tweeningManager; _pluginConfig = pluginConfig; _baseUiComposition = baseUiComposition; _editorInstanceManager = editorInstanceManager; @@ -71,7 +79,7 @@ private Editor( _saberGrabController = saberGrabController; _menuSaberProvider = menuSaberProvider; - _pedestal = new Pedestal(embeddedAssetLoader, pluginDirs.SaberFactoryDir.GetFile("pedestal")); + _pedestal = new Pedestal(pluginDirs.SaberFactoryDir.GetFile("pedestal")); _sfLogoAnim = new SFLogoAnim(embeddedAssetLoader); Instance = this; @@ -93,6 +101,10 @@ public async void Initialize() // Create Pedestal var pos = new Vector3(0.3f, 0, 0.9f); await _pedestal.Instantiate(pos, Quaternion.Euler(0, 25, 0)); + SetPedestalText(1, "SF v"+_metaData.HVersion+""); +#if PAT + SetPedestalText(2, "Patreon ♥"); +#endif SetupGlobalShaderVars(); } @@ -155,6 +167,20 @@ public void Close(bool instant) _menuSaberProvider.RequestSaberVisiblity(true); } + public void SetPedestalText(int line, string text) + { + _pedestal.SetText(line, text); + } + + public void FlashPedestal(Color color) + { + _tweeningManager.KillAllTweens(_pedestal.SaberContainerTransform); + _tweeningManager.AddTween(new FloatTween(1, 0, f => + { + _pedestal.SetLedColor(color.ColorWithAlpha(f)); + }, 1, EaseType.InCubic), _pedestal.SaberContainerTransform); + } + private async void OnModelCompositionSet(ModelComposition composition) { _spawnedSaber?.Destroy(); diff --git a/SaberFactory/Editor/Pedestal.cs b/SaberFactory/Editor/Pedestal.cs index 27ea8a7..42c846f 100644 --- a/SaberFactory/Editor/Pedestal.cs +++ b/SaberFactory/Editor/Pedestal.cs @@ -1,7 +1,10 @@ using System; using System.IO; +using System.Linq; using System.Threading.Tasks; using SaberFactory.Helpers; +using SaberFactory.Loaders; +using TMPro; using UnityEngine; using Object = UnityEngine.Object; @@ -9,6 +12,8 @@ namespace SaberFactory.Editor { internal class Pedestal { + private static readonly string PedestalPath = String.Join(".", nameof(SaberFactory), "Resources", "pedestal"); + public bool IsVisible { set @@ -29,13 +34,15 @@ public Vector3 Position public Transform SaberContainerTransform { get; private set; } private readonly FileInfo _customPedestalFile; - private readonly EmbeddedAssetLoader _embeddedAssetLoader; - private Transform _rootTransform; + private TextMeshPro _textMeshPro; + private Material _ledMat; + + private readonly string[] _lines = new string[3]; + private static readonly int LedColor = Shader.PropertyToID("_LedColor"); - public Pedestal(EmbeddedAssetLoader embeddedAssetLoader, FileInfo customPedestalFile) + public Pedestal(FileInfo customPedestalFile) { - _embeddedAssetLoader = embeddedAssetLoader; _customPedestalFile = customPedestalFile; } @@ -54,7 +61,20 @@ public async Task Instantiate(Vector3 pos, Quaternion rot) return; } - Object.Instantiate(prefab, _rootTransform, false); + var instantiated = Object.Instantiate(prefab, _rootTransform, false); + _textMeshPro = instantiated.GetComponentsInChildren() + .FirstOrDefault(x => x.name == "Pedestal_Display"); + var leds = instantiated.GetComponentsInChildren().FirstOrDefault(x => x.name == "Leds"); + + if (_textMeshPro) + { + _textMeshPro.alignment = TextAlignmentOptions.Center; + } + + if (leds) + { + _ledMat = leds.sharedMaterial; + } SaberContainerTransform = _rootTransform.CreateGameObject("SaberContainer").transform; SaberContainerTransform.localPosition += new Vector3(0, 1, 0); @@ -66,6 +86,17 @@ public async Task Instantiate(Vector3 pos, Quaternion rot) IsVisible = false; } + public void SetText(int line, string text) + { + if (!_textMeshPro) + { + return; + } + + _lines[line] = text; + _textMeshPro.text = string.Join("\n", _lines); + } + private async Task GetPedestalAsset() { if (_customPedestalFile.Exists) @@ -82,7 +113,10 @@ private async Task GetPedestalAsset() } } - return await _embeddedAssetLoader.LoadAsset("Pedestal"); + var data = await Readers.ReadResourceAsync(PedestalPath); + var bundle = await Readers.LoadAssetFromAssetBundleAsync(data, "Pedestal"); + bundle.Item2.Unload(false); + return bundle.Item1; } public void Destroy() @@ -92,5 +126,15 @@ public void Destroy() _rootTransform.gameObject.TryDestroy(); } } + + public void SetLedColor(Color color) + { + if (!_ledMat) + { + return; + } + + _ledMat.SetColor(LedColor, color); + } } } \ No newline at end of file diff --git a/SaberFactory/Game/SFSaberModelController.cs b/SaberFactory/Game/SFSaberModelController.cs index f9e883b..60de154 100644 --- a/SaberFactory/Game/SFSaberModelController.cs +++ b/SaberFactory/Game/SFSaberModelController.cs @@ -1,6 +1,10 @@ -using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using HarmonyLib; using SaberFactory.Helpers; using SaberFactory.Instances; +using SaberFactory.Misc; using SaberFactory.Models; using SiraUtil.Interfaces; using SiraUtil.Logging; @@ -12,12 +16,12 @@ namespace SaberFactory.Game { internal class SfSaberModelController : SaberModelController, IColorable { - [InjectOptional] private readonly AFHandler _afHandler = null; [InjectOptional] private readonly EventPlayer _eventPlayer = null; [Inject] private readonly GameSaberSetup _gameSaberSetup = null; [Inject] private readonly SiraLog _logger = null; [Inject] private readonly SaberInstance.Factory _saberInstanceFactory = null; [Inject] private readonly SaberSet _saberSet = null; + [Inject] private readonly List _customizers = null; private Color? _saberColor; private SaberInstance _saberInstance; @@ -43,19 +47,17 @@ public override async void Init(Transform parent, Saber saber) var saberModel = saber.saberType == SaberType.SaberA ? _saberSet.LeftSaber : _saberSet.RightSaber; _saberInstance = _saberInstanceFactory.Create(saberModel); + + if (saber.saberType == SaberType.SaberA) + { + _customizers.Do(x=>x.SetSaber(_saberInstance)); + } + _saberInstance.SetParent(transform); _saberInstance.CreateTrail(false, _saberTrail); SetColor(_saberColor ?? _colorManager.ColorForSaberType(_saberInstance.Model.SaberSlot.ToSaberType())); - if (_afHandler != null && AFHandler.IsValid && AFHandler.ShouldFire) - { - await Task.Delay(4000); - await _afHandler.Shoot(this, saber.saberType); - } - else - { - _eventPlayer?.SetPartEventList(_saberInstance.Events, saber.saberType); - } + _eventPlayer?.SetPartEventList(_saberInstance.Events, saber.saberType); _logger.Info("Instantiated Saber"); } diff --git a/SaberFactory/Game/SaberMovementTester.cs b/SaberFactory/Game/SaberMovementTester.cs index 527bafe..77bb705 100644 --- a/SaberFactory/Game/SaberMovementTester.cs +++ b/SaberFactory/Game/SaberMovementTester.cs @@ -1,8 +1,11 @@ -using System.Collections; +using System; +using System.Collections; +using System.Linq; using System.Threading.Tasks; using SiraUtil.Sabers; using UnityEngine; using Zenject; +using Object = UnityEngine.Object; namespace SaberFactory.Game { @@ -11,8 +14,6 @@ internal class SaberMovementTester : IInitializable private readonly AudioTimeSyncController _audioController; private readonly InitData _initData; private readonly SiraSaberFactory _saberFactory; - - private Transform _movementContainer; private SiraSaber _saber; private SaberMovementTester(InitData initData, SiraSaberFactory saberFactory, AudioTimeSyncController audioController) @@ -33,30 +34,75 @@ public async void Initialize() return; } - _movementContainer = new GameObject("SaberTester").transform; - _movementContainer.localPosition = new Vector3(0, 1.5f, 0); - _saber = _saberFactory.Spawn(SaberType.SaberA); - _saber.transform.SetParent(_movementContainer, false); + var saberA = CreateSaber(SaberType.SaberA, new Vector3(0, 0.6f, 0), Quaternion.Euler(90, 0, 0)); + var saberB = CreateSaber(SaberType.SaberB, new Vector3(0, 0.6f, 0), Quaternion.Euler(90, 0, 0)); + + SharedCoroutineStarter.instance.StartCoroutine(GroundRoundAnimationCoroutine(-0.2f, saberA)); + SharedCoroutineStarter.instance.StartCoroutine(GroundRoundAnimationCoroutine(0.2f, saberB)); + + // Don't try this at home + var allLRs = Object.FindObjectsOfType() + .Where(x => x.name.Contains("SaberBurnMark")); + + var normalLR = allLRs.First(x => !x.name.Contains("Sira")); + var siraLRs = allLRs.Where(x => x.name.Contains("Sira")); + + foreach (var lineRenderer in siraLRs) + { + lineRenderer.sharedMaterial = new Material(normalLR.sharedMaterial); + lineRenderer.textureMode = LineTextureMode.Stretch; + //lineRenderer.widthMultiplier = 2; + } + } + + public Transform CreateSaber(SaberType saberType, Vector3 pos, Quaternion rot) + { + var parent = new GameObject("SaberTester_" + saberType).transform; + parent.localPosition = new Vector3(0, 0.6f, 0); + parent.localRotation = Quaternion.Euler(90, 0, 0); + _saber = _saberFactory.Spawn(saberType); + _saber.transform.SetParent(parent, false); - SharedCoroutineStarter.instance.StartCoroutine(AnimationCoroutine()); + return parent; } - private IEnumerator AnimationCoroutine() + //private IEnumerator AnimationCoroutine() + //{ + // var currentRot = _movementContainer.localEulerAngles.x; + // while (true) + // { + // while (currentRot < 90) + // { + // currentRot += 1; + // _movementContainer.localEulerAngles = new Vector3(currentRot, 0, 0); + // yield return new WaitForEndOfFrame(); + // } + + // while (currentRot > -90) + // { + // currentRot -= 1; + // _movementContainer.localEulerAngles = new Vector3(currentRot, 0, 0); + // yield return new WaitForEndOfFrame(); + // } + // } + //} + + private IEnumerator GroundRoundAnimationCoroutine(float xPos, Transform t) { - var currentRot = _movementContainer.localEulerAngles.x; + var currentPos = t.localPosition.z; while (true) { - while (currentRot < 90) + while (currentPos < 1) { - currentRot += 1; - _movementContainer.localEulerAngles = new Vector3(currentRot, 0, 0); + currentPos += 0.02f; + t.localPosition = new Vector3(xPos, 0.6f, currentPos); yield return new WaitForEndOfFrame(); } - while (currentRot > -90) + while (currentPos > 0) { - currentRot -= 1; - _movementContainer.localEulerAngles = new Vector3(currentRot, 0, 0); + currentPos -= 0.02f; + t.localPosition = new Vector3(xPos, 0.6f, currentPos); yield return new WaitForEndOfFrame(); } } diff --git a/SaberFactory/Helpers/CommonHelpers.cs b/SaberFactory/Helpers/CommonHelpers.cs index 46464f6..d8629f7 100644 --- a/SaberFactory/Helpers/CommonHelpers.cs +++ b/SaberFactory/Helpers/CommonHelpers.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using System.Threading.Tasks; using IPA.Utilities; using SaberFactory.DataStore; @@ -83,5 +84,27 @@ public static async Task WaitForFinish(this ILoadingTask loadingTask) await loadingTask.CurrentTask; } + + public static Component Upgrade(Component monoBehaviour, Type upgradingType) + { + var originalType = monoBehaviour.GetType(); + + var gameObject = monoBehaviour.gameObject; + var upgradedDummyComponent = Activator.CreateInstance(upgradingType); + foreach (FieldInfo info in originalType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)) + { + info.SetValue(upgradedDummyComponent, info.GetValue(monoBehaviour)); + } + UnityEngine.Object.DestroyImmediate(monoBehaviour); + bool goState = gameObject.activeSelf; + gameObject.SetActive(false); + var upgradedMonoBehaviour = gameObject.AddComponent(upgradingType); + foreach (FieldInfo info in upgradingType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)) + { + info.SetValue(upgradedMonoBehaviour, info.GetValue(upgradedDummyComponent)); + } + gameObject.SetActive(goState); + return upgradedMonoBehaviour; + } } } \ No newline at end of file diff --git a/SaberFactory/Helpers/DebugTools.cs b/SaberFactory/Helpers/DebugTools.cs index f2f640e..343d99c 100644 --- a/SaberFactory/Helpers/DebugTools.cs +++ b/SaberFactory/Helpers/DebugTools.cs @@ -1,4 +1,9 @@ -using UnityEngine; +using System.Reflection; +using System.Text; +using BeatSaberMarkupLanguage.FloatingScreen; +using BeatSaberMarkupLanguage.Tags; +using UnityEngine; +using UnityEngine.UI; namespace SaberFactory.Helpers { @@ -23,6 +28,37 @@ public static GameObject CreateBall(BallOptions options) return go; } + public static string DumpObject(Object obj) + { + var str = new StringBuilder(); + foreach (var fieldInfo in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + str.AppendLine($"{fieldInfo.Name}: {fieldInfo.GetValue(obj)}"); + } + + foreach (var propertyInfo in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + if(!propertyInfo.CanRead) continue; + str.AppendLine($"{propertyInfo.Name}: {propertyInfo.GetValue(obj)}"); + } + + return str.ToString(); + } + + public static FloatingScreen CreateScreen(Vector2? size = null, Vector3? pos = null) + { + return FloatingScreen.CreateFloatingScreen(size??new Vector2(500, 500), false, pos??new Vector3(0, 1, 1), Quaternion.identity); + } + + public static void TextureScreen(Texture tex, Vector2? size = null, Vector3? pos = null) + { + var screen = CreateScreen(size, pos); + screen.gameObject.name = "TextureScreen_" + tex.name; + var rawImage = new RawImageTag().CreateObject(screen.transform).GetComponentInChildren(); + rawImage.texture = tex; + rawImage.transform.AsRectTransform().sizeDelta = screen.transform.AsRectTransform().sizeDelta; + } + public struct BallOptions { public Vector3 Pos; diff --git a/SaberFactory/Helpers/SaberHelpers.cs b/SaberFactory/Helpers/SaberHelpers.cs index 96c3f9e..87d1ed2 100644 --- a/SaberFactory/Helpers/SaberHelpers.cs +++ b/SaberFactory/Helpers/SaberHelpers.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using System.Linq; using CustomSaber; +using SaberFactory.Instances; using UnityEngine; namespace SaberFactory.Helpers { - public static class SaberHelpers + internal static class SaberHelpers { public static List GetTrails(GameObject saberObject) { @@ -20,5 +21,10 @@ public static List GetTrails(GameObject saberObject) .OrderByDescending(x => x.PointEnd.position.z) .ToList(); } + + public static SaberInstance.SaberMonoBehaviour GetSaberMonoBehaviour(GameObject go) + { + return go.GetComponentInParent(); + } } } \ No newline at end of file diff --git a/SaberFactory/Installers/PluginAppInstaller.cs b/SaberFactory/Installers/PluginAppInstaller.cs index 608f3c3..896dce1 100644 --- a/SaberFactory/Installers/PluginAppInstaller.cs +++ b/SaberFactory/Installers/PluginAppInstaller.cs @@ -8,6 +8,7 @@ using SaberFactory.Instances; using SaberFactory.Instances.PostProcessors; using SaberFactory.Instances.Trail; +using SaberFactory.Misc; using SaberFactory.Models; using SaberFactory.Models.CustomSaber; using SaberFactory.Serialization; @@ -78,6 +79,8 @@ public override void InstallBindings() Container.Bind().AsSingle(); Container.Bind().AsSingle(); + Container.BindInterfacesAndSelfTo().AsSingle(); + InstallFactories(); InstallMiddlewares(); } diff --git a/SaberFactory/Installers/PluginGameInstaller.cs b/SaberFactory/Installers/PluginGameInstaller.cs index 49783f8..3bf0dcc 100644 --- a/SaberFactory/Installers/PluginGameInstaller.cs +++ b/SaberFactory/Installers/PluginGameInstaller.cs @@ -4,6 +4,7 @@ using SaberFactory.Configuration; using SaberFactory.Game; using SaberFactory.Helpers; +using SaberFactory.Misc; using SaberFactory.Models; using SiraUtil.Interfaces; using SiraUtil.Sabers; @@ -33,9 +34,7 @@ public override void InstallBindings() //Container.BindInterfacesAndSelfTo().AsSingle(); Container.BindInterfacesAndSelfTo().AsSingle(); - //Container.Bind().To().AsSingle(); - Container.BindInstance(SaberModelRegistration.Create()); - + Container.BindInstance(SaberModelRegistration.Create(300)).AsSingle(); #if DEBUG && TEST_TRAIL if (Container.TryResolve()?.FPFC ?? false) diff --git a/SaberFactory/Instances/CustomSaber/CustomSaberInstance.cs b/SaberFactory/Instances/CustomSaber/CustomSaberInstance.cs index 1721ac8..b2e6239 100644 --- a/SaberFactory/Instances/CustomSaber/CustomSaberInstance.cs +++ b/SaberFactory/Instances/CustomSaber/CustomSaberInstance.cs @@ -8,7 +8,6 @@ using SaberFactory.Models; using SaberFactory.Models.CustomSaber; using SiraUtil.Logging; -using SiraUtil.Tools; using UnityEngine; namespace SaberFactory.Instances.CustomSaber diff --git a/SaberFactory/Instances/MaterialDescriptor.cs b/SaberFactory/Instances/MaterialDescriptor.cs index 51a9fad..7eec1e6 100644 --- a/SaberFactory/Instances/MaterialDescriptor.cs +++ b/SaberFactory/Instances/MaterialDescriptor.cs @@ -7,7 +7,7 @@ namespace SaberFactory.Instances /// Extension class for with more information /// and possibly to revert a changed /// - internal class MaterialDescriptor + public class MaterialDescriptor { public bool IsValid => Material != null; public Material Material; diff --git a/SaberFactory/Instances/PostProcessors/MainSaberPostProcessor.cs b/SaberFactory/Instances/PostProcessors/MainSaberPostProcessor.cs index 094cbb9..7b8e0f0 100644 --- a/SaberFactory/Instances/PostProcessors/MainSaberPostProcessor.cs +++ b/SaberFactory/Instances/PostProcessors/MainSaberPostProcessor.cs @@ -1,6 +1,7 @@ using HarmonyLib; using SaberFactory.Configuration; using SaberFactory.Helpers; +using SaberFactory.ProjectComponents; using UnityEngine; namespace SaberFactory.Instances.PostProcessors @@ -18,8 +19,13 @@ public void ProcessSaber(GameObject saberObject) { saberObject.SetLayer(12); saberObject.GetComponentsInChildren().Do(x => x.enabled = false); - saberObject.GetComponentsInChildren(true).Do(x => x.volume = x.volume * _config.SaberAudioVolumeMultiplier); + saberObject.GetComponentsInChildren(true).Do(x => x.volume *= _config.SaberAudioVolumeMultiplier); saberObject.GetComponentsInChildren(true).Do(x => { x.sortingOrder = 3; }); + + if (saberObject.GetComponentInChildren() is { } saberSound) + { + saberSound.ConfigVolume = _config.SwingSoundVolume; + } } } } \ No newline at end of file diff --git a/SaberFactory/Instances/SaberInstance.cs b/SaberFactory/Instances/SaberInstance.cs index 4e29152..32be529 100644 --- a/SaberFactory/Instances/SaberInstance.cs +++ b/SaberFactory/Instances/SaberInstance.cs @@ -38,6 +38,8 @@ public class SaberInstance private InstanceTrailData _instanceTrailData; private List _secondaryTrails; + private readonly Dictionary _saberComponents = new Dictionary(); + private SaberInstance( SaberModel model, BasePieceInstance.Factory pieceFactory, @@ -51,7 +53,7 @@ private SaberInstance( Model = model; GameObject = new GameObject(SaberName); - GameObject.AddComponent().Init(this, OnSaberGameObjectDestroyed); + GameObject.AddComponent().Init(this, _saberComponents, OnSaberGameObjectDestroyed); CachedTransform = GameObject.transform; @@ -160,6 +162,14 @@ public void DestroyTrail(bool immediate = false) } } + public bool GetSaberComponent(out T saberComp) where T : Component + { + saberComp = null; + if (!_saberComponents.TryGetValue(typeof(T), out var comp)) return false; + saberComp = (T)comp; + return saberComp; + } + public void Destroy() { GameObject.TryDestroy(); @@ -221,17 +231,25 @@ internal class SaberMonoBehaviour : MonoBehaviour { public SaberInstance SaberInstance { get; private set; } private Action _onDestroyed; + public Dictionary SaberComponentDict; private void OnDestroy() { _onDestroyed?.Invoke(); } - public void Init(SaberInstance saberInstance, Action onDestroyedCallback) + public void Init(SaberInstance saberInstance, Dictionary saberComponentDict, Action onDestroyedCallback) { SaberInstance = saberInstance; + SaberComponentDict = saberComponentDict; _onDestroyed = onDestroyedCallback; } + + public void RegisterComponent(Component comp) + { + SaberComponentDict[comp.GetType()] = comp; + } + } } } \ No newline at end of file diff --git a/SaberFactory/Misc/CustomSaberBurnMarkArea.cs b/SaberFactory/Misc/CustomSaberBurnMarkArea.cs new file mode 100644 index 0000000..dbd0b07 --- /dev/null +++ b/SaberFactory/Misc/CustomSaberBurnMarkArea.cs @@ -0,0 +1,122 @@ +using System; +using SaberFactory.Configuration; +using SaberFactory.Models; +using SaberFactory.ProjectComponents; +using UnityEngine; +using Zenject; +using Object = UnityEngine.Object; + +namespace SaberFactory.Misc +{ + internal class CustomSaberBurnMarkArea : SaberBurnMarkArea + { + public Material FadeoutMaterial + { + get => _fadeOutMaterial; + set + { + if (_fadeOutMaterial) + { + DestroyImmediate(_fadeOutMaterial); + _fadeOutMaterial = value; + } + } + } + + public float RandomOffset + { + get => _blackMarkLineRandomOffset; + set => _blackMarkLineRandomOffset = value; + } + + public float FadeoutStrength + { + get => _burnMarksFadeOutStrength; + set => _burnMarksFadeOutStrength = value; + } + + public float BurnmarkSize + { + get => _burnmarkSize; + set + { + _burnmarkSize = value; + foreach (var lineRenderer in _lineRenderers) + { + lineRenderer.widthMultiplier = _burnmarkSize; + } + } + } + + public Material BurnmarkMaterial + { + get => _lineRenderers[0].sharedMaterial; + set + { + foreach (var lineRenderer in _lineRenderers) + { + var oldMat = lineRenderer.sharedMaterial; + lineRenderer.material = new Material(value) {color = oldMat.color}; + } + } + } + + private float _burnmarkSize = 0.1f; + + [Inject] private readonly SaberSet _saberSet = null; + [Inject] private readonly PluginConfig _pluginConfig = null; + + private SFBurnmarks _info; + + public override void Start() + { + base.Start(); + Init(); + } + + public void Init() + { + if (!_pluginConfig.EnableCustomBurnmarks) + { + return; + } + + if (!_saberSet.LeftSaber.GetCustomSaber(out var cs)) + { + return; + } + + _info = cs.ModelComposition.AdditionalInstanceHandler.GetComponent(); + + if (!_info) + { + return; + } + + if (_info.BurnMarkMaterial) + { + BurnmarkMaterial = _info.BurnMarkMaterial; + } + + //if (_info.SparkleMaterial) + //{ + // _sparkle.Material = _info.SparkleMaterial; + //} + + if (_info.FadeoutMaterial) + { + FadeoutMaterial = new Material(_info.FadeoutMaterial); + } + + if (_info.FloorMaterial) + { + GetComponent().material = new Material(_info.FloorMaterial); + } + + BurnmarkSize = _info.BurnmarkSize; + + FadeoutStrength = _info.FadeStrength; + RandomOffset = _info.RandomBurnmarkJitter; + } + } +} \ No newline at end of file diff --git a/SaberFactory/Misc/SaberClashCustomizer.cs b/SaberFactory/Misc/SaberClashCustomizer.cs new file mode 100644 index 0000000..de4122e --- /dev/null +++ b/SaberFactory/Misc/SaberClashCustomizer.cs @@ -0,0 +1,163 @@ +using System.IO; +using HarmonyLib; +using IPA.Utilities; +using SaberFactory.Helpers; +using SaberFactory.Instances; +using SaberFactory.Models; +using SaberFactory.ProjectComponents; +using SiraUtil.Affinity; +using UnityEngine; +using Zenject; +using Object = UnityEngine.Object; + +namespace SaberFactory.Misc +{ + internal class PSManager + { + public readonly ParticleSystem ParticleSystem; + public readonly ParticleSystemRenderer Renderer; + + private ParticleSystem.MainModule _main; + + public Material Material + { + get => Renderer.sharedMaterial; + set => Renderer.sharedMaterial = value; + } + + public Color Color + { + get => _main.startColor.color; + set => _main.startColor = value; + } + + public ParticleSystem.MainModule Main => _main; + + public PSManager(ParticleSystem particleSystem) + { + ParticleSystem = particleSystem; + _main = particleSystem.main; + Renderer = particleSystem.GetComponent(); + } + } + + internal class SaberClashCustomizer : IInitializable, IAffinity, ICustomizer + { + public bool ClashEnabled { get; set; } + + private SaberClashEffect _currentClashEffect; + private PSManager _sparkle; + private PSManager _glow; + + internal SaberClashCustomizer(EmbeddedAssetLoader assetLoader) + { + } + + public void SetSaber(SaberInstance saber) + { + var info = saber.Model.PieceCollection[AssetTypeDefinition.CustomSaber].ModelComposition + .AdditionalInstanceHandler.GetComponent(); + if (!info) + { + return; + } + + if (info.Material) + { + _glow.Material = info.Material; + } + } + + [AffinityPostfix] + [AffinityPatch(typeof(SaberClashEffect), nameof(SaberClashEffect.Start))] + protected void Setup(SaberClashEffect __instance, ParticleSystem ____sparkleParticleSystem, ParticleSystem ____glowParticleSystem) + { + _currentClashEffect = __instance; + _sparkle = new PSManager(____sparkleParticleSystem); + _glow = new PSManager(____glowParticleSystem); + } + + public void Initialize() + { + + } + } + + //internal class SaberBurnMarkCustomizer : IInitializable, IAffinity, ICustomizer + //{ + // private SaberBurnMarkSparkles _currentSpakles; + // private readonly CustomSaberBurnMarkArea _burnMarkArea; + + // private PSManager _sparkle; + // private PSManager[] _burnMarks; + + // private SFBurnmarks _currentInfo; + + // public SaberBurnMarkCustomizer(CustomSaberBurnMarkArea burnMarkArea) + // { + // _burnMarkArea = burnMarkArea; + // } + + // public void SetSaber(SaberInstance saber) + // { + // var info = saber.Model.PieceCollection[AssetTypeDefinition.CustomSaber].ModelComposition + // .AdditionalInstanceHandler.GetComponent(); + // if (!info) + // { + // return; + // } + + // if (info.BurnMarkMaterial) + // { + // _burnMarkArea.BurnmarkMaterial = info.BurnMarkMaterial; + // } + + // if (info.SparkleMaterial) + // { + // _sparkle.Material = info.SparkleMaterial; + // } + + // if (info.FadeoutMaterial) + // { + // _burnMarkArea.FadeoutMaterial = new Material(info.FadeoutMaterial); + // } + + // if (info.FloorMaterial) + // { + // _burnMarkArea.GetComponent().material = new Material(info.FloorMaterial); + // } + + // _burnMarkArea.BurnmarkSize = info.BurnmarkSize; + + // _burnMarkArea.FadeoutStrength = info.FadeStrength; + // _burnMarkArea.RandomOffset = info.RandomBurnmarkJitter; + // } + + // [AffinityPostfix] + // [AffinityPatch(typeof(SaberBurnMarkSparkles), nameof(SaberBurnMarkSparkles.Start))] + // protected void Setup(SaberBurnMarkSparkles __instance, ParticleSystem ____sparklesPS, + // ParticleSystem[] ____burnMarksPS) + // { + // _currentSpakles = __instance; + + // _sparkle = new PSManager(____sparklesPS); + // Object.DestroyImmediate(_sparkle.Renderer); + // _burnMarks = new PSManager[2]; + // _burnMarks[0] = new PSManager(____burnMarksPS[0]); + // _burnMarks[1] = new PSManager(____burnMarksPS[1]); + + // Object.DestroyImmediate(_burnMarks[0].Renderer); + // Object.DestroyImmediate(_burnMarks[1].Renderer); + // } + + // public void Initialize() + // { + + // } + //} + + internal interface ICustomizer + { + public void SetSaber(SaberInstance saber); + } +} diff --git a/SaberFactory/Models/AdditionalInstanceHandler.cs b/SaberFactory/Models/AdditionalInstanceHandler.cs index edb6609..731cb5e 100644 --- a/SaberFactory/Models/AdditionalInstanceHandler.cs +++ b/SaberFactory/Models/AdditionalInstanceHandler.cs @@ -1,5 +1,7 @@ -using SaberFactory.Helpers; +using System; +using SaberFactory.Helpers; using UnityEngine; +using Object = UnityEngine.Object; namespace SaberFactory.Models { @@ -7,19 +9,21 @@ namespace SaberFactory.Models /// Class for handling gameobject outside of the left and right sabers or parts /// Mostly used for custom sabers /// - internal class AdditionalInstanceHandler + public class AdditionalInstanceHandler { public bool IsInstantiated => _instance != null; private readonly GameObject _prefab; + private readonly GameObject _fallbackRightSaber; private GameObject _customSaberLeftSaber; private GameObject _customSaberRightSaber; private GameObject _instance; - public AdditionalInstanceHandler(GameObject prefab) + public AdditionalInstanceHandler(GameObject prefab, GameObject fallbackRightSaber) { _prefab = prefab; + _fallbackRightSaber = fallbackRightSaber; } public GameObject GetInstance() @@ -55,6 +59,11 @@ public GameObject GetSaber(ESaberSlot saberSlot) return saberSlot == ESaberSlot.Left ? _customSaberLeftSaber : _customSaberRightSaber; } + public T GetComponent() where T : Component + { + return GetInstance().GetComponent(); + } + public Transform FindInInstance(string name) { return GetInstance().transform.Find(name); @@ -67,8 +76,20 @@ private void Instantiate() _customSaberLeftSaber = GetSaber(ESaberSlot.Left); _customSaberRightSaber = GetSaber(ESaberSlot.Right); - _customSaberLeftSaber.SetActive(false); - _customSaberRightSaber.SetActive(false); + if (_customSaberRightSaber == null) + { + _customSaberRightSaber = Object.Instantiate(_fallbackRightSaber); + } + + if (_customSaberLeftSaber != null) + { + _customSaberLeftSaber.SetActive(false); + } + + if (_customSaberRightSaber != null) + { + _customSaberRightSaber.SetActive(false); + } } } } \ No newline at end of file diff --git a/SaberFactory/Models/AssetTypeDefinition.cs b/SaberFactory/Models/AssetTypeDefinition.cs index de2dfec..ae0cc6f 100644 --- a/SaberFactory/Models/AssetTypeDefinition.cs +++ b/SaberFactory/Models/AssetTypeDefinition.cs @@ -3,7 +3,7 @@ namespace SaberFactory.Models { [Serializable] - internal readonly struct AssetTypeDefinition + public readonly struct AssetTypeDefinition { public static readonly AssetTypeDefinition CustomSaber = new AssetTypeDefinition(EAssetType.Model, EAssetSubType.CustomSaber); diff --git a/SaberFactory/Models/BasePieceModel.cs b/SaberFactory/Models/BasePieceModel.cs index 4dac711..b6cbbc8 100644 --- a/SaberFactory/Models/BasePieceModel.cs +++ b/SaberFactory/Models/BasePieceModel.cs @@ -14,7 +14,7 @@ namespace SaberFactory.Models /// Model related to everything that makes up a saber /// like parts, halos, accessories, custom sabers /// - internal class BasePieceModel : IDisposable, IFactorySerializable + public class BasePieceModel : IDisposable, IFactorySerializable { /// /// Type of the associated instance class diff --git a/SaberFactory/Models/CustomSaber/CustomSaberModel.cs b/SaberFactory/Models/CustomSaber/CustomSaberModel.cs index 53c5bfd..e8f5823 100644 --- a/SaberFactory/Models/CustomSaber/CustomSaberModel.cs +++ b/SaberFactory/Models/CustomSaber/CustomSaberModel.cs @@ -14,7 +14,7 @@ namespace SaberFactory.Models.CustomSaber { - internal class CustomSaberModel : BasePieceModel + public class CustomSaberModel : BasePieceModel { public override Type InstanceType { get; protected set; } = typeof(CustomSaberInstance); diff --git a/SaberFactory/Models/CustomSaber/CustomSaberModelLoader.cs b/SaberFactory/Models/CustomSaber/CustomSaberModelLoader.cs index bb022fb..976847f 100644 --- a/SaberFactory/Models/CustomSaber/CustomSaberModelLoader.cs +++ b/SaberFactory/Models/CustomSaber/CustomSaberModelLoader.cs @@ -1,6 +1,8 @@ -using SaberFactory.Configuration; +using System; +using SaberFactory.Configuration; using SaberFactory.DataStore; using UnityEngine; +using Object = UnityEngine.Object; namespace SaberFactory.Models.CustomSaber { @@ -19,6 +21,21 @@ public ModelComposition GetComposition(StoreAsset storeAsset) { var (leftSaber, rightSaber) = GetSabers(storeAsset.Prefab.transform); + if (rightSaber == null) + { + var newParent = new GameObject("RightSaber").transform; + newParent.parent = storeAsset.Prefab.transform; + + rightSaber = Object.Instantiate(leftSaber, newParent, false); + rightSaber.transform.position = Vector3.zero; + rightSaber.transform.localScale = new Vector3(-1, 1, 1); + + rightSaber.name = "RightSaberMirror"; + + rightSaber = newParent.gameObject; + rightSaber.SetActive(false); + } + var storeAssetLeft = new StoreAsset(storeAsset.RelativePath, leftSaber, storeAsset.AssetBundle); var storeAssetRight = new StoreAsset(storeAsset.RelativePath, rightSaber, storeAsset.AssetBundle); diff --git a/SaberFactory/Models/EAssetSubType.cs b/SaberFactory/Models/EAssetSubType.cs index b1b17b5..950de23 100644 --- a/SaberFactory/Models/EAssetSubType.cs +++ b/SaberFactory/Models/EAssetSubType.cs @@ -1,6 +1,6 @@ namespace SaberFactory.Models { - internal enum EAssetSubType + public enum EAssetSubType { Blade, Emitter, diff --git a/SaberFactory/Models/EAssetType.cs b/SaberFactory/Models/EAssetType.cs index 3c037be..d181629 100644 --- a/SaberFactory/Models/EAssetType.cs +++ b/SaberFactory/Models/EAssetType.cs @@ -1,6 +1,6 @@ namespace SaberFactory.Models { - internal enum EAssetType + public enum EAssetType { Model, Halo diff --git a/SaberFactory/Models/ESaberSlot.cs b/SaberFactory/Models/ESaberSlot.cs index 1c3ce0a..6278d1d 100644 --- a/SaberFactory/Models/ESaberSlot.cs +++ b/SaberFactory/Models/ESaberSlot.cs @@ -1,6 +1,6 @@ namespace SaberFactory.Models { - internal enum ESaberSlot + public enum ESaberSlot { Left, Right diff --git a/SaberFactory/Models/ModelComposition.cs b/SaberFactory/Models/ModelComposition.cs index 3e062ac..d70f611 100644 --- a/SaberFactory/Models/ModelComposition.cs +++ b/SaberFactory/Models/ModelComposition.cs @@ -8,7 +8,7 @@ namespace SaberFactory.Models /// /// Stores left and right piece models + additional detached game objects in a composition /// - internal class ModelComposition : IDisposable, ICustomListItem + public class ModelComposition : IDisposable, ICustomListItem { public readonly AdditionalInstanceHandler AdditionalInstanceHandler; public readonly AssetTypeDefinition AssetTypeDefinition; @@ -25,7 +25,7 @@ public ModelComposition(AssetTypeDefinition definition, BasePieceModel modelLeft AssetTypeDefinition = definition; _modelLeft = modelLeft; _modelRight = modelRight; - AdditionalInstanceHandler = new AdditionalInstanceHandler(additionalData); + AdditionalInstanceHandler = new AdditionalInstanceHandler(additionalData, modelRight.StoreAsset.Prefab); if (_modelLeft == null && _modelRight == null) { diff --git a/SaberFactory/Models/ModelMetaData.cs b/SaberFactory/Models/ModelMetaData.cs index e2b015e..343ea9e 100644 --- a/SaberFactory/Models/ModelMetaData.cs +++ b/SaberFactory/Models/ModelMetaData.cs @@ -2,7 +2,7 @@ namespace SaberFactory.Models { - internal struct ModelMetaData + public struct ModelMetaData { public string Name; public string Author; diff --git a/SaberFactory/Models/PartEvents.cs b/SaberFactory/Models/PartEvents.cs index ea4f7ab..703b8eb 100644 --- a/SaberFactory/Models/PartEvents.cs +++ b/SaberFactory/Models/PartEvents.cs @@ -4,7 +4,7 @@ namespace SaberFactory.Models { - internal class PartEvents + public class PartEvents { public UnityEvent MultiplierUp; diff --git a/SaberFactory/Models/PieceCollection.cs b/SaberFactory/Models/PieceCollection.cs index c58c782..6360083 100644 --- a/SaberFactory/Models/PieceCollection.cs +++ b/SaberFactory/Models/PieceCollection.cs @@ -7,7 +7,7 @@ namespace SaberFactory.Models /// Class for managing a collection of parts /// /// - internal class PieceCollection : IEnumerable + public class PieceCollection : IEnumerable { public T this[AssetTypeDefinition definition] { diff --git a/SaberFactory/Models/PreloadMetaData.cs b/SaberFactory/Models/PreloadMetaData.cs index 79d4c9b..d8a9000 100644 --- a/SaberFactory/Models/PreloadMetaData.cs +++ b/SaberFactory/Models/PreloadMetaData.cs @@ -10,7 +10,7 @@ namespace SaberFactory.Models { - internal class PreloadMetaData : ICustomListItem + public class PreloadMetaData : ICustomListItem { public AssetTypeDefinition AssetTypeDefinition { get; private set; } @@ -40,18 +40,18 @@ public Sprite CoverSprite } } - public readonly AssetMetaPath AssetMetaPath; + internal readonly AssetMetaPath AssetMetaPath; private byte[] _coverData; private Sprite _coverSprite; private Texture2D _coverTex; - public PreloadMetaData(AssetMetaPath assetMetaPath) + internal PreloadMetaData(AssetMetaPath assetMetaPath) { AssetMetaPath = assetMetaPath; } - public PreloadMetaData(AssetMetaPath assetMetaPath, ICustomListItem customListItem, AssetTypeDefinition assetTypeDefinition) + internal PreloadMetaData(AssetMetaPath assetMetaPath, ICustomListItem customListItem, AssetTypeDefinition assetTypeDefinition) { AssetMetaPath = assetMetaPath; AssetTypeDefinition = assetTypeDefinition; diff --git a/SaberFactory/Models/PropHandler/PiecePropertyBlock.cs b/SaberFactory/Models/PropHandler/PiecePropertyBlock.cs index 64bf418..da287ad 100644 --- a/SaberFactory/Models/PropHandler/PiecePropertyBlock.cs +++ b/SaberFactory/Models/PropHandler/PiecePropertyBlock.cs @@ -8,7 +8,7 @@ namespace SaberFactory.Models.PropHandler /// /// Base class for storing customizable / serializable properties /// - internal abstract class PiecePropertyBlock : IFactorySerializable + public abstract class PiecePropertyBlock : IFactorySerializable { public TransformPropertyBlock TransformProperty; diff --git a/SaberFactory/Models/PropHandler/TransformPropertyBlock.cs b/SaberFactory/Models/PropHandler/TransformPropertyBlock.cs index 63fb8ae..e36e228 100644 --- a/SaberFactory/Models/PropHandler/TransformPropertyBlock.cs +++ b/SaberFactory/Models/PropHandler/TransformPropertyBlock.cs @@ -8,7 +8,7 @@ namespace SaberFactory.Models.PropHandler /// /// Class to store transform data /// - internal class TransformPropertyBlock : IFactorySerializable + public class TransformPropertyBlock : IFactorySerializable { public float Width { get; set; } = 1; diff --git a/SaberFactory/Models/SaberModel.cs b/SaberFactory/Models/SaberModel.cs index 8310c91..b9008f0 100644 --- a/SaberFactory/Models/SaberModel.cs +++ b/SaberFactory/Models/SaberModel.cs @@ -12,7 +12,7 @@ namespace SaberFactory.Models /// Stores information on how to build a saber instance /// [JsonObject(MemberSerialization.OptIn)] - internal class SaberModel : IFactorySerializable + public class SaberModel : IFactorySerializable { public bool IsEmpty => PieceCollection.PieceCount == 0; public readonly PieceCollection PieceCollection; diff --git a/SaberFactory/Models/SaberSet.cs b/SaberFactory/Models/SaberSet.cs index 59b2e92..018d27e 100644 --- a/SaberFactory/Models/SaberSet.cs +++ b/SaberFactory/Models/SaberSet.cs @@ -15,7 +15,7 @@ namespace SaberFactory.Models /// /// Stores currently used left and right saber model implementation /// - internal class SaberSet : IFactorySerializable, ILoadingTask + public class SaberSet : IFactorySerializable, ILoadingTask { public SaberModel LeftSaber { get; set; } diff --git a/SaberFactory/Models/TrailModel.cs b/SaberFactory/Models/TrailModel.cs index e4151ab..aebbb0f 100644 --- a/SaberFactory/Models/TrailModel.cs +++ b/SaberFactory/Models/TrailModel.cs @@ -14,7 +14,7 @@ namespace SaberFactory.Models /// /// Stores information on how to build a trail /// - internal class TrailModel : IFactorySerializable + public class TrailModel : IFactorySerializable { public int OriginalLength { get; private set; } diff --git a/SaberFactory/Modifiers/BaseModifierImpl.cs b/SaberFactory/Modifiers/BaseModifierImpl.cs index 70093cd..f4714d6 100644 --- a/SaberFactory/Modifiers/BaseModifierImpl.cs +++ b/SaberFactory/Modifiers/BaseModifierImpl.cs @@ -3,7 +3,7 @@ namespace SaberFactory.Modifiers { - internal abstract class BaseModifierImpl : UpdatableSerialializable, IStringUiProvider + public abstract class BaseModifierImpl : UpdatableSerialializable, IStringUiProvider { public abstract string Name { get; } public abstract string TypeName { get; } diff --git a/SaberFactory/Modifiers/ModifyableComponentManager.cs b/SaberFactory/Modifiers/ModifyableComponentManager.cs index 800a63d..eba59bf 100644 --- a/SaberFactory/Modifiers/ModifyableComponentManager.cs +++ b/SaberFactory/Modifiers/ModifyableComponentManager.cs @@ -12,7 +12,7 @@ namespace SaberFactory.Modifiers { - internal class ModifyableComponentManager : IFactorySerializable + public class ModifyableComponentManager : IFactorySerializable { public readonly Dictionary Mods = new Dictionary(); @@ -22,7 +22,7 @@ internal class ModifyableComponentManager : IFactorySerializable public ModifyableComponentManager(GameObject prefab) { - SaberModifierCollection = prefab.GetComponent(); + SaberModifierCollection = prefab.GetComponentInChildren(); } public async Task FromJson(JObject obj, Serializer serializer) @@ -102,7 +102,7 @@ public void SetInstance(GameObject gameObject) return; } - var saberModifierCollection = gameObject.GetComponent(); + var saberModifierCollection = gameObject.GetComponentInChildren(); if (saberModifierCollection == null) { return; diff --git a/SaberFactory/Modifiers/TransformModifierImpl.cs b/SaberFactory/Modifiers/TransformModifierImpl.cs index 2278b88..cf2491a 100644 --- a/SaberFactory/Modifiers/TransformModifierImpl.cs +++ b/SaberFactory/Modifiers/TransformModifierImpl.cs @@ -276,6 +276,11 @@ private void SetPositionOffset(Vector3 offset) foreach (var t in _transforms) { + if (!t.transform) + { + continue; + } + t.transform.localPosition = t.ogPos + offset; } } @@ -289,6 +294,11 @@ private void SetScaleOffset(Vector3 offset) foreach (var t in _transforms) { + if (!t.transform) + { + continue; + } + t.transform.localScale = t.ogScale + offset; } } @@ -302,6 +312,11 @@ private void SetRotationOffset(float offset) foreach (var t in _transforms) { + if (!t.transform) + { + continue; + } + t.transform.localRotation = t.ogRotation * Quaternion.Euler(Vector3.forward * offset); } } diff --git a/SaberFactory/Plugin.cs b/SaberFactory/Plugin.cs index d948c6a..386ffe9 100644 --- a/SaberFactory/Plugin.cs +++ b/SaberFactory/Plugin.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using HarmonyLib; @@ -6,10 +7,14 @@ using IPA.Config; using IPA.Config.Stores; using IPA.Loader; +using IPA.Utilities; using SaberFactory.Configuration; using SaberFactory.Helpers; using SaberFactory.Installers; +using SaberFactory.Misc; using SiraUtil.Zenject; +using UnityEngine; +using Zenject; using IPALogger = IPA.Logging.Logger; namespace SaberFactory @@ -42,6 +47,12 @@ public async void Init(IPALogger logger, Config conf, Zenjector zenjector, Plugi zenjector.Install(Location.App, logger, pluginConfig, metadata); zenjector.Install(Location.Menu); zenjector.Install(Location.Player | Location.MultiPlayer); + + zenjector.Mutate("Environment", (ctx, ogBurnMark) => + { + var newBurner = CommonHelpers.Upgrade(ogBurnMark, typeof(CustomSaberBurnMarkArea)); + ctx.Container.QueueForInject(newBurner); + }); } [OnEnable] diff --git a/SaberFactory/ProjectComponents/HelpAttribute.cs b/SaberFactory/ProjectComponents/HelpAttribute.cs new file mode 100644 index 0000000..e4a08d0 --- /dev/null +++ b/SaberFactory/ProjectComponents/HelpAttribute.cs @@ -0,0 +1,230 @@ +// -------------------------------------------------------------------------------------------------------------------- +/// +/// +/// Copyright (c) 2017, John Earnshaw, reblGreen Software Limited +/// +/// +/// +/// All rights reserved. +/// Redistribution and use in source and binary forms, with or without modification, are +/// permitted provided that the following conditions are met: +/// 1. Redistributions of source code must retain the above copyright notice, this list of +/// conditions and the following disclaimer. +/// 2. Redistributions in binary form must reproduce the above copyright notice, this list +/// of conditions and the following disclaimer in the documentation and/or other materials +/// provided with the distribution. +/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +/// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +/// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE +/// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +/// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +/// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +/// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +/// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/// +// -------------------------------------------------------------------------------------------------------------------- +using System; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + +[AttributeUsage(AttributeTargets.Field, Inherited = true)] +public class HelpAttribute : PropertyAttribute +{ + public readonly string text; + + // MessageType exists in UnityEditor namespace and can throw an exception when used outside the editor. + // We spoof MessageType at the bottom of this script to ensure that errors are not thrown when + // MessageType is unavailable. + public readonly MessageType type; + + + /// + /// Adds a HelpBox to the Unity property inspector above this field. + /// + /// The help text to be displayed in the HelpBox. + /// The icon to be displayed in the HelpBox. + public HelpAttribute(string text, MessageType type = MessageType.Info) + { + this.text = text; + this.type = type; + } +} + +#if UNITY_EDITOR +[CustomPropertyDrawer(typeof(HelpAttribute))] +public class HelpDrawer : PropertyDrawer +{ + // Used for top and bottom padding between the text and the HelpBox border. + const int paddingHeight = 8; + + // Used to add some margin between the the HelpBox and the property. + const int marginHeight = 2; + + // Global field to store the original (base) property height. + float baseHeight = 0; + + // Custom added height for drawing text area which has the MultilineAttribute. + float addedHeight = 0; + + /// + /// A wrapper which returns the PropertyDrawer.attribute field as a HelpAttribute. + /// + HelpAttribute helpAttribute { get { return (HelpAttribute)attribute; } } + + /// + /// A helper property to check for RangeAttribute. + /// + RangeAttribute rangeAttribute + { + get + { + var attributes = fieldInfo.GetCustomAttributes(typeof(RangeAttribute), true); + return attributes != null && attributes.Length > 0 ? (RangeAttribute)attributes[0] : null; + } + } + + /// + /// A helper property to check for MultiLineAttribute. + /// + MultilineAttribute multilineAttribute + { + get + { + var attributes = fieldInfo.GetCustomAttributes(typeof(MultilineAttribute), true); + return attributes != null && attributes.Length > 0 ? (MultilineAttribute)attributes[0] : null; + } + } + + + public override float GetPropertyHeight(SerializedProperty prop, GUIContent label) + { + // We store the original property height for later use... + baseHeight = base.GetPropertyHeight(prop, label); + + // This stops icon shrinking if text content doesn't fill out the container enough. + float minHeight = paddingHeight * 5; + + // Calculate the height of the HelpBox using the GUIStyle on the current skin and the inspector + // window's currentViewWidth. + var content = new GUIContent(helpAttribute.text); + var style = GUI.skin.GetStyle("helpbox"); + + var height = style.CalcHeight(content, EditorGUIUtility.currentViewWidth); + + // We add tiny padding here to make sure the text is not overflowing the HelpBox from the top + // and bottom. + height += marginHeight * 2; + + // Since we draw a custom text area with the label above if our property contains the + // MultilineAttribute, we need to add some extra height to compensate. This is stored in a + // seperate global field so we can use it again later. + if (multilineAttribute != null && prop.propertyType == SerializedPropertyType.String) + { + addedHeight = 48f; + } + + // If the calculated HelpBox is less than our minimum height we use this to calculate the returned + // height instead. + return height > minHeight ? height + baseHeight + addedHeight : minHeight + baseHeight + addedHeight; + } + + + public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label) + { + // We get a local reference to the MultilineAttribute as we use it twice in this method and it + // saves calling the logic twice for minimal optimization, etc... + var multiline = multilineAttribute; + + EditorGUI.BeginProperty(position, label, prop); + + // Copy the position out so we can calculate the position of our HelpBox without affecting the + // original position. + var helpPos = position; + + helpPos.height -= baseHeight + marginHeight; + + + if (multiline != null) + { + helpPos.height -= addedHeight; + } + + // Renders the HelpBox in the Unity inspector UI. + EditorGUI.HelpBox(helpPos, helpAttribute.text, helpAttribute.type); + + position.y += helpPos.height + marginHeight; + position.height = baseHeight; + + + // If we have a RangeAttribute on our field, we need to handle the PropertyDrawer differently to + // keep the same style as Unity's default. + var range = rangeAttribute; + + if (range != null) + { + if (prop.propertyType == SerializedPropertyType.Float) + { + EditorGUI.Slider(position, prop, range.min, range.max, label); + } + else if (prop.propertyType == SerializedPropertyType.Integer) + { + EditorGUI.IntSlider(position, prop, (int)range.min, (int)range.max, label); + } + else + { + // Not numeric so draw standard property field as punishment for adding RangeAttribute to + // a property which can not have a range :P + EditorGUI.PropertyField(position, prop, label); + } + } + else if (multiline != null) + { + // Here's where we handle the PropertyDrawer differently if we have a MultiLineAttribute, to try + // and keep some kind of multiline text area. This is not identical to Unity's default but is + // better than nothing... + if (prop.propertyType == SerializedPropertyType.String) + { + var style = GUI.skin.label; + var size = style.CalcHeight(label, EditorGUIUtility.currentViewWidth); + + EditorGUI.LabelField(position, label); + + position.y += size; + position.height += addedHeight - size; + + // Fixed text dissappearing thanks to: http://answers.unity3d.com/questions/244043/textarea-does-not-work-text-dissapears-solution-is.html + prop.stringValue = EditorGUI.TextArea(position, prop.stringValue); + } + else + { + // Again with a MultilineAttribute on a non-text field deserves for the standard property field + // to be drawn as punishment :P + EditorGUI.PropertyField(position, prop, label); + } + } + else + { + // If we get to here it means we're drawing the default property field below the HelpBox. More custom + // and built in PropertyDrawers could be implemented to enable HelpBox but it could easily make for + // hefty else/if block which would need refactoring! + EditorGUI.PropertyField(position, prop, label); + } + + EditorGUI.EndProperty(); + } +} +#else + // Replicate MessageType Enum if we are not in editor as this enum exists in UnityEditor namespace. + // This should stop errors being logged the same as Shawn Featherly's commit in the Github repo but I + // feel is cleaner than having the conditional directive in the middle of the HelpAttribute constructor. + public enum MessageType + { + None, + Info, + Warning, + Error, + } +#endif diff --git a/SaberFactory/ProjectComponents/SFBurnmarks.cs b/SaberFactory/ProjectComponents/SFBurnmarks.cs new file mode 100644 index 0000000..c698dc9 --- /dev/null +++ b/SaberFactory/ProjectComponents/SFBurnmarks.cs @@ -0,0 +1,29 @@ +using UnityEngine; + +namespace SaberFactory.ProjectComponents +{ + public class SFBurnmarks : MonoBehaviour + { + [Header("Parameters")] + public float RandomBurnmarkJitter = 0.001f; + + public float FadeStrength = 0.3f; + + public float BurnmarkSize = 0.1f; + + [Help("Only Assign the materials you actually need")] + [Header("Basic Materials")] + [Tooltip("Material for the spark particle system")] + public Material SparkleMaterial; + + [Tooltip("Material for the linerenderer of the burnmarks")] + public Material BurnMarkMaterial; + + [Tooltip("The rendertexture of the burnmarks will be passed to this material relative to the floor")] + public Material FloorMaterial; + + [Header("Advanced Materials")] + [Tooltip("Material to use for blitting the faded rendertexture")] + public Material FadeoutMaterial; + } +} \ No newline at end of file diff --git a/SaberFactory/ProjectComponents/SFClashEffect.cs b/SaberFactory/ProjectComponents/SFClashEffect.cs new file mode 100644 index 0000000..914d241 --- /dev/null +++ b/SaberFactory/ProjectComponents/SFClashEffect.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace SaberFactory.ProjectComponents +{ + public class SFClashEffect : MonoBehaviour + { + public Material Material; + } +} \ No newline at end of file diff --git a/SaberFactory/ProjectComponents/SFSaberSound.cs b/SaberFactory/ProjectComponents/SFSaberSound.cs new file mode 100644 index 0000000..262e8a4 --- /dev/null +++ b/SaberFactory/ProjectComponents/SFSaberSound.cs @@ -0,0 +1,78 @@ +#if !UNITY +using SaberFactory.Helpers; +#endif +using UnityEngine; + +namespace SaberFactory.ProjectComponents +{ + public class SFSaberSound : MonoBehaviour + { + public Transform SaberTop; + + public AudioSource AudioSource; + + public AnimationCurve PitchBySpeedCurve; + + public AnimationCurve GainBySpeedCurve; + + public float SpeedMultiplier = 0.05f; + + public float UpSmooth = 4f; + + public float DownSmooth = 4f; + + [Tooltip("No sound is produced if saber point moves more than this distance in one frame.")] + public float NoSoundTopThresholdSqr = 1f; + + [Range(0, 1)] + public float Volume = 1; + +#if !UNITY + public float ConfigVolume = 1; + + private Vector3 _prevPos; + private float _speed; + + public virtual void Start() + { + _prevPos = SaberTop.position; + + var saberMb = SaberHelpers.GetSaberMonoBehaviour(gameObject); + if (saberMb) + { + saberMb.RegisterComponent(this); + } + } + + public virtual void Update() + { + var position = SaberTop.position; + if ((_prevPos - position).sqrMagnitude > NoSoundTopThresholdSqr) _prevPos = position; + + float targetSpeed; + if (Time.deltaTime == 0f) + { + targetSpeed = 0f; + } + else + { + targetSpeed = SpeedMultiplier * Vector3.Distance(position, _prevPos) / Time.deltaTime; + } + + if (targetSpeed < _speed) + { + _speed = Mathf.Clamp01(Mathf.Lerp(_speed, targetSpeed, Time.deltaTime * DownSmooth)); + } + else + { + _speed = Mathf.Clamp01(Mathf.Lerp(_speed, targetSpeed, Time.deltaTime * UpSmooth)); + } + + AudioSource.pitch = PitchBySpeedCurve.Evaluate(_speed); + AudioSource.volume = GainBySpeedCurve.Evaluate(_speed) * Volume * ConfigVolume; + + _prevPos = position; + } +#endif + } +} \ No newline at end of file diff --git a/SaberFactory/ProjectComponents/SaberModifierCollection.cs b/SaberFactory/ProjectComponents/SaberModifierCollection.cs index 1df1792..f1bb824 100644 --- a/SaberFactory/ProjectComponents/SaberModifierCollection.cs +++ b/SaberFactory/ProjectComponents/SaberModifierCollection.cs @@ -26,7 +26,7 @@ public bool Init() { return _inited; } - + Newtonsoft.Json.JsonConvert.PopulateObject(ObjectJson, this); foreach (var mod in VisibilityModifiers) diff --git a/SaberFactory/Resources/pedestal b/SaberFactory/Resources/pedestal new file mode 100644 index 0000000..90d9de4 Binary files /dev/null and b/SaberFactory/Resources/pedestal differ diff --git a/SaberFactory/SaberFactory.csproj b/SaberFactory/SaberFactory.csproj index 20a03c4..9d182b1 100644 --- a/SaberFactory/SaberFactory.csproj +++ b/SaberFactory/SaberFactory.csproj @@ -148,10 +148,10 @@ False - ..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.JSONSerializeModule.dll + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.JSONSerializeModule.dll - ..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.ParticleSystemModule.dll + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.ParticleSystemModule.dll $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.PhysicsModule.dll @@ -195,7 +195,8 @@ - + + diff --git a/SaberFactory/Serialization/PresetSaveManager.cs b/SaberFactory/Serialization/PresetSaveManager.cs index 893e963..7717cf9 100644 --- a/SaberFactory/Serialization/PresetSaveManager.cs +++ b/SaberFactory/Serialization/PresetSaveManager.cs @@ -7,7 +7,7 @@ namespace SaberFactory.Serialization { - internal class PresetSaveManager + public class PresetSaveManager { private readonly DirectoryInfo _presetDir; private readonly Serializer _serializer; diff --git a/SaberFactory/Serialization/Serializer.cs b/SaberFactory/Serialization/Serializer.cs index 09774fd..2cbf2d3 100644 --- a/SaberFactory/Serialization/Serializer.cs +++ b/SaberFactory/Serialization/Serializer.cs @@ -10,7 +10,7 @@ namespace SaberFactory.Serialization { - internal class Serializer + public class Serializer { public static readonly JsonSerializer JsonSerializer = new JsonSerializer(); diff --git a/SaberFactory/Serialization/UpdatableSerialializable.cs b/SaberFactory/Serialization/UpdatableSerialializable.cs index a3a6c9f..9e17b18 100644 --- a/SaberFactory/Serialization/UpdatableSerialializable.cs +++ b/SaberFactory/Serialization/UpdatableSerialializable.cs @@ -9,7 +9,7 @@ namespace SaberFactory.Serialization { - internal class UpdatableSerialializable : IFactorySerializable + public class UpdatableSerialializable : IFactorySerializable { private readonly List<(HandledValueAttribute, PropertyInfo)> _updateProps = new List<(HandledValueAttribute, PropertyInfo)>(); diff --git a/SaberFactory/UI/CustomSaber/Views/Modifier/MainModifierPanelView.cs b/SaberFactory/UI/CustomSaber/Views/Modifier/MainModifierPanelView.cs index 63c8595..c1bdcf1 100644 --- a/SaberFactory/UI/CustomSaber/Views/Modifier/MainModifierPanelView.cs +++ b/SaberFactory/UI/CustomSaber/Views/Modifier/MainModifierPanelView.cs @@ -26,6 +26,7 @@ internal class MainModifierPanelView : SubView, INavigationCategoryView protected override string _resourceName => PatViewPath; [UIValue("pat-preview")] private string PatPreview => "https://www.youtube.com/watch?v=xI-YFoPilxU"; [UIValue("has-pat-preview")] private bool HasPatPreview => true; + [UIValue("additional-pat-info")] private string AdditionalPatInfo => "Feature needs to be implemented on saber"; #endif [UIObject("container")] private readonly GameObject _container = null; diff --git a/SaberFactory/UI/CustomSaber/Views/SaberSelectorView.cs b/SaberFactory/UI/CustomSaber/Views/SaberSelectorView.cs index fe438d5..811e333 100644 --- a/SaberFactory/UI/CustomSaber/Views/SaberSelectorView.cs +++ b/SaberFactory/UI/CustomSaber/Views/SaberSelectorView.cs @@ -16,7 +16,6 @@ using SaberFactory.Helpers; using SaberFactory.Loaders; using SaberFactory.Models; -using SaberFactory.Serialization; using SaberFactory.UI.CustomSaber.CustomComponents; using SaberFactory.UI.CustomSaber.Popups; using SaberFactory.UI.Lib; @@ -62,12 +61,10 @@ private float SaberWidth [Inject] private readonly EditorInstanceManager _editorInstanceManager = null; [Inject] private readonly MainAssetStore _mainAssetStore = null; - [Inject(Id = nameof(SaberFactory))] private readonly PluginMetadata _metadata = null; [Inject] private readonly PluginConfig _pluginConfig = null; [Inject] private readonly List _remoteParts = null; [Inject] private readonly SaberFileWatcher _saberFileWatcher = null; [Inject] private readonly SaberSet _saberSet = null; - [Inject] private readonly Serializer _serializer = null; private ModelComposition _currentComposition; private PreloadMetaData _currentPreloadMetaData; private SaberListDirectoryManager _dirManager; @@ -108,7 +105,7 @@ private async void Setup() _dirManager = new SaberListDirectoryManager(_mainAssetStore.AdditionalCustomSaberFolders); _saberList.OnItemSelected += SaberSelected; _saberList.OnCategorySelected += DirectorySelected; - _listTitle = "Saber Factory " + _metadata.HVersion + ""; + _listTitle = ""; _saberList.SetText(_listTitle); await LoadSabers(); @@ -123,15 +120,6 @@ private async void Setup() } } - private async void Update() - { - if (Input.GetKeyDown(KeyCode.N)) - { - File.WriteAllText("Serialized.txt", (await _saberSet.ToJson(_serializer)).ToString()); - Debug.LogWarning("Serialized"); - } - } - private async void DirectorySelected(string dir) { _dirManager.Navigate(dir); @@ -206,6 +194,7 @@ orderby meta.IsFavorite descending if (_currentComposition != null) { _saberList.Select(_mainAssetStore.GetMetaDataForComposition(_currentComposition)?.ListName, !scrollToTop); + UpdatePedestalText(_currentComposition); } if (scrollToTop) @@ -213,6 +202,7 @@ orderby meta.IsFavorite descending _saberList.ScrollTo(0); } + UpdateUi(); } @@ -275,14 +265,27 @@ await _mainAssetStore.CreateMetaData( { await ShowSabers(); } + + _editor.FlashPedestal(new Color(0.24f, 0.77f, 1f)); } private void CompositionDidChange(ModelComposition comp) { _currentComposition = comp; + UpdatePedestalText(comp); _saberList.Select(comp); } + private void UpdatePedestalText(ICustomListItem item) + { + var saberName = item.ListName; + if (saberName.Length > 12) + { + saberName = saberName.Substring(0, 9) + "..."; + } + _editor.SetPedestalText(0, saberName); + } + private void UpdateUi() { if (_currentComposition == null) diff --git a/SaberFactory/UI/CustomSaber/Views/SettingsView.bsml b/SaberFactory/UI/CustomSaber/Views/SettingsView.bsml index 08572c9..17f2c61 100644 --- a/SaberFactory/UI/CustomSaber/Views/SettingsView.bsml +++ b/SaberFactory/UI/CustomSaber/Views/SettingsView.bsml @@ -1,7 +1,7 @@  - + @@ -13,6 +13,8 @@ hover-hint='Choose a random saber on every song start'/> + + diff --git a/SaberFactory/UI/CustomSaber/Views/SettingsView.cs b/SaberFactory/UI/CustomSaber/Views/SettingsView.cs index c6938a4..63f6ad3 100644 --- a/SaberFactory/UI/CustomSaber/Views/SettingsView.cs +++ b/SaberFactory/UI/CustomSaber/Views/SettingsView.cs @@ -1,7 +1,10 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using System.Threading; using BeatSaberMarkupLanguage.Attributes; using SaberFactory.Configuration; +using SaberFactory.Editor; +using SaberFactory.ProjectComponents; using SaberFactory.UI.CustomSaber.CustomComponents; using SaberFactory.UI.Lib; using UnityEngine; @@ -11,8 +14,8 @@ namespace SaberFactory.UI.CustomSaber.Views { internal class SettingsView : SubView, INavigationCategoryView { - private static readonly string PROFILE_URL = "https://ko-fi.com/tonimacaroni"; - private static readonly string DISCORD_URL = "https://discord.gg/PjD7WcChH3"; + private const string ProfileUrl = "https://ko-fi.com/tonimacaroni"; + private const string DiscordUrl = "https://discord.gg/PjD7WcChH3"; [UIComponent("changelog-popup")] private readonly ChangelogPopup _changelogPopup = null; [UIObject("github-button")] private readonly GameObject _githubButton = null; @@ -61,16 +64,48 @@ private bool OverrideSongSaber } } + private float SwingSoundVolume + { + get => _pluginConfig.SwingSoundVolume; + set + { + _pluginConfig.SwingSoundVolume = value; + if (_saberSound) + { + _saberSound.ConfigVolume = value; + } + OnPropertyChanged(); + } + } + + private bool EnableCustomBurnmarks + { + get => _pluginConfig.EnableCustomBurnmarks; + set + { + _pluginConfig.EnableCustomBurnmarks = value; + OnPropertyChanged(); + } + } + [Inject] private readonly PluginConfig _pluginConfig = null; [Inject] private readonly PluginManager _pluginManager = null; + [Inject] private readonly EditorInstanceManager _editorInstanceManager = null; public ENavigationCategory Category => ENavigationCategory.Settings; + private SFSaberSound _saberSound; + public override void DidClose() { _changelogPopup.Hide(); } + public override void DidOpen() + { + _editorInstanceManager.CurrentSaber.GetSaberComponent(out _saberSound); + } + [UIAction("#post-parse")] private async void Setup() { @@ -84,13 +119,13 @@ private async void Setup() [UIAction("profile-clicked")] private void ProfileClicked() { - Process.Start(PROFILE_URL); + Process.Start(ProfileUrl); } [UIAction("discord-clicked")] private void DiscordClicked() { - Process.Start(DISCORD_URL); + Process.Start(DiscordUrl); } [UIAction("github-clicked")] diff --git a/SaberFactory/UI/Lib/PropCell.cs b/SaberFactory/UI/Lib/PropCell.cs index c6f7613..079541a 100644 --- a/SaberFactory/UI/Lib/PropCell.cs +++ b/SaberFactory/UI/Lib/PropCell.cs @@ -14,7 +14,6 @@ public class PropCell : TableCell public void SetData(PropertyDescriptor data) { - Console.WriteLine($"Setup Data {data.Type}"); switch (data.Type) { case EPropertyType.Float: diff --git a/SaberFactory/UI/PatreonView.bsml b/SaberFactory/UI/PatreonView.bsml index eb49547..33ab3f2 100644 --- a/SaberFactory/UI/PatreonView.bsml +++ b/SaberFactory/UI/PatreonView.bsml @@ -2,11 +2,14 @@ xsi:noNamespaceSchemaLocation='file:///C:/CustomBSMLSchema.xsd' pref-height="{template;NavHeight}" bg="round-rect-panel" custom-color="$default-panel" vertical-fit="PreferredSize" pref-width="70" pad="5"> - + + + + \ No newline at end of file diff --git a/SaberFactory/manifest.json b/SaberFactory/manifest.json index ac3db7c..82706ec 100644 --- a/SaberFactory/manifest.json +++ b/SaberFactory/manifest.json @@ -3,7 +3,7 @@ "id": "SaberFactory", "name": "Saber Factory", "author": "Toni Macaroni", - "version": "2.4.2", + "version": "2.5.0", "description": "A highly customizable saber mod", "gameVersion": "1.19.0", "dependsOn": { diff --git a/changelog.md b/changelog.md index a6fe1ce..53651b6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,9 @@ *(for Beat Saber 1.19)* -- Fixed choosing trails on trailless sabers -- Automatically apply and close "choose trail" popup on exit +- Ability to export only one saber if both of your sabers are identical +- Added custom saber swing sounds +- Added custom saber burnmarks +- New pedestal +- Made majority of the sf internals public to make it easier for mods to interact with sf *Needs `BeatSaberMarkupLanguage` and `SiraUtil` (available on ModAssistant)* \ No newline at end of file