2024-07-18 17:32:29 -07:00

493 lines
21 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;
namespace ScarletMansion.GamePatch {
public class InitPatch {
private static void CreateNetworkManager(StartOfRound __instance){
Plugin.logger.LogInfo($"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.LogInfo("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;
PlayerAnimatorStateHelper.ClearAnimators();
__instance.StartCoroutine(WaitForNetworkObject(__instance, CreateNetworkManager));
/*
foreach(var p in __instance.levels){
var totalScrap = p.spawnableScrap.Sum(s => s.rarity);
var totalEnemy = p.Enemies.Sum(s => s.rarity);
Plugin.logger.LogInfo($"{p.PlanetName}: S{totalScrap}, E{totalEnemy}");
}*/
// safety cleaning
DunGenPatch.Patch.Deactivate();
//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();
// DunGenAnalyis.Analysis(Assets.dungeon, __instance, RoundManager.Instance);
}
[HarmonyPatch(typeof(RoundManager), "Awake")]
[HarmonyPrefix]
public static void RoundManagerAwakePatch(ref RoundManager __instance) {
FixDungeonPrefabValues(__instance);
/*
foreach(var d in __instance.dungeonFlowTypes) {
Plugin.logger.LogInfo("--------");
Plugin.logger.LogInfo($"{d.name}: {d.Length.ToString()}");
Plugin.logger.LogInfo($"{d.BranchMode}: {d.BranchCount.ToString()}");
foreach(var l in d.Lines){
Plugin.logger.LogInfo($" {l.Position}, {l.Length}");
foreach(var a in l.DungeonArchetypes){
Plugin.logger.LogInfo($" {a.BranchCount.ToString()}, {a.BranchingDepth.ToString()}");
}
}
Plugin.logger.LogInfo("--------\n");
}
*/
}
[HarmonyPatch(typeof(RoundManager), "Start")]
[HarmonyPostfix]
public static void RoundManagerStartPatch(ref RoundManager __instance) {
MyOwnCoroutine.AddCoroutine(UpdateNetworkConfig(__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;
}
private static void FixParticleSystemMaterial(ParticleSystemRenderer toFixParticleSystem, ParticleSystemRenderer copyParticleSystem) {
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 {
// 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>();
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){
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, 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.LogInfo("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;
FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle);
FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle);
}
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;
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);
return;
}
}
public static void FixDungeonPrefabValues(RoundManager manager){
var networkmanager = GameObject.FindObjectOfType<NetworkManager>();
var prefabs = networkmanager.NetworkConfig.Prefabs.Prefabs;
var prefabFixList = Assets.networkObjectList.toFixGameObjects;
//foreach(var p in prefabs){
// var hasInteract = p.Prefab.GetComponentInChildren<InteractTrigger>();
// var interStr = hasInteract ? "YES" : "";
// Plugin.logger.LogInfo($"{p.Prefab.name}: {interStr}");
//}
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.LogInfo("Failed to setup vanilla fire exit. The 'Use SDM Fire Exits' config will be ignored");
Plugin.logger.LogError(e.ToString());
}
}
private static IEnumerator UpdateNetworkConfig(RoundManager roundManager){
while(PluginConfig.Synced == false){
yield return null;
}
/*
var list = ExtendedDungeonMapLoad.GetCustomMoons(PluginConfig.Instance.customMapsValue, PluginConfig.Instance.customMapDefaultWeightValue);
ExtendedDungeonMapLoad.ClearLastCustomMoonEntryList();
// overriding with dis
if (list.Count > 0){
Plugin.logger.LogInfo("Overring default snow moons. Loading with custom moon list");
ExtendedDungeonMapLoad.AddToExtendedDungeonFlow(list);
}
else {
Plugin.logger.LogInfo("Loading SDM to default snow moons");
Assets.dineEntry.SetRarity(PluginConfig.Instance.dungeonSnowWeightValue);
Assets.rendEntry.SetRarity(PluginConfig.Instance.dungeonSnowWeightValue);
Assets.titanEntry.SetRarity(PluginConfig.Instance.dungeonTitanWeightValue);
var dun = Assets.dungeonExtended;
Assets.dineEntry.Add(dun);
Assets.rendEntry.Add(dun);
Assets.titanEntry.Add(dun);
}
*/
Lights.ScarletLightCleanup.weights = new float[] {
PluginConfig.Instance.lightsSpawnZeroWeightValue,
PluginConfig.Instance.lightsSpawnOneWeightValue,
PluginConfig.Instance.lightsSpawnTwoWeightValue
//PluginConfig.Instance.lightsSpawnThreeWeightValue
};
/*
foreach(var item in Assets.items){
item.UpdateStringWithRarity();
}
// hack
foreach(var level in PatchedContent.ExtendedLevels) {
Plugin.logger.LogInfo($"{level.name}: {level.AuthorName}");
var hackCall = typeof(ItemManager).GetMethod("InjectCustomItemsIntoLevelViaDynamicRarity", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
hackCall.Invoke(null, new object[] { level, true } );
}
*/
//Assets.dungeonExtended.DynamicDungeonSizeMinMax = new Vector2(PluginConfig.Instance.dunGenMultiplierValue.min, PluginConfig.Instance.dunGenMultiplierValue.max);
Assets.dungeon.Length = PluginConfig.Instance.mainPathLengthValue.GetDungenIntRange();
var mayorTileSet = PluginConfig.Instance.disableBasementValue ? Assets.networkObjectList.mayorVanillaTileset : Assets.networkObjectList.mayorRegularTileset;
Assets.dungeon.Nodes[1].TileSets = new List<DunGen.TileSet>() { mayorTileSet };
DungeonFlow.GlobalPropSettings GetGlobalPropSetting(int id) {
foreach(var p in Assets.dungeon.GlobalProps){
if (p.ID == id ) return p;
}
return null;
}
GetGlobalPropSetting(254).Count = new DunGen.IntRange(
PluginConfig.Instance.paintingCountValue,
PluginConfig.Instance.paintingCountValue
);
Plugin.logger.LogInfo("Set networked config values");
}
public static void UpdateTileWeightDebug(string tile, float multiplier){
var tilesets = Assets.dungeon.GetUsedTileSets();
foreach(var s in tilesets){
foreach(var t in s.TileWeights.Weights){
var n = t.Value.name;
if (n.Contains(tile)) {
Plugin.logger.LogInfo($"{t.Value.name} weight being multiplied by {multiplier}");
t.MainPathWeight *= multiplier;
t.BranchPathWeight *= multiplier;
}
}
}
}
}
}