From faa391c309887ba79f5d05de856d7fc9a8d060c0 Mon Sep 17 00:00:00 2001 From: LadyAliceMargatroid Date: Thu, 18 Jul 2024 17:32:29 -0700 Subject: [PATCH] New item SnowGlobe And a bunch of fixes --- ScarletMansion/ScarletMansion/Assets.cs | 18 ++- .../ScarletMansion/GamePatch/InitPatch.cs | 28 +++- .../Items/PlayerAnimatorStateHelper.cs | 124 +++++++++++++++ .../GamePatch/Items/ScarletKnife.cs | 11 +- .../GamePatch/Items/ScarletSnowGlobe.cs | 149 ++++++++++++++++++ .../NetworkObjectListScriptableObject.cs | 3 +- ScarletMansion/ScarletMansion/Plugin.cs | 10 +- ScarletMansion/ScarletMansion/PluginConfig.cs | 10 ++ .../ScarletMansion/ScarletMansion.csproj | 2 + 9 files changed, 337 insertions(+), 18 deletions(-) create mode 100644 ScarletMansion/ScarletMansion/GamePatch/Items/PlayerAnimatorStateHelper.cs create mode 100644 ScarletMansion/ScarletMansion/GamePatch/Items/ScarletSnowGlobe.cs diff --git a/ScarletMansion/ScarletMansion/Assets.cs b/ScarletMansion/ScarletMansion/Assets.cs index 22964bf..c3282a6 100644 --- a/ScarletMansion/ScarletMansion/Assets.cs +++ b/ScarletMansion/ScarletMansion/Assets.cs @@ -140,7 +140,8 @@ namespace ScarletMansion { { "Deco. crystal", () => PluginConfig.Instance.crystalWeightValue }, { "Shattered deco. crystal", () => PluginConfig.Instance.crystalBrokenWeightValue }, { "Demonic painting", () => 0 }, - { "Maid's knife", () => 0 } + { "Maid's knife", () => 0 }, + { "Doll Snow Globe", () => PluginConfig.Instance.snowGlobeWeightValue } }; @@ -186,7 +187,7 @@ namespace ScarletMansion { dungeon = Load("SDMLevel"); networkObjectList = Load("SDMList"); - entranceAudioClip = Load("entrance"); + entranceAudioClip = Load("entrance_ogg"); knight = new Enemy( Load("NET_KnightEnemy"), @@ -209,9 +210,21 @@ namespace ScarletMansion { globalItems = new List(); scrapItems = new List(); foreach(var i in networkObjectList.items){ + var entry = new GlobalItem(i); + globalItems.Add(entry); + + Items.RegisterItem(i); + Plugin.logger.LogInfo($"Global Item {i.itemName} registered"); + } + + foreach(var i in networkObjectList.scrapItems) { var entry = new ScrapItem(i, GetItemRarityFunction(i)); scrapItems.Add(entry); globalItems.Add(entry); + + Items.RegisterScrap(i, 0, Levels.LevelTypes.None); + NetworkPrefabs.RegisterNetworkPrefab(i.spawnPrefab); + Plugin.logger.LogInfo($"Scrap Item {i.itemName} registered"); } flashlight = new Flashlight("Pro Flashlight", 0); @@ -224,6 +237,7 @@ namespace ScarletMansion { public static Func GetItemRarityFunction(Item item){ var name = item.itemName; + if (itemRarityTable.ContainsKey(name)){ return itemRarityTable[name]; } diff --git a/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs b/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs index 377a9a1..76a1444 100644 --- a/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs +++ b/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs @@ -48,6 +48,7 @@ namespace ScarletMansion.GamePatch { [HarmonyPrefix] public static void StartOfRound_Start(ref StartOfRound __instance) { ScarletYukariTrigger.audioClipIndex = -1; + PlayerAnimatorStateHelper.ClearAnimators(); __instance.StartCoroutine(WaitForNetworkObject(__instance, CreateNetworkManager)); @@ -110,7 +111,6 @@ namespace ScarletMansion.GamePatch { } private static void FixParticleSystemMaterial(ParticleSystemRenderer toFixParticleSystem, ParticleSystemRenderer copyParticleSystem) { - Debug.Log(copyParticleSystem.name); toFixParticleSystem.sharedMaterial = copyParticleSystem.sharedMaterial; } @@ -212,18 +212,19 @@ namespace ScarletMansion.GamePatch { itemToFix.itemIcon = itemReference.itemIcon; itemToFix.grabSFX = itemReference.grabSFX; itemToFix.dropSFX = itemReference.dropSFX; + itemToFix.pocketSFX = itemReference.pocketSFX; } var magClass = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "magnifyingglass"); if (GameReadNullCheck(magClass, "magnifyingglass", "Item will have missing image and sound assets")) { QuickItemFix(Assets.scrapItems[0].item, magClass); QuickItemFix(Assets.scrapItems[1].item, magClass); + QuickItemFix(Assets.scrapItems[4].item, magClass); } var paintClass = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "fancypainting"); - if (GameReadNullCheck(paintClass, "fancypainting", "Item will have missing image and sound assets")){ - QuickItemFix(Assets.scrapItems[2].item, magClass); + QuickItemFix(Assets.scrapItems[2].item, paintClass); } void UpdateFlashlight(Assets.Flashlight flashlight, string search, int fixIndex){ @@ -311,6 +312,27 @@ namespace ScarletMansion.GamePatch { UpdateFlashlight(Assets.flashlightBB, "flashlight", 3); } + var keyItem = round.allItemsList.itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "key"); + if (GameReadNullCheck(keyItem, "key", "Item will have missing image and sound assets")){ + var scarletPrefab = Assets.networkObjectList.toFixGameObjects[5]; + var scarletKey = scarletPrefab.GetComponent().itemProperties; + QuickItemFix(scarletKey, keyItem); + + var keyPrefab = keyItem.spawnPrefab; + scarletPrefab.GetComponent().sharedMesh = keyPrefab.GetComponent().sharedMesh; + + var mats = keyPrefab.GetComponent().sharedMaterials; + mats = mats.Select(m => { + var n = new Material(m); + n.color = Color.red; + return n; + }).ToArray(); + scarletPrefab.GetComponent().sharedMaterials = mats; + + + Assets.dungeonExtended.OverrideKeyPrefab = scarletPrefab; + } + } catch (Exception e) { Plugin.logger.LogError("Error when reading base game's item list. Pretty big error I would say"); Plugin.logger.LogError(e); diff --git a/ScarletMansion/ScarletMansion/GamePatch/Items/PlayerAnimatorStateHelper.cs b/ScarletMansion/ScarletMansion/GamePatch/Items/PlayerAnimatorStateHelper.cs new file mode 100644 index 0000000..231eef2 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/Items/PlayerAnimatorStateHelper.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace ScarletMansion.GamePatch.Items { + + public class PlayerAnimatorStateHelper { + private Animator animator; + private AnimatorStateInfo currentStateInfo; + private float currentAnimationTime; + private bool isCrouching; + private bool isJumping; + private bool isWalking; + private bool isSprinting; + private RuntimeAnimatorController originalAnimatorController; + + public PlayerAnimatorStateHelper(Animator animator) { + this.animator = animator; + this.originalAnimatorController = animator.runtimeAnimatorController; + } + + private static Dictionary animatorStateHelpers; + + public static PlayerAnimatorStateHelper TryAddAnimator(ulong id, Animator animator){ + if (!animatorStateHelpers.TryGetValue(id, out var v)) { + v = new PlayerAnimatorStateHelper(animator); + animatorStateHelpers.Add(id, v); + } + return v; + } + + public static void ClearAnimators(){ + animatorStateHelpers = new Dictionary(); + } + + //We need to Save the important states due to how unity handles switching animator overrides (So stupid) + public void SaveAnimatorStates() { + if (animator != null) { + isCrouching = animator.GetBool("crouching"); + isJumping = animator.GetBool("Jumping"); + isWalking = animator.GetBool("Walking"); + isSprinting = animator.GetBool("Sprinting"); + currentStateInfo = animator.GetCurrentAnimatorStateInfo(0); + currentAnimationTime = currentStateInfo.normalizedTime; + + //Debug.Log("Saved Animator States - Crouching: " + isCrouching + + // ", Jumping: " + isJumping + + // ", Walking: " + isWalking + + // ", Sprinting: " + isSprinting + + // ", State: " + currentStateInfo.fullPathHash + + // ", Time: " + currentAnimationTime); + } + } + + //We need to Restore the important states due to how unity handles switching animator overrides + public void RestoreAnimatorStates() { + if (animator != null) { + animator.Play(currentStateInfo.fullPathHash, 0, currentAnimationTime); + animator.SetBool("crouching", isCrouching); + animator.SetBool("Jumping", isJumping); + animator.SetBool("Walking", isWalking); + animator.SetBool("Sprinting", isSprinting); + + //Debug.Log("Restored Animator States - Crouching: " + isCrouching + + // ", Jumping: " + isJumping + + // ", Walking: " + isWalking + + // ", Sprinting: " + isSprinting + + // ", State: " + currentStateInfo.fullPathHash + + // ", Time: " + currentAnimationTime); + } + } + + public void RestoreOriginalAnimatorController() { + if (animator != null) { + animator.runtimeAnimatorController = originalAnimatorController; + RestoreAnimatorStates(); + } + } + + public class AnimationClipOverrides : List> { + public AnimationClipOverrides(int capacity) : base(capacity) {} + + public AnimationClip this[string name] { + get => this.Find(x => x.Key.name.Equals(name)).Value; + set { + int index = this.FindIndex(x => x.Key.name.Equals(name)); + if (index != -1) this[index] = new KeyValuePair(this[index].Key, value); + } + } + + public override string ToString() { + var items = this.Select(d => { + var keyValue = d.Key ? d.Key.name : "NULL"; + var vValue = d.Value ? d.Value.name : "NULL"; + return $"{keyValue}: {vValue}"; + }); + return string.Join(", ", items); + } + } + + public void SetAnimatorOverrideController(AnimatorOverrideController overrideController, string originalAnimationClipName, AnimationClip overrideAnimationClip) { + if (animator != null) { + overrideController.runtimeAnimatorController = originalAnimatorController; + + var clipOverrides = new AnimationClipOverrides(overrideController.overridesCount); + overrideController.GetOverrides(clipOverrides); + clipOverrides[originalAnimationClipName] = overrideAnimationClip; + overrideController.ApplyOverrides(clipOverrides); + + animator.runtimeAnimatorController = overrideController; + RestoreAnimatorStates(); + } + } + + public RuntimeAnimatorController GetOriginalAnimatorController() { + return originalAnimatorController; + } + } + + +} diff --git a/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs index d94c093..15298d6 100644 --- a/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs +++ b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs @@ -81,7 +81,7 @@ namespace ScarletMansion.GamePatch.Items public void HitKnife(bool cancel = false) { if (previousPlayerHeldBy == null) { - Debug.LogError("Previousplayerheldby is null on this client when HitShovel is called."); + Plugin.logger.LogError("Previousplayerheldby is null on this client when HitShovel is called."); return; } previousPlayerHeldBy.activatingItem = false; @@ -134,7 +134,7 @@ namespace ScarletMansion.GamePatch.Items flag2 = true; } catch (Exception arg) { - Debug.Log($"Exception caught when hitting object with shovel from player #{previousPlayerHeldBy.playerClientId}: {arg}"); + Plugin.logger.LogError($"Exception caught when hitting object with shovel from player #{previousPlayerHeldBy.playerClientId}: {arg}"); } } } @@ -187,7 +187,10 @@ namespace ScarletMansion.GamePatch.Items player.activatingItem = true; yield return new WaitForSeconds(0.25f); - player.DamagePlayer(PluginConfig.Instance.maidKnifeSelfDamageValue, true, true, CauseOfDeath.Stabbing); + var dmg = PluginConfig.Instance.maidKnifeSelfDamageValue; + var realDmg = Mathf.Max(dmg, Mathf.RoundToInt(player.health * (dmg / 100f))); + player.DamagePlayer(realDmg, true, true, CauseOfDeath.Stabbing); + bloodParticle.Play(withChildren: true); RoundManager.PlayRandomClip(knifeAudio, hitSFX); @@ -220,7 +223,7 @@ namespace ScarletMansion.GamePatch.Items if (maidReference.TryGet(out var maid)){ parentMaid = maid; } else { - Debug.LogWarning($"Could not get parent maid for scarlet maid. Spooky I guess"); + Plugin.logger.LogWarning($"Could not get parent maid for scarlet maid. Spooky I guess"); } } diff --git a/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletSnowGlobe.cs b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletSnowGlobe.cs new file mode 100644 index 0000000..7f5ebda --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletSnowGlobe.cs @@ -0,0 +1,149 @@ +using GameNetcodeStuff; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace ScarletMansion.GamePatch.Items { + + public class ScarletSnowGlobe : GrabbableObject { + + [Header("Activation")] + public bool activated; + public GameObject mainLightGameObject; + public ParticleSystem snowParticleSystem; + public ParticleSystemRenderer snowRenderer; + public AudioSource musicAS; + + [Header("Extra")] + public Transform prefabPivot; + public ScanNodeProperties scanNode; + public AnimatorOverrideController SnowGlobeOverride; + public string SnowGlobeOriginalHoldClipName; + public AnimationClip SnowGlobeOverrideHoldClip; + + [System.Serializable] + public class SnowGlobeType { + public string name; + public GameObject prefab; + } + public SnowGlobeType[] types; + public SnowGlobeType currentType; + + public override void Start() { + base.Start(); + StartCoroutine(WaitForScrapValue()); + } + + public override void Update() { + base.Update(); + if (musicAS.isPlaying) { + var lerp = musicAS.time / musicAS.clip.length; + var spinRot = lerp * 5f * 360f; + //var pingRot = Mathf.PingPong(lerp * 95f / 8f, 11.875f / 32f) * (64f / 11.875f) - 1f; + + prefabPivot.localRotation = Quaternion.Euler(90f + spinRot, 90f, -90f); + } else { + prefabPivot.localRotation = Quaternion.Euler(90f, 90f, -90f); + } + } + + public IEnumerator WaitForScrapValue(){ + while (scrapValue == 0) yield return null; + + var index = scrapValue % types.Length; + currentType = types[index]; + + Instantiate(currentType.prefab, prefabPivot); + scanNode.headerText = $"{currentType.name} Snow Globe"; + } + + private PlayerAnimatorStateHelper GetAnimator(PlayerControllerB player) { + if (player != null && player.playerBodyAnimator != null) { + return PlayerAnimatorStateHelper.TryAddAnimator(player.actualClientId, player.playerBodyAnimator); + } + return null; + } + + public override void EquipItem() { + base.EquipItem(); + + var animator = GetAnimator(playerHeldBy); + if (animator != null) { + animator.SaveAnimatorStates(); + animator.SetAnimatorOverrideController(SnowGlobeOverride, SnowGlobeOriginalHoldClipName, SnowGlobeOverrideHoldClip); + Plugin.logger.LogInfo("Animator override set for player: " + playerHeldBy.playerBodyAnimator.gameObject.name); + } + + // Coming from pocketing since this is also called when using inventory + ToggleParticleRenderer(true); + } + + public override void PocketItem() { + var animator = GetAnimator(playerHeldBy); + if (animator != null) { + animator.SaveAnimatorStates(); + animator.RestoreOriginalAnimatorController(); + Plugin.logger.LogInfo("Animator restored for player: " + playerHeldBy.playerBodyAnimator.gameObject.name); + } + base.PocketItem(); + + // Disable Particles renderer + ToggleParticleRenderer(false); + } + + public override void DiscardItem() { + var animator = GetAnimator(playerHeldBy); + if (animator != null) { + animator.SaveAnimatorStates(); + animator.RestoreOriginalAnimatorController(); + Plugin.logger.LogInfo("Animator restored for player: " + playerHeldBy.playerBodyAnimator.gameObject.name); + } + + base.DiscardItem(); + } + + public override void ItemActivate(bool used, bool buttonDown = true) { + base.ItemActivate(used, buttonDown); + if (!activated) { + musicAS.volume = 0.2f; + StartCoroutine(ActivateSnowGlobeCoroutine()); + activated = true; + } + } + + public IEnumerator ActivateSnowGlobeCoroutine() { + yield return new WaitForEndOfFrame(); + yield return ToggleSnowGlobeCoroutine(true); + yield return new WaitUntil(() => !musicAS.isPlaying); + yield return ToggleSnowGlobeCoroutine(false); + yield return new WaitForSeconds(2f); + activated = false; + } + + IEnumerator ToggleSnowGlobeCoroutine(bool toggle, float delay = 0.2f) { + ToggleParticles(toggle); + ToggleMusic(toggle); + yield return new WaitForSeconds(delay); + mainLightGameObject.SetActive(toggle); + } + + void ToggleParticles(bool toggle) { + if (toggle) snowParticleSystem.Play(); + else snowParticleSystem.Stop(); + } + + void ToggleMusic(bool toggle) { + if (toggle) musicAS.Play(); + else musicAS.Stop(); + } + + void ToggleParticleRenderer(bool toggle) { + snowRenderer.enabled = toggle; + } + + } +} diff --git a/ScarletMansion/ScarletMansion/LoadingPatch/NetworkObjectListScriptableObject.cs b/ScarletMansion/ScarletMansion/LoadingPatch/NetworkObjectListScriptableObject.cs index 24fd309..3413b3a 100644 --- a/ScarletMansion/ScarletMansion/LoadingPatch/NetworkObjectListScriptableObject.cs +++ b/ScarletMansion/ScarletMansion/LoadingPatch/NetworkObjectListScriptableObject.cs @@ -21,6 +21,7 @@ namespace ScarletMansion { [Header("To Fix With InitPatch")] public List toFixGameObjects; public List items; + public List scrapItems; [Header("DunGen")] public TileSet mayorRegularTileset; @@ -32,7 +33,7 @@ namespace ScarletMansion { [Header("Main Prefabs")] public GameObject scarletNetworkManager; - + [Header("Mis References")] public Material ghostMaterial; public AudioClip sinkingAudioClip; diff --git a/ScarletMansion/ScarletMansion/Plugin.cs b/ScarletMansion/ScarletMansion/Plugin.cs index 313e8ef..ad8c931 100644 --- a/ScarletMansion/ScarletMansion/Plugin.cs +++ b/ScarletMansion/ScarletMansion/Plugin.cs @@ -32,7 +32,7 @@ namespace ScarletMansion { public class Plugin : BaseUnityPlugin { public const string modGUID = "ImoutoSama.ScarletMansion"; private const string modName = "Scarlet Mansion"; - private const string modVersion = "1.3.18"; + private const string modVersion = "1.3.21"; public readonly Harmony harmony = new Harmony(modGUID); @@ -92,6 +92,7 @@ namespace ScarletMansion { sdmLevelMatchProperties.planetNames.Add(new StringWithRarity("Dine", 300)); sdmLevelMatchProperties.planetNames.Add(new StringWithRarity("Rend", 300)); sdmLevelMatchProperties.planetNames.Add(new StringWithRarity("Titan", 69)); + sdmLevelMatchProperties.planetNames.Add(new StringWithRarity("Sanguine", 900)); var extendedContent = new List(); @@ -125,16 +126,9 @@ namespace ScarletMansion { PatchedContent.RegisterExtendedMod(extendedMod); - // - Assets.extendedMod = extendedMod; Assets.dungeonExtended = extendedDungeon; - foreach(var i in Assets.scrapItems){ - Items.RegisterScrap(i.item, 0, Levels.LevelTypes.None); - NetworkPrefabs.RegisterNetworkPrefab(i.item.spawnPrefab); - } - extendedDungeon.DungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch); DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction); } diff --git a/ScarletMansion/ScarletMansion/PluginConfig.cs b/ScarletMansion/ScarletMansion/PluginConfig.cs index 52c0a13..91132ca 100644 --- a/ScarletMansion/ScarletMansion/PluginConfig.cs +++ b/ScarletMansion/ScarletMansion/PluginConfig.cs @@ -244,6 +244,15 @@ namespace ScarletMansion { new AcceptableValueRange(0, 999) ); + public static ConfigEntryBundle snowGlobeWeight = new ConfigEntryBundle( + dungeonLootPrefix, + "Doll Snow Globe Weight", + 40, + "The doll snow globe's spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.", + null, + new AcceptableValueRange(0, 999) + ); + public static ConfigEntryBundle maidKnifeSelfDamage = new ConfigEntryBundle( dungeonLootPrefix, "Maid Knife Feed Damage", @@ -256,6 +265,7 @@ namespace ScarletMansion { public float lootMultiplierValue; public int crystalWeightValue; public int crystalBrokenWeightValue; + public int snowGlobeWeightValue; public int maidKnifeSelfDamageValue; // enemies diff --git a/ScarletMansion/ScarletMansion/ScarletMansion.csproj b/ScarletMansion/ScarletMansion/ScarletMansion.csproj index 7d22323..b7ab0c0 100644 --- a/ScarletMansion/ScarletMansion/ScarletMansion.csproj +++ b/ScarletMansion/ScarletMansion/ScarletMansion.csproj @@ -204,9 +204,11 @@ + +