LadyAliceMargatroid 8e00725dd0 Fixed mimic compability
Updated to LLL 1.4.0
Fixed config syncing
2025-01-17 09:33:57 -08:00

400 lines
17 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using UnityEngine;
using DunGen.Graph;
using ScarletMansion.DunGenPatch;
using LethalLib.Modules;
using Unity.Netcode;
using LethalLevelLoader;
using static UnityEngine.GraphicsBuffer;
using ScarletMansion.GamePatch.Components;
using System.Security.Cryptography;
using ScarletMansion.GamePatch.Enemies;
using UnityEngine.UI;
using ScarletMansion.GamePatch.Items;
using DunGenPlus.Managers;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch {
public class InitPatch {
private static void CreateNetworkManager(StartOfRound __instance){
Plugin.logger.LogDebug($"IsServer: {__instance.IsServer}");
if (__instance.IsServer) {
if (ScarletNetworkManager.Instance == null) {
var prefab = Assets.networkObjectList.scarletNetworkManager;
var obj = GameObject.Instantiate(prefab);
var comp = obj.GetComponent<NetworkObject>();
comp.Spawn(false);
Plugin.logger.LogDebug("Created Scarlet Network Manager. We in");
} else {
Plugin.logger.LogWarning("Scarlet Network Manager already exists? Probably scary");
}
}
}
private static IEnumerator WaitForNetworkObject(StartOfRound __instance, Action<StartOfRound> action){
while(__instance.NetworkObject.IsSpawned == false) {
yield return null;
}
action(__instance);
}
[HarmonyPatch(typeof(StartOfRound), "Awake")]
[HarmonyPrefix]
public static void StartOfRound_Start(ref StartOfRound __instance) {
ScarletYukariTrigger.audioClipIndex = -1;
ScarletPlayerControllerB.InitializeScarletScripts();
DoorwayManager.onMainEntranceTeleportSpawnedEvent.ClearTemporaryActionList();
__instance.StartCoroutine(WaitForNetworkObject(__instance, CreateNetworkManager));
// safety cleaning
DunGenPatch.Patch.Deactivate(true);
//ScarletLightingManager.Clean();
FixMapReferences(__instance);
FixItemPrefabValues(__instance);
// fix audio clips
var statusEffectClips = __instance.statusEffectClips.ToList();
ScarletYukariTrigger.audioClipIndex = statusEffectClips.Count;
statusEffectClips.Add(Assets.networkObjectList.sinkingAudioClip);
__instance.statusEffectClips = statusEffectClips.ToArray();
}
[HarmonyPatch(typeof(RoundManager), "Awake")]
[HarmonyPrefix]
public static void RoundManagerAwakePatch(ref RoundManager __instance) {
FixDungeonPrefabValues(__instance);
}
[HarmonyPatch(typeof(RoundManager), "Start")]
[HarmonyPostfix]
public static void RoundManagerStartPatch(ref RoundManager __instance) {
MyOwnCoroutine.AddCoroutine(UpdateConfigMain());
MyOwnCoroutine.AddCoroutine(UpdateConfigDungeon(ConfigDungeonFoyer.Instance));
MyOwnCoroutine.AddCoroutine(UpdateConfigDungeon(ConfigDungeonBasement.Instance));
}
public static bool GameReadNullCheck(object target, string targetString, string error) {
if (target == null) {
Plugin.logger.LogError($"Error when find ({targetString}). {error}!");
return false;
}
return true;
}
public static void FixMapReferences(StartOfRound round){
try {
// use the names of assets instead of the itemName or enemyName
// to account for the korean patch and such
if (Assets.genericItemGroup == null || Assets.tabletopItemGroup == null){
var bottleItem = round.allItemsList.itemsList
.FirstOrDefault(i => i.name.ToLowerInvariant() == "bottlebin");
if (GameReadNullCheck(bottleItem, "bottlebin", "Generic/Tabletop scrap will not spawn")){
Assets.genericItemGroup = bottleItem.spawnPositionTypes[0];
Assets.tabletopItemGroup = bottleItem.spawnPositionTypes[1];
}
}
if (Assets.smallItemGroup == null){
var cupValue = round.allItemsList.itemsList
.FirstOrDefault(i => i.name.ToLowerInvariant() == "fancycup");
if (GameReadNullCheck(cupValue, "fancycup", "Small scrap will not spawn")){
Assets.smallItemGroup = cupValue.spawnPositionTypes[1];
}
}
var allEnemies = round.levels
.SelectMany(lev => lev.Enemies);
var knight = Assets.knight;
if (knight.enemyType == null){
var springItem = allEnemies.FirstOrDefault(e => e.enemyType.name.ToLowerInvariant() == "springman");
if (GameReadNullCheck(springItem, "springman", "Knight enemy will not spawn")) {
var type = ScriptableObject.Instantiate(springItem.enemyType);
type.name = "Knight";
type.enemyPrefab = knight.enemy;
type.enemyName = "Knight";
knight.enemyType = type;
knight.enemy.GetComponentInChildren<EnemyAI>().enemyType = type;
LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, knight.terminalNode, knight.terminalKeyword);
}
}
var maid = Assets.maid;
if (maid.enemyType == null){
var butlerItem = allEnemies.FirstOrDefault(e => e.enemyType.name.ToLowerInvariant() == "butler");
if (GameReadNullCheck(butlerItem, "butler", "Maid enemy will not spawn")) {
var type = ScriptableObject.Instantiate(butlerItem.enemyType);
type.name = "Maid";
type.enemyPrefab = maid.enemy;
type.enemyName = "Maid";
type.pushPlayerForce *= 0.25f;
maid.enemyType = type;
var maidScript = maid.enemy.GetComponentInChildren<MaidVariant>();
maidScript.enemyType = type;
var butlerPrefab = butlerItem.enemyType.enemyPrefab;
var butlerScript = butlerPrefab.GetComponent<ButlerEnemyAI>();
Utility.FixParticleSystemMaterialAndChildren(maidScript.stabBloodParticle, butlerScript.stabBloodParticle);
LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, maid.terminalNode, maid.terminalKeyword);
}
}
} catch (Exception e) {
Plugin.logger.LogError("Error when reading base game's item list. Pretty big error I would say");
Plugin.logger.LogError(e);
return;
}
}
public static void FixItemPrefabValues(StartOfRound round){
try {
var itemsList = round.allItemsList.itemsList;
void QuickItemFix(Item itemToFix, Item itemReference){
if (itemToFix.itemIcon == null) itemToFix.itemIcon = itemReference.itemIcon;
if (itemToFix.grabSFX == null) itemToFix.grabSFX = itemReference.grabSFX;
if (itemToFix.dropSFX == null) itemToFix.dropSFX = itemReference.dropSFX;
if (itemToFix.pocketSFX == null) 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);
Assets.scrapItems[5].item.itemIcon = magClass.itemIcon;
}
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, paintClass);
}
void UpdateFlashlight(Assets.Flashlight flashlight, string search, int fixIndex){
var flashItem = round.allItemsList.itemsList
.FirstOrDefault(i => i.name.ToLowerInvariant() == search);
if (GameReadNullCheck(flashItem, search, "CRYSTAL FLASHLIGHT WILL FAIL")){
var prefab = Assets.networkObjectList.toFixGameObjects[fixIndex];
var item = ScriptableObject.Instantiate(flashItem);
item.name = flashlight.assetName;
item.itemName = flashlight.displayName;
item.saveItemVariable = true;
item.batteryUsage *= 1.5f;
item.spawnPrefab = prefab;
item.weight += 0.02f;
item.isScrap = true;
flashlight.item = item;
flashlight.lethalVanillaItem = flashItem;
var scarletItemComp = prefab.GetComponentInChildren<FlashlightItem>();
var refItemComp = flashItem.spawnPrefab.GetComponentInChildren<FlashlightItem>();
scarletItemComp.bulbDark = refItemComp.bulbDark;
scarletItemComp.bulbLight = refItemComp.bulbLight;
scarletItemComp.flashlightClips = refItemComp.flashlightClips;
scarletItemComp.flashlightFlicker = refItemComp.flashlightFlicker;
scarletItemComp.outOfBatteriesClip = refItemComp.outOfBatteriesClip;
scarletItemComp.itemProperties = item;
var scarletAudioComp = scarletItemComp.flashlightAudio;
var refAudioComp = refItemComp.flashlightAudio;
scarletAudioComp.SetCustomCurve(AudioSourceCurveType.CustomRolloff, refAudioComp.GetCustomCurve(AudioSourceCurveType.CustomRolloff));
var scarletLightComp = scarletItemComp.flashlightBulb;
var refLightComp = refItemComp.flashlightBulb;
scarletLightComp.cookie = refLightComp.cookie;
void CopyAndPasteMeshFilterAndRenderer(MeshRenderer meshScarlet, MeshRenderer meshReference){
meshScarlet.sharedMaterials = meshReference.sharedMaterials;
var scarletMeshComp = meshScarlet.GetComponent<MeshFilter>();
var refMeshComp = meshReference.GetComponent<MeshFilter>();
scarletMeshComp.sharedMesh = refMeshComp.sharedMesh;
}
CopyAndPasteMeshFilterAndRenderer(scarletItemComp.flashlightMesh, refItemComp.flashlightMesh);
var scarletTie = scarletItemComp.flashlightMesh.transform.Find("Tie");
var refTie = refItemComp.flashlightMesh.transform.Find("Tie");
if (scarletTie && refTie) {
var scarletTieMesh = scarletTie.GetComponent<MeshRenderer>();
var refTieMesh = refTie.GetComponent<MeshRenderer>();
if (scarletTieMesh && refTieMesh) {
Plugin.logger.LogDebug("Fixed tie of bb flashlight");
CopyAndPasteMeshFilterAndRenderer(scarletTieMesh, refTieMesh);
}
}
LethalLib.Modules.Items.RegisterItem(item);
}
}
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;
Utility.FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle);
Utility.FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle);
}
var shovelItem = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "shovel");
if (GameReadNullCheck(shovelItem, "shovel", "Item will have missing image and sound assets")){
QuickItemFix(Assets.scrapItems[5].item, shovelItem);
var shovelScript = shovelItem.spawnPrefab.GetComponent<Shovel>();
var goheiScript = Assets.scrapItems[5].item.spawnPrefab.GetComponent<ScarletGohei>();
goheiScript.reelUp = shovelScript.reelUp;
goheiScript.swing = shovelScript.swing;
}
if (Assets.flashlight.item == null) {
UpdateFlashlight(Assets.flashlight, "proflashlight", 2);
}
if (Assets.flashlightBB.item == null) {
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<GrabbableObject>().itemProperties;
QuickItemFix(scarletKey, keyItem);
var keyPrefab = keyItem.spawnPrefab;
scarletPrefab.GetComponent<MeshFilter>().sharedMesh = keyPrefab.GetComponent<MeshFilter>().sharedMesh;
var mats = keyPrefab.GetComponent<MeshRenderer>().sharedMaterials;
mats = mats.Select(m => {
var n = new Material(m);
n.color = Color.red;
return n;
}).ToArray();
scarletPrefab.GetComponent<MeshRenderer>().sharedMaterials = mats;
}
} catch (Exception e) {
Plugin.logger.LogError("Error when reading base game's item list. Pretty big error I would say");
Plugin.logger.LogError(e);
return;
}
}
public static void FixDungeonPrefabValues(RoundManager manager){
var networkmanager = GameObject.FindObjectOfType<NetworkManager>();
var prefabs = networkmanager.NetworkConfig.Prefabs.Prefabs;
var prefabFixList = Assets.networkObjectList.toFixGameObjects;
var steeldoor = prefabs.FirstOrDefault(p => p.Prefab.name.ToLowerInvariant() == "fancydoormapmodel");
if (GameReadNullCheck(steeldoor, "FancyDoorMapModel", "SDM doors will have missing icons and sounds")){
var interact = steeldoor.Prefab.GetComponentInChildren<InteractTrigger>();
var animateTrigger = steeldoor.Prefab.GetComponentInChildren<AnimatedObjectTrigger>();
Assets.hoverIcon = interact.hoverIcon;
var boolFalse = animateTrigger.boolFalseAudios;
var boolTrue = animateTrigger.boolTrueAudios;
var secondary = animateTrigger.secondaryAudios;
FixDoorway(prefabFixList[0]);
FixDoorway(prefabFixList[1]);
void FixDoorway(GameObject g){
var animate = g.GetComponentInChildren<AnimatedObjectTrigger>();
animate.boolFalseAudios = boolFalse;
animate.boolTrueAudios = boolTrue;
animate.secondaryAudios = secondary;
}
}
var mine = prefabs.FirstOrDefault(p => p.Prefab.name.ToLowerInvariant() == "landmine");
var turret = prefabs.FirstOrDefault(p => p.Prefab.name.ToLowerInvariant() == "turretcontainer");
var spiketrap = prefabs.FirstOrDefault(p => p.Prefab.name.ToLowerInvariant() == "spikerooftraphazard");
if (GameReadNullCheck(mine, "LandMine", "Map Object Hazards will not spawn") && GameReadNullCheck(turret, "TurretContainer", "Map Object Hazards will not spawn") && GameReadNullCheck(spiketrap, "SpikeRoofTrapHazard", "Map Object Hazards will not spawn")){
Assets.dungeonMapHazardFound = true;
Assets.dungeonMinesMapHazard = mine.Prefab;
Assets.dungeonTurretMapHazard = turret.Prefab;
Assets.dungeonSpikeTrapMapHazard = spiketrap.Prefab;
}
try {
var flow = manager.dungeonFlowTypes.Select(d => d.dungeonFlow).FirstOrDefault(d => d.name == "Level1Flow");
var tiles = flow.Nodes[1].TileSets[0].TileWeights.Weights;
var doorways = tiles.SelectMany(t => t.Value.GetComponentsInChildren<DunGen.Doorway>());
var blockers = doorways.SelectMany(d => d.BlockerPrefabWeights).Select(e => e.GameObject);
var globalprops = blockers.SelectMany(b => b.GetComponentsInChildren<DunGen.GlobalProp>());
var fireProp = globalprops.FirstOrDefault(f => f.PropGroupID == 1231);
prefabFixList[4].GetComponent<FixValues.FixFireExit>().Setup(fireProp.gameObject);
} catch (Exception e){
Plugin.logger.LogDebug("Failed to setup vanilla fire exit. The 'Use SDM Fire Exits' config will be ignored");
Plugin.logger.LogError(e.ToString());
}
}
private static IEnumerator UpdateConfigMain(){
while(ConfigMain.Synced == false){
yield return null;
}
foreach(var scrap in Assets.scrapItems){
scrap.UpdateItemValue();
}
Plugin.logger.LogDebug("Config Main updated");
}
private static IEnumerator UpdateConfigDungeon<T>(ConfigDungeon<T> instance) where T: ConfigDungeon<T>{
while(instance.SyncedLocal == false){
yield return null;
}
var defaultKeyPrefab = typeof(DungeonLoader).GetField("defaultKeyPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null) as GameObject;
instance.dungeon.Length = instance.mainPathLengthValue.GetDungenIntRange();
instance.dungeonExtended.OverrideKeyPrefab = Assets.key.GetConfigItemEntry().enabled ? Assets.key.item.spawnPrefab : defaultKeyPrefab;
Plugin.logger.LogDebug($"Config {typeof(T)} updated");
}
}
}