Added maid's knife (dropped from maid)

This commit is contained in:
LadyAliceMargatroid 2024-06-29 04:59:14 -07:00
parent 05ea3dc4c3
commit 69b2bc24cf
8 changed files with 306 additions and 20 deletions

View File

@ -139,9 +139,11 @@ namespace ScarletMansion {
public static Dictionary<string, Func<int>> itemRarityTable = new Dictionary<string, Func<int>>(){
{ "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;

View File

@ -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<Type, float> EnemyDoorDamagePerSecond = new Dictionary<Type, float>(){
@ -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){

View File

@ -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<NetworkObject>().Spawn();
newKnife.GetComponent<ScarletKnife>().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;

View File

@ -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<ParticleSystemRenderer>(), copyParticleSystem.GetComponent<ParticleSystemRenderer>());
}
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<ParticleSystemRenderer>();
var child1 = copyParticleSystem.transform.GetChild(i).GetComponent<ParticleSystemRenderer>();
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<ButlerEnemyAI>();
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<ParticleSystemRenderer>();
ps1.material = butlerBloodStabParticle.GetComponent<ParticleSystemRenderer>().material;
var ps2 = ps1.transform.GetChild(0).GetComponent<ParticleSystemRenderer>();
ps2.material = butlerBloodParticle.GetComponent<ParticleSystemRenderer>().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<ScarletKnife>();
var knifePrefab = knifeItem.spawnPrefab;
var knifeScript = knifePrefab.GetComponent<KnifeItem>();
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);
}

View File

@ -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<RaycastHit> objectsHitByKnifeList = new List<RaycastHit>();
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<IHittable>(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<RoundManager>().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<PlayerControllerB>(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<MaidVariant>(out var maid)){
parentMaid = maid;
} else {
Debug.LogWarning($"Could not get parent maid for scarlet maid. Spooky I guess");
}
}
}
}

View File

@ -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);

View File

@ -244,6 +244,21 @@ namespace ScarletMansion {
new AcceptableValueRange<int>(0, 999)
);
public static ConfigEntryBundle<int> maidKnifeSelfDamage = new ConfigEntryBundle<int>(
dungeonLootPrefix,
"Maid Knife Feed Damage",
50,
"The amount of self damage taken by feeding the maid's knife. Can kill.",
null,
new AcceptableValueRange<int>(0, 100)
);
public float lootMultiplierValue;
public int crystalWeightValue;
public int crystalBrokenWeightValue;
public int maidKnifeSelfDamageValue;
// enemies
public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>(
dungeonEnemiesPrefix,
"Map Hazards Multiplier",
@ -298,10 +313,6 @@ namespace ScarletMansion {
new AcceptableValueRange<int>(0, 999)
);
public float lootMultiplierValue;
public int crystalWeightValue;
public int crystalBrokenWeightValue;
public float mapHazardsMultiplierValue;
public int minIndoorEnemySpawnCountValue;

View File

@ -205,6 +205,7 @@
<Compile Include="GamePatch\Items\FlandreCrystal.cs" />
<Compile Include="GamePatch\Items\IScarletItem.cs" />
<Compile Include="GamePatch\Items\ScarletFlashlight.cs" />
<Compile Include="GamePatch\Items\ScarletKnife.cs" />
<Compile Include="GamePatch\Items\ScarletPainting.cs" />
<Compile Include="GamePatch\JesterAIPatch.cs" />
<Compile Include="GamePatch\LoadAssetsIntoLevelPatch.cs" />