From 69b2bc24cf344cccf64b024cef86cc869e6b5976 Mon Sep 17 00:00:00 2001 From: LadyAliceMargatroid Date: Sat, 29 Jun 2024 04:59:14 -0700 Subject: [PATCH] Added maid's knife (dropped from maid) --- ScarletMansion/ScarletMansion/Assets.cs | 4 +- .../GamePatch/Components/ScarletDoorLock.cs | 5 + .../GamePatch/Enemies/MaidVariant.cs | 16 +- .../ScarletMansion/GamePatch/InitPatch.cs | 51 +++- .../GamePatch/Items/ScarletKnife.cs | 228 ++++++++++++++++++ ScarletMansion/ScarletMansion/Plugin.cs | 2 +- ScarletMansion/ScarletMansion/PluginConfig.cs | 19 +- .../ScarletMansion/ScarletMansion.csproj | 1 + 8 files changed, 306 insertions(+), 20 deletions(-) create mode 100644 ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs diff --git a/ScarletMansion/ScarletMansion/Assets.cs b/ScarletMansion/ScarletMansion/Assets.cs index a574ca7..22964bf 100644 --- a/ScarletMansion/ScarletMansion/Assets.cs +++ b/ScarletMansion/ScarletMansion/Assets.cs @@ -139,9 +139,11 @@ namespace ScarletMansion { public static Dictionary> itemRarityTable = new Dictionary>(){ { "Deco. crystal", () => PluginConfig.Instance.crystalWeightValue }, { "Shattered deco. crystal", () => PluginConfig.Instance.crystalBrokenWeightValue }, - { "Demonic painting", () => 0 } + { "Demonic painting", () => 0 }, + { "Maid's knife", () => 0 } }; + public static Flashlight flashlight; public static Flashlight flashlightBB; diff --git a/ScarletMansion/ScarletMansion/GamePatch/Components/ScarletDoorLock.cs b/ScarletMansion/ScarletMansion/GamePatch/Components/ScarletDoorLock.cs index 3333f82..94b6bc7 100644 --- a/ScarletMansion/ScarletMansion/GamePatch/Components/ScarletDoorLock.cs +++ b/ScarletMansion/ScarletMansion/GamePatch/Components/ScarletDoorLock.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; using Unity.Netcode; +using ScarletMansion.GamePatch.Enemies; namespace ScarletMansion.GamePatch.Components { public class ScarletDoorLock : DoorLock { @@ -84,6 +85,8 @@ namespace ScarletMansion.GamePatch.Components { { typeof(SandSpiderAI), (e) => e.currentBehaviourStateIndex <= 1 }, { typeof(SpringManAI), (e) => e.currentBehaviourStateIndex == 0 }, { typeof(KnightVariant), (e) => e.currentBehaviourStateIndex == 0 }, + { typeof(ButlerEnemyAI), (e) => e.currentBehaviourStateIndex <= 1 }, + { typeof(MaidVariant), (e) => e.currentBehaviourStateIndex <= 1 } }; static readonly Dictionary EnemyDoorDamagePerSecond = new Dictionary(){ @@ -100,6 +103,8 @@ namespace ScarletMansion.GamePatch.Components { { typeof(SandSpiderAI), 25f }, { typeof(SpringManAI), 12.5f }, { typeof(KnightVariant), 12.5f }, + { typeof(ButlerEnemyAI), 50f }, + { typeof(MaidVariant), 50f } }; public bool IsInOpenDoorNormallyState(EnemyAI enemy){ diff --git a/ScarletMansion/ScarletMansion/GamePatch/Enemies/MaidVariant.cs b/ScarletMansion/ScarletMansion/GamePatch/Enemies/MaidVariant.cs index f4896e6..b29c274 100644 --- a/ScarletMansion/ScarletMansion/GamePatch/Enemies/MaidVariant.cs +++ b/ScarletMansion/ScarletMansion/GamePatch/Enemies/MaidVariant.cs @@ -1,6 +1,7 @@ //using System; using System.Collections; using GameNetcodeStuff; +using ScarletMansion.GamePatch.Items; using Unity.Netcode; using UnityEngine; using UnityEngine.Animations.Rigging; @@ -180,6 +181,10 @@ namespace ScarletMansion.GamePatch.Enemies { // Token: 0x04000110 RID: 272 public GameObject knifePrefab; + public GameObject knifeRender; + + public Transform knifeRenderTransform; + public AudioSource chaseMusicStart; // Token: 0x04000111 RID: 273 @@ -256,6 +261,14 @@ namespace ScarletMansion.GamePatch.Enemies { if (!startedButlerDeathAnimation) { startedButlerDeathAnimation = true; this.creatureAnimator.SetTrigger("Dead"); + + knifeRender.SetActive(false); + + if (IsServer) { + var newKnife = Object.Instantiate(knifePrefab, base.transform.position + Vector3.up * 0.5f, Quaternion.identity, RoundManager.Instance.spawnedScrapContainer); + newKnife.GetComponent().Spawn(); + newKnife.GetComponent().LinkKnifeToMaidServerRpc(this); + } } } @@ -277,7 +290,6 @@ namespace ScarletMansion.GamePatch.Enemies { base.HitEnemy(force, playerWhoHit, playHitSFX, hitID); enemyHP -= force; - if (hitID == 5) enemyHP -= 100; if (playerWhoHit != null) berserkModeTimer = 8f; if (enemyHP <= 0 && IsOwner) { @@ -530,7 +542,7 @@ namespace ScarletMansion.GamePatch.Enemies { } // and it looks like our target is alone, kill - if (currentBehaviourStateIndex == 1 && CheckLineOfSightForPlayer(120f, 50, 2) == targetPlayer && playersInVicinity < 2 && timeSpentWaitingForPlayer > 12f) { + if (currentBehaviourStateIndex == 1 && CheckLineOfSightForPlayer(120f, 50, 2) == targetPlayer && playersInVicinity < 2 && timeSpentWaitingForPlayer > 20f) { SwitchToBehaviourStateOnLocalClient(2); SwitchOwnershipAndSetToStateServerRpc(2, targetPlayer.actualClientId, -1f); return; diff --git a/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs b/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs index 815ebdd..377a9a1 100644 --- a/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs +++ b/ScarletMansion/ScarletMansion/GamePatch/InitPatch.cs @@ -16,6 +16,7 @@ using ScarletMansion.GamePatch.Components; using System.Security.Cryptography; using ScarletMansion.GamePatch.Enemies; using UnityEngine.UI; +using ScarletMansion.GamePatch.Items; namespace ScarletMansion.GamePatch { @@ -108,6 +109,25 @@ namespace ScarletMansion.GamePatch { return true; } + private static void FixParticleSystemMaterial(ParticleSystemRenderer toFixParticleSystem, ParticleSystemRenderer copyParticleSystem) { + Debug.Log(copyParticleSystem.name); + toFixParticleSystem.sharedMaterial = copyParticleSystem.sharedMaterial; + } + + private static void FixParticleSystemMaterial(ParticleSystem toFixParticleSystem, ParticleSystem copyParticleSystem) { + FixParticleSystemMaterial(toFixParticleSystem.GetComponent(), copyParticleSystem.GetComponent()); + } + + private static void FixParticleSystemMaterialAndChildren(ParticleSystem toFixParticleSystem, ParticleSystem copyParticleSystem) { + FixParticleSystemMaterial(toFixParticleSystem, copyParticleSystem); + + for(var i = 0; i < toFixParticleSystem.transform.childCount && i < copyParticleSystem.transform.childCount; ++i) { + var child0 = toFixParticleSystem.transform.GetChild(i).GetComponent(); + var child1 = copyParticleSystem.transform.GetChild(i).GetComponent(); + FixParticleSystemMaterial(child0, child1); + } + } + public static void FixMapReferences(StartOfRound round){ try { @@ -168,18 +188,7 @@ namespace ScarletMansion.GamePatch { var butlerPrefab = butlerItem.enemyType.enemyPrefab; var butlerScript = butlerPrefab.GetComponent(); - var butlerBloodStabParticle = Utility.FindChildRecurvisely(butlerPrefab.transform, "BloodStabParticle"); - var butlerBloodParticle = Utility.FindChildRecurvisely(butlerPrefab.transform, "BloodParticle"); - - if (GameReadNullCheck(butlerBloodStabParticle, "BloodStabParticle", "Messed up errors will probably happen with blood splats") && GameReadNullCheck(butlerBloodParticle, "BloodParticle", "Messed up errors will probably happen with blood splats")){ - var ps1 = maidScript.stabBloodParticle.GetComponent(); - ps1.material = butlerBloodStabParticle.GetComponent().material; - - var ps2 = ps1.transform.GetChild(0).GetComponent(); - ps2.material = butlerBloodParticle.GetComponent().material; - } - - maidScript.knifePrefab = butlerScript.knifePrefab; + FixParticleSystemMaterialAndChildren(maidScript.stabBloodParticle, butlerScript.stabBloodParticle); LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, maid.terminalNode, maid.terminalKeyword); } @@ -276,6 +285,24 @@ namespace ScarletMansion.GamePatch { } } + var knifeItem = round.allItemsList.itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "knife"); + if (GameReadNullCheck(knifeItem, "knife", "Item will have missing image and sound assets")){ + var scarletKnife = Assets.scrapItems[3].item; + QuickItemFix(scarletKnife, knifeItem); + + var scarletPrefab = scarletKnife.spawnPrefab; + var scarletScript = scarletPrefab.GetComponent(); + + var knifePrefab = knifeItem.spawnPrefab; + var knifeScript = knifePrefab.GetComponent(); + + scarletScript.hitSFX = knifeScript.hitSFX; + scarletScript.swingSFX = knifeScript.swingSFX; + + FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle); + FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle); + } + if (Assets.flashlight.item == null) { UpdateFlashlight(Assets.flashlight, "proflashlight", 2); } diff --git a/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs new file mode 100644 index 0000000..d94c093 --- /dev/null +++ b/ScarletMansion/ScarletMansion/GamePatch/Items/ScarletKnife.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using GameNetcodeStuff; +using OdinSerializer; +using ScarletMansion.GamePatch.Enemies; +using Unity.Netcode; +using UnityEngine; + +namespace ScarletMansion.GamePatch.Items +{ + public class ScarletKnife : GrabbableObject { + + public AudioSource knifeAudio; + + private List objectsHitByKnifeList = new List(); + + public PlayerControllerB previousPlayerHeldBy; + + private RaycastHit[] objectsHitByKnife; + + public int knifeHitForce; + + public AudioClip[] hitSFX; + + public AudioClip[] swingSFX; + + private int knifeMask = 11012424; + + private float timeAtLastDamageDealt; + + public ParticleSystem bloodParticle; + + [Header("Scarlet")] + public MaidVariant parentMaid; + public bool buffed; + + [Header("Buff Animations")] + public ParticleSystem buffedParticleSystem; + public MeshRenderer knifeRenderer; + + public override void EquipItem() { + base.EquipItem(); + playerHeldBy.equippedUsableItemQE = true; + parentMaid = null; + } + + public override void LateUpdate(){ + base.LateUpdate(); + if (parentMaid) { + transform.position = parentMaid.knifeRenderTransform.position; + transform.rotation = parentMaid.knifeRenderTransform.rotation; + } + } + + public override void ItemActivate(bool used, bool buttonDown = true) { + + if (playerHeldBy != null) { + if (playerHeldBy.activatingItem) return; + + RoundManager.PlayRandomClip(knifeAudio, swingSFX); + + previousPlayerHeldBy = playerHeldBy; + if (playerHeldBy.IsOwner) playerHeldBy.playerBodyAnimator.SetTrigger("UseHeldItem1"); + } + + if (IsOwner) { + HitKnife(); + } + } + + public override void ItemInteractLeftRight(bool right) { + base.ItemInteractLeftRight(right); + if (playerHeldBy != null) { + if (!right && !buffed && playerHeldBy.IsOwner && !playerHeldBy.activatingItem && !StartOfRound.Instance.inShipPhase) { + BuffShovelServerRpc(playerHeldBy); + } + } + } + + public void HitKnife(bool cancel = false) { + if (previousPlayerHeldBy == null) { + Debug.LogError("Previousplayerheldby is null on this client when HitShovel is called."); + return; + } + previousPlayerHeldBy.activatingItem = false; + bool flag = false; + bool flag2 = false; + int num = -1; + if (!cancel) { + previousPlayerHeldBy.twoHanded = false; + objectsHitByKnife = Physics.SphereCastAll(previousPlayerHeldBy.gameplayCamera.transform.position + previousPlayerHeldBy.gameplayCamera.transform.right * 0.1f, 0.3f, previousPlayerHeldBy.gameplayCamera.transform.forward, 0.75f, knifeMask, QueryTriggerInteraction.Collide); + objectsHitByKnifeList = objectsHitByKnife.OrderBy((RaycastHit x) => x.distance).ToList(); + for (int i = 0; i < objectsHitByKnifeList.Count; i++) { + if (objectsHitByKnifeList[i].transform.gameObject.layer == 8 || objectsHitByKnifeList[i].transform.gameObject.layer == 11) { + flag = true; + string text = objectsHitByKnifeList[i].collider.gameObject.tag; + for (int j = 0; j < StartOfRound.Instance.footstepSurfaces.Length; j++) { + if (StartOfRound.Instance.footstepSurfaces[j].surfaceTag == text) { + num = j; + break; + } + } + } + else { + if (!objectsHitByKnifeList[i].transform.TryGetComponent(out var component) + || objectsHitByKnifeList[i].transform == previousPlayerHeldBy.transform + || (!(objectsHitByKnifeList[i].point == Vector3.zero) + && Physics.Linecast(previousPlayerHeldBy.gameplayCamera.transform.position, objectsHitByKnifeList[i].point, out var _, StartOfRound.Instance.collidersAndRoomMaskAndDefault))) { + continue; + } + + flag = true; + Vector3 forward = previousPlayerHeldBy.gameplayCamera.transform.forward; + try { + if (Time.realtimeSinceStartup - timeAtLastDamageDealt > 0.43f) { + timeAtLastDamageDealt = Time.realtimeSinceStartup; + + // buff + if (buffed) { + // prevent the player from one-shotting their friends + var target = component is PlayerControllerB ? previousPlayerHeldBy as IHittable : component; + target.Hit(50, forward, previousPlayerHeldBy, playHitSFX: true); + UnBuffShovelServerRpc(); + } + // normal knife + else { + component.Hit(knifeHitForce, forward, previousPlayerHeldBy, playHitSFX: true); + } + + bloodParticle.Play(withChildren: true); + } + flag2 = true; + } + catch (Exception arg) { + Debug.Log($"Exception caught when hitting object with shovel from player #{previousPlayerHeldBy.playerClientId}: {arg}"); + } + } + } + } + + if (flag) { + RoundManager.PlayRandomClip(knifeAudio, hitSFX); + UnityEngine.Object.FindObjectOfType().PlayAudibleNoise(base.transform.position, 17f, 0.8f); + if (!flag2 && num != -1) { + knifeAudio.PlayOneShot(StartOfRound.Instance.footstepSurfaces[num].hitSurfaceSFX); + WalkieTalkie.TransmitOneShotAudio(knifeAudio, StartOfRound.Instance.footstepSurfaces[num].hitSurfaceSFX); + } + + HitShovelServerRpc(num); + } + } + + [ServerRpc] + public void HitShovelServerRpc(int hitSurfaceID) { + HitShovelClientRpc(hitSurfaceID); + } + + [ClientRpc] + public void HitShovelClientRpc(int hitSurfaceID) { + if(!base.IsOwner) { + RoundManager.PlayRandomClip(knifeAudio, hitSFX); + if (hitSurfaceID != -1) HitSurfaceWithKnife(hitSurfaceID); + } + } + + private void HitSurfaceWithKnife(int hitSurfaceID) { + knifeAudio.PlayOneShot(StartOfRound.Instance.footstepSurfaces[hitSurfaceID].hitSurfaceSFX); + WalkieTalkie.TransmitOneShotAudio(knifeAudio, StartOfRound.Instance.footstepSurfaces[hitSurfaceID].hitSurfaceSFX); + } + + [ServerRpc(RequireOwnership = false)] + public void BuffShovelServerRpc(NetworkBehaviourReference playerReference){ + BuffShovelClientRpc(playerReference); + } + + [ClientRpc] + public void BuffShovelClientRpc(NetworkBehaviourReference playerReference){ + if (playerReference.TryGet(out var player)){ + StartCoroutine(BuffCoroutine(player)); + } + } + + public IEnumerator BuffCoroutine(PlayerControllerB player){ + player.playerBodyAnimator.SetTrigger("UseHeldItem1"); + player.activatingItem = true; + yield return new WaitForSeconds(0.25f); + + player.DamagePlayer(PluginConfig.Instance.maidKnifeSelfDamageValue, true, true, CauseOfDeath.Stabbing); + bloodParticle.Play(withChildren: true); + RoundManager.PlayRandomClip(knifeAudio, hitSFX); + + buffed = true; + buffedParticleSystem.Play(); + knifeRenderer.material.color = Color.red; + + player.activatingItem = false; + } + + [ServerRpc(RequireOwnership = false)] + public void UnBuffShovelServerRpc(){ + UnBuffShovelClientRpc(); + } + + [ClientRpc] + public void UnBuffShovelClientRpc(){ + buffed = false; + buffedParticleSystem.Stop(); + knifeRenderer.material.color = Color.white; + } + + [ServerRpc] + public void LinkKnifeToMaidServerRpc(NetworkBehaviourReference maidReference){ + LinkKnifeToMaidClientRpc(maidReference); + } + + [ClientRpc] + public void LinkKnifeToMaidClientRpc(NetworkBehaviourReference maidReference) { + if (maidReference.TryGet(out var maid)){ + parentMaid = maid; + } else { + Debug.LogWarning($"Could not get parent maid for scarlet maid. Spooky I guess"); + } + } + + } +} diff --git a/ScarletMansion/ScarletMansion/Plugin.cs b/ScarletMansion/ScarletMansion/Plugin.cs index b80c657..313e8ef 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.17"; + private const string modVersion = "1.3.18"; public readonly Harmony harmony = new Harmony(modGUID); diff --git a/ScarletMansion/ScarletMansion/PluginConfig.cs b/ScarletMansion/ScarletMansion/PluginConfig.cs index 04d09a5..52c0a13 100644 --- a/ScarletMansion/ScarletMansion/PluginConfig.cs +++ b/ScarletMansion/ScarletMansion/PluginConfig.cs @@ -244,6 +244,21 @@ namespace ScarletMansion { new AcceptableValueRange(0, 999) ); + public static ConfigEntryBundle maidKnifeSelfDamage = new ConfigEntryBundle( + dungeonLootPrefix, + "Maid Knife Feed Damage", + 50, + "The amount of self damage taken by feeding the maid's knife. Can kill.", + null, + new AcceptableValueRange(0, 100) + ); + + public float lootMultiplierValue; + public int crystalWeightValue; + public int crystalBrokenWeightValue; + public int maidKnifeSelfDamageValue; + + // enemies public static ConfigEntryBundle mapHazardsMultiplier = new ConfigEntryBundle( dungeonEnemiesPrefix, "Map Hazards Multiplier", @@ -298,10 +313,6 @@ namespace ScarletMansion { new AcceptableValueRange(0, 999) ); - public float lootMultiplierValue; - public int crystalWeightValue; - public int crystalBrokenWeightValue; - public float mapHazardsMultiplierValue; public int minIndoorEnemySpawnCountValue; diff --git a/ScarletMansion/ScarletMansion/ScarletMansion.csproj b/ScarletMansion/ScarletMansion/ScarletMansion.csproj index b35c019..7d22323 100644 --- a/ScarletMansion/ScarletMansion/ScarletMansion.csproj +++ b/ScarletMansion/ScarletMansion/ScarletMansion.csproj @@ -205,6 +205,7 @@ +