Moved dungen code to DunGenPlus

Added critical damage as a mechanic
Maid knife feed deals critical damage, then kills on second time
Added concept of treasure room with kitchen
Frames now only start at the player when not being looked
New attempt at snowglobe animations, it broke though
Added concept of indoor weathers
LethalConfig compability now changes the color of configs that my presets touch
Added jukebox
Changed modGUID
Removed AC compability
Falling into void deals critical damage and teleports, then kills on second time
Maid spawns revenant ghost on death
This commit is contained in:
LadyAliceMargatroid 2024-08-02 08:56:09 -07:00
parent faa391c309
commit 056cac8df1
63 changed files with 976 additions and 2061 deletions

View File

@ -13,6 +13,7 @@ using LethalLib.Modules;
using LethalLevelLoader; using LethalLevelLoader;
using ScarletMansion.GamePatch.Items; using ScarletMansion.GamePatch.Items;
using static ScarletMansion.Assets; using static ScarletMansion.Assets;
using DunGenPlus;
namespace ScarletMansion { namespace ScarletMansion {
public static class Assets { public static class Assets {
@ -26,6 +27,7 @@ namespace ScarletMansion {
// main assets // main assets
public static AssetBundle MainAssetBundle = null; public static AssetBundle MainAssetBundle = null;
public static DungeonFlow dungeon; public static DungeonFlow dungeon;
public static DunGenExtender dunGenExtender;
public static NetworkObjectListScriptableObject networkObjectList; public static NetworkObjectListScriptableObject networkObjectList;
public static AudioClip entranceAudioClip; public static AudioClip entranceAudioClip;
@ -33,11 +35,6 @@ namespace ScarletMansion {
public static ExtendedMod extendedMod; public static ExtendedMod extendedMod;
public static ExtendedDungeonFlow dungeonExtended; public static ExtendedDungeonFlow dungeonExtended;
//public static ExtendedDungeonMapLoad.CustomMoonEntry rendEntry;
//public static ExtendedDungeonMapLoad.CustomMoonEntry dineEntry;
//public static ExtendedDungeonMapLoad.CustomMoonEntry titanEntry;
//public static List<ExtendedDungeonMapLoad.CustomMoonEntry> customMoonEntryList;
// enemy values // enemy values
public class Enemy { public class Enemy {
@ -187,6 +184,7 @@ namespace ScarletMansion {
dungeon = Load<DungeonFlow>("SDMLevel"); dungeon = Load<DungeonFlow>("SDMLevel");
networkObjectList = Load<NetworkObjectListScriptableObject>("SDMList"); networkObjectList = Load<NetworkObjectListScriptableObject>("SDMList");
dunGenExtender = Load<DunGenExtender>("DunGenExtender");
entranceAudioClip = Load<AudioClip>("entrance_ogg"); entranceAudioClip = Load<AudioClip>("entrance_ogg");
knight = new Enemy( knight = new Enemy(
@ -232,6 +230,11 @@ namespace ScarletMansion {
globalItems.Add(flashlight); globalItems.Add(flashlight);
globalItems.Add(flashlightBB); globalItems.Add(flashlightBB);
foreach(var e in networkObjectList.enemies) {
Enemies.RegisterEnemy(e, 0, Levels.LevelTypes.None, null, null);
NetworkPrefabs.RegisterNetworkPrefab(e.enemyPrefab);
}
onAssetsLoadEvent.Call(); onAssetsLoadEvent.Call();
} }

View File

@ -1,235 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using DunGen.Graph;
using UnityEngine;
namespace ScarletMansion {
public static class AnalysisUtilities {
public class Average {
public List<float> values;
public float total;
public Average() {
values = new List<float>();
}
public void Add(float value) {
values.Add(value);
total += value;
}
public float GetAverage(){
if (values.Count == 0) return 0f;
return total / values.Count;
}
public float GetStdDev(){
if (values.Count == 0) return 0f;
var avg = GetAverage();
var x = values.Sum(i => {
var y = i - avg;
return y * y;
});
return Mathf.Sqrt(x / values.Count);
}
public override string ToString() {
//var avg = GetAverage();
//var dev = GetStdDev();
var strList = new List<string>();
values.Sort();
var sectionCount = Mathf.Clamp(values.Count, 1, 10);
var sectionDistance = values.Count / sectionCount;
for(var i = 0; i * sectionDistance < values.Count; ++i){
var avg = GetAverage(values.Skip(i * sectionDistance).Take(sectionDistance));
strList.Add($"[{i}]{avg}");
}
/*
var leftIndex = values.Count / 4;
var rightIndex = values.Count - leftIndex;
var left = values[leftIndex];
var right = values[rightIndex];
return $"Avg[{avg:0.00}]({left:0.00} - {right:0.00}) Dev[{dev:0.00}]";
*/
var strListFormatted = string.Join(", ", strList);
return $"({strListFormatted})";
}
public float GetAverage(IEnumerable<float> items){
return items.Sum() / items.Count();
}
}
}
public static class DunGenAnalyis {
public class Average {
public float totalVolume;
public float totalWeight;
public float average => totalWeight > 0f ? (totalVolume / totalWeight) : 0f;
public Dictionary<string, bool> baseDictionary = new Dictionary<string, bool>(){
{ "SM_MayorEntrance_FINAL_32x24 Tile", false }
};
public void AddToAverage(float volume, float weight, string name){
var isBaseUsed = false;
var isBaseTile = baseDictionary.TryGetValue(name, out isBaseUsed);
if (!isBaseTile) {
totalVolume += volume;
totalWeight += weight;
}
}
public void AddToBase(float volume, string name){
var isBaseUsed = false;
var isBaseTile = baseDictionary.TryGetValue(name, out isBaseUsed);
totalWeight = 1f;
if (isBaseTile && !isBaseUsed) {
totalVolume += volume;
baseDictionary[name] = true;
}
}
public override string ToString() {
return $"{totalVolume / totalWeight}";
}
}
public static void Analysis(DungeonFlow flow, StartOfRound startofround, RoundManager roundmanager){
foreach(var l in startofround.levels){
var mult = l.factorySizeMultiplier * roundmanager.mapSizeMultiplier;
Plugin.logger.LogInfo($"{l.PlanetName}: {mult}");
var length = flow.Length;
var minLength = Mathf.RoundToInt(length.Min * mult);
var maxLength = Mathf.RoundToInt(length.Max * mult);
var minTotal = 0f;
for(var i = 0; i <= minLength; ++i){
minTotal += GetAverage(flow, minLength, i, 0);
minTotal += GetAverage(flow, minLength, i, 1);
minTotal += GetAverage(flow, minLength, i, 2);
//Plugin.logger.LogInfo($"new: {minTotal}");
}
var maxTotal = 0f;
for(var i = 0; i <= maxLength; ++i){
maxTotal += GetAverage(flow, maxLength, i, 0);
maxTotal += GetAverage(flow, maxLength, i, 1);
maxTotal += GetAverage(flow, maxLength, i, 2);
//Plugin.logger.LogInfo($"new: {maxTotal}");
}
var maxSizeBounds = DunGenPatch.Patch.GetDungeonBounds(mult);
var maxSizeTotal = GetVolume(maxSizeBounds) * (3f / 5f);
var minPer = (minTotal / maxSizeTotal).ToString("0.00");
var maxPer = (maxTotal / maxSizeTotal).ToString("0.00");
Plugin.logger.LogInfo($"Min size required: {minTotal} - {maxTotal}");
Plugin.logger.LogInfo($"All space: {maxSizeTotal}");
Plugin.logger.LogInfo($"Taken space: {minPer} - {maxPer}");
Plugin.logger.LogInfo("");
}
}
public static float GetAverage(DungeonFlow flow, int length, int index, int lineIndex){
var average = new Average();
var depth = (float)index / length;
if (depth <= 1f){
// start
if (depth == 0f){
// count the start once
if (lineIndex == 0){
foreach(var t in flow.Nodes[0].TileSets){
ModifyAverage(t, average, 0f);
}
}
// skip
else {
}
}
// end
else if (depth == 1f) {
foreach(var t in flow.Nodes[1].TileSets){
ModifyAverage(t, average, depth);
}
}
// entrance tile
else if (index == 1){
// count the mayor once
if (lineIndex == 0) {
foreach(var a in flow.Lines[0].DungeonArchetypes){
foreach(var t in a.TileSets){
AddBase(t, average);
}
}
}
// skip
else {
}
}
// in between
else {
var line = flow.GetLineAtDepth(depth);
foreach(var a in line.DungeonArchetypes){
foreach(var t in a.TileSets){
ModifyAverage(t, average, depth);
}
}
}
}
//Plugin.logger.LogInfo($"b{lineIndex}i{index}l{length}: {average.average}");
return average.average;
}
public static void AddBase(TileSet tileset, Average baseVol) {
foreach(var t in tileset.TileWeights.Weights){
var gobj = t.Value;
var tile = gobj.GetComponent<Tile>();
var volume = GetVolume(tile.TileBoundsOverride);
baseVol.AddToBase(volume, t.Value.name);
}
}
public static void ModifyAverage(TileSet tileset, Average averageVol, float depth){
foreach(var t in tileset.TileWeights.Weights){
var gobj = t.Value;
var tile = gobj.GetComponent<Tile>();
var volume = GetVolume(tile.TileBoundsOverride);
var weight = t.MainPathWeight * t.DepthWeightScale.Evaluate(depth);
averageVol.AddToAverage(volume * weight, weight, t.Value.name);
}
}
public static float GetVolume(Bounds bounds){
var size = bounds.size;
return size.x * size.y * size.z;
}
}
}

View File

@ -1,60 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion {
public class DoorwayConnectionSisterChain : MonoBehaviour {
public Doorway[] chain;
public bool loop = true;
[ContextMenu("Create Chain")]
public void CreateChain(){
if (chain == null) return;
if (chain.Length <= 1) return;
for(var i = 0; i < chain.Length; ++i){
var current = chain[i];
var list = new List<Doorway>();
// add prev
if (i > 0 || loop) {
var prev = chain[((i - 1) + chain.Length) % chain.Length];
list.Add(prev);
}
if (i < chain.Length - 1 || loop){
var next = chain[((i + 1) + chain.Length) % chain.Length];
list.Add(next);
}
var script = current.GetComponent<DoorwayConnectionSisterRuleInfo>();
if (script == null) {
script = current.gameObject.AddComponent<DoorwayConnectionSisterRuleInfo>();
}
script.sisters = list.ToArray();
}
}
public void OnDrawGizmosSelected(){
if (chain == null) return;
if (chain.Length <= 1) return;
for(var i = 0; i < chain.Length; ++i){
if (!loop && i == chain.Length - 1) continue;
var current = chain[i];
var next = chain[(i + 1) % chain.Length];
var color = new Color((float)i / chain.Length, 1f, 1f);
Gizmos.color = color;
Gizmos.DrawLine(current.transform.position, next.transform.position);
}
}
}
}

View File

@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion {
public class DoorwayConnectionSisterRuleInfo : MonoBehaviour {
private Doorway _self;
public Doorway self {
get {
if (_self == null) {
_self = GetComponent<Doorway>();
}
return _self;
}
}
public Doorway[] sisters;
public void OnDrawGizmosSelected(){
var center = transform.position;
if (sisters == null) return;
foreach(var sis in sisters){
var target = sis.transform.position;
var comp = sis.GetComponent<DoorwayConnectionSisterRuleInfo>();
if (self == null) {
Gizmos.color = Color.magenta;
} else if (comp == null || comp.sisters == null){
Gizmos.color = Color.yellow;
} else if (!comp.sisters.Contains(self)) {
Gizmos.color = Color.red;
} else {
Gizmos.color = Color.green;
}
Gizmos.DrawLine(center, target);
}
}
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion {
public class MainRoomDoorwayGroups : MonoBehaviour {
public List<Doorway> groupA;
public List<Doorway> groupB;
public List<Doorway> groupBasement;
public List<Doorway> GrabDoorwayGroup(Doorway target){
if (groupA.Contains(target)) return groupA;
else if (groupB.Contains(target)) return groupB;
else if (groupBasement.Contains(target)) return groupBasement;
return null;
}
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
namespace ScarletMansion {
public class RemoveConnectorIfConnectedDoorwayBasic : MonoBehaviour, IDungeonCompleteReceiver {
public void OnDungeonComplete(Dungeon dungeon){
var d = GetComponent<Doorway>();
if (d == null || d.ConnectedDoorway == null) return;
if (d.ConnectedDoorway.DoorPrefabPriority == 0) {
d.ConnectorSceneObjects[0].SetActive(false);
}
}
}
}

View File

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
namespace ScarletMansion.DunGenPatch.Components {
public class RemoveGameObjectsBasedOnCBSelected : MonoBehaviour, IDungeonCompleteReceiver {
public Doorway doorway;
public List<GameObject> targets;
public GameObject cb;
void Reset(){
doorway = GetComponent<Doorway>();
}
public void OnDungeonComplete(Dungeon dungeon) {
var result = false;
foreach(Transform t in transform){
if (t.name.Contains(cb.name)) {
result = true;
break;
}
}
if (result) {
foreach(var t in targets) t.SetActive(false);
}
}
}
}

View File

@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
namespace ScarletMansion.DunGenPatch.Components {
public class RemoveGameObjectsBasedOnConnectedDoorway : MonoBehaviour, IDungeonCompleteReceiver {
public enum Operation { Equal, NotEqual, LessThan, GreaterThan }
public Doorway doorway;
public List<GameObject> targets;
public int doorwayPriority;
public Operation operation = Operation.Equal;
void Reset(){
doorway = GetComponent<Doorway>();
}
public void OnDungeonComplete(Dungeon dungeon) {
if (doorway.connectedDoorway == null) return;
var result = GetOperation().Invoke(doorway.connectedDoorway);
if (result) {
foreach(var t in targets) t.SetActive(false);
}
}
public Func<Doorway, bool> GetOperation(){
switch(operation){
case Operation.Equal:
return EqualOperation;
case Operation.NotEqual:
return NotEqualOperation;
case Operation.LessThan:
return LessThanOperation;
case Operation.GreaterThan:
return GreaterThanOperation;
}
return null;
}
public bool EqualOperation(Doorway other){
return other.DoorPrefabPriority == doorwayPriority;
}
public bool NotEqualOperation(Doorway other){
return other.DoorPrefabPriority != doorwayPriority;
}
public bool LessThanOperation(Doorway other){
return other.DoorPrefabPriority < doorwayPriority;
}
public bool GreaterThanOperation(Doorway other){
return other.DoorPrefabPriority > doorwayPriority;
}
}
}

View File

@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
namespace ScarletMansion.DunGenPatch.Components {
public class SwitchConnectorBlockerBasedOnCBSelected : MonoBehaviour, IDungeonCompleteReceiver{
public enum Action { SwitchToConnector, SwitchToBlocker };
public Doorway doorway;
public GameObject cb;
public Action switchAction;
void Reset(){
doorway = GetComponent<Doorway>();
}
public void OnDungeonComplete(Dungeon dungeon) {
var result = false;
foreach(Transform t in transform){
if (t.name.Contains(cb.name)) {
result = true;
break;
}
}
if (result) {
var connectorStatus = switchAction == Action.SwitchToConnector;
var blockerStatus = switchAction == Action.SwitchToBlocker;
foreach(var c in doorway.ConnectorSceneObjects) c.SetActive(connectorStatus);
foreach(var b in doorway.BlockerSceneObjects) b.SetActive(blockerStatus);
}
}
}
}

View File

@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using GameNetcodeStuff;
using System.Reflection;
using System.Reflection.Emit;
using BepInEx.Logging;
using UnityEngine;
using DunGen;
namespace ScarletMansion.DunGenPatch {
public class DoorwayConnectionPatch {
[HarmonyTranspiler]
[HarmonyPatch(typeof(DungeonProxy), "ConnectOverlappingDoorways")]
public static IEnumerable<CodeInstruction> ConnectOverlappingDoorwaysPatch(IEnumerable<CodeInstruction> instructions){
var callFunction = typeof(DunGen.Graph.DungeonFlow).GetMethod("CanDoorwaysConnect", BindingFlags.Instance | BindingFlags.Public);
var sequence = new InstructionSequence("doorway connect", false);
sequence.AddBasic(OpCodes.Callvirt, callFunction);
sequence.AddBasic(OpCodes.Brfalse);
foreach(var instruction in instructions){
if (sequence.VerifyStage(instruction)){
var method = typeof(DoorwayConnectionSisterRule).GetMethod("CanDoorwaysConnect", BindingFlags.Static | BindingFlags.Public);
var getTileProxy = typeof(DoorwayProxy).GetMethod("get_TileProxy", BindingFlags.Instance | BindingFlags.Public);
yield return new CodeInstruction(OpCodes.Ldloc_2);
yield return new CodeInstruction(OpCodes.Callvirt, getTileProxy);
yield return new CodeInstruction(OpCodes.Ldloc_S, 4);
yield return new CodeInstruction(OpCodes.Callvirt, getTileProxy);
yield return new CodeInstruction(OpCodes.Ldloc_2);
yield return new CodeInstruction(OpCodes.Ldloc_S, 4);
yield return new CodeInstruction(OpCodes.Call, method);
yield return instruction;
continue;
}
yield return instruction;
}
sequence.ReportComplete();
}
[HarmonyPatch(typeof(DungeonProxy), "ConnectOverlappingDoorways")]
[HarmonyPrefix]
public static void ConnectOverlappingDoorwaysPrePatch(ref DungeonProxy __instance){
var enumerable = __instance.AllTiles.SelectMany(t => t.Doorways);
DoorwayConnectionSisterRule.UpdateCache(enumerable);
}
}
}

View File

@ -1,91 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
using DunGen.Tags;
namespace ScarletMansion.DunGenPatch {
public static class DoorwayConnectionSisterRule {
public static bool active => Patch.active;
public class Data {
public DoorwayConnectionSisterRuleInfo info;
public List<DoorwayProxy> proxies;
}
public static Dictionary<Doorway, Data> doorwayDictionary;
public static Dictionary<DoorwayProxy, Data> doorwayProxyDictionary;
public static void UpdateCache(IEnumerable<DoorwayProxy> list){
if (!active) return;
Plugin.logger.LogInfo("Updating cache from DungeonProxy");
doorwayDictionary = new Dictionary<Doorway, Data>();
doorwayProxyDictionary = new Dictionary<DoorwayProxy, Data>();
foreach(var a in list){
var doorway = a.DoorwayComponent;
if (doorwayDictionary.TryGetValue(doorway, out var data)){
data.proxies.Add(a);
doorwayProxyDictionary.Add(a, data);
} else {
var proxies = new List<DoorwayProxy>();
proxies.Add(a);
var item = new Data {
info = doorway.GetComponent<DoorwayConnectionSisterRuleInfo>(),
proxies = proxies
};
doorwayProxyDictionary.Add(a, item);
doorwayDictionary.Add(a.DoorwayComponent, item);
}
}
}
public static bool CanDoorwaysConnect(bool result, TileProxy tileA, TileProxy tileB, DoorwayProxy doorwayA, DoorwayProxy doorwayB){
if (!result) return false;
if (!active) return true;
var infoA = doorwayProxyDictionary[doorwayA].info;
var infoB = doorwayProxyDictionary[doorwayB].info;
// deny if any sister doorway is already in use
// cause it feels like dumb otherwise
if (CheckIfSisterActive(infoA, tileB)){
return false;
}
if (CheckIfSisterActive(infoB, tileA)){
return false;
}
// allow like normal
return true;
}
public static bool CheckIfSisterActive(DoorwayConnectionSisterRuleInfo info, TileProxy targetTile){
if (info == null || info.sisters == null) return false;
foreach(var sis in info.sisters){
var proxies = doorwayDictionary[sis].proxies;
foreach(var proxy in proxies){
var result = proxy.ConnectedDoorway != null && proxy.ConnectedDoorway.TileProxy == targetTile;
if (result) return true;
}
}
return false;
}
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.DunGenPatch.Doorways {
public abstract class DCleanBase : MonoBehaviour {
public DoorwayCleanup parent;
void Reset(){
parent = GetComponent<DoorwayCleanup>();
}
public abstract void Cleanup();
}
}

View File

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.DunGenPatch.Doorways {
public class DCleanConnectorBlockerBasedOnSelected : DCleanBase {
public enum Action { SwitchToConnector, SwitchToBlocker };
public Action switchAction;
public GameObject target;
public override void Cleanup() {
var result = false;
foreach(Transform t in parent.doorway.transform){
if (t.gameObject.activeSelf && t.name.Contains(target.name)) {
result = true;
break;
}
}
if (result) {
parent.SwitchConnectorBlocker(switchAction == Action.SwitchToConnector);
}
}
}
}

View File

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion.DunGenPatch.Doorways {
public abstract class DCleanDoorwayCompare : DCleanBase {
public enum Operation { Equal, NotEqual, LessThan, GreaterThan }
public Func<Doorway, int, bool> GetOperation(Operation operation){
switch(operation){
case Operation.Equal:
return EqualOperation;
case Operation.NotEqual:
return NotEqualOperation;
case Operation.LessThan:
return LessThanOperation;
case Operation.GreaterThan:
return GreaterThanOperation;
}
return null;
}
public bool EqualOperation(Doorway other, int doorwayPriority){
return other.DoorPrefabPriority == doorwayPriority;
}
public bool NotEqualOperation(Doorway other, int doorwayPriority){
return other.DoorPrefabPriority != doorwayPriority;
}
public bool LessThanOperation(Doorway other, int doorwayPriority){
return other.DoorPrefabPriority < doorwayPriority;
}
public bool GreaterThanOperation(Doorway other, int doorwayPriority){
return other.DoorPrefabPriority > doorwayPriority;
}
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion.DunGenPatch.Doorways {
public class DCleanRemoveDoorwayBasedOnConnectedDoor : DCleanDoorwayCompare {
public int doorwayPriority;
public Operation operation = Operation.Equal;
public override void Cleanup() {
var doorway = parent.doorway;
if (doorway.connectedDoorway == null) return;
var result = GetOperation(operation).Invoke(doorway.connectedDoorway, doorwayPriority);
if (result) {
parent.SwitchDoorwayGameObject(false);
}
}
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.DunGenPatch.Doorways {
public class DCleanRemoveDoorwayBasedOnSelected : DCleanBase {
public GameObject target;
public override void Cleanup() {
var result = false;
foreach(Transform t in parent.doorway.transform){
if (t.gameObject.activeSelf && t.name.Contains(target.name)) {
result = true;
break;
}
}
if (result) {
parent.SwitchDoorwayGameObject(false);
}
}
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion.DunGenPatch.Doorways {
public class DCleanRemoveGameObjectsBasedOnConnectedDoor : DCleanDoorwayCompare {
public List<GameObject> targets;
public int doorwayPriority;
public Operation operation = Operation.Equal;
public override void Cleanup() {
var doorway = parent.doorway;
if (doorway.connectedDoorway == null) return;
var result = GetOperation(operation).Invoke(doorway.connectedDoorway, doorwayPriority);
if (result) {
foreach(var t in targets) t.SetActive(false);
}
}
}
}

View File

@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
using ScarletMansion.GamePatch.Managers;
namespace ScarletMansion.DunGenPatch.Doorways {
public class DoorwayCleanup : MonoBehaviour, IDungeonCompleteReceiver {
[Header("Doorway References")]
public Doorway doorway;
public List<GameObject> connectors;
public List<GameObject> blockers;
public GameObject doorwayGameObject;
[Header("Cleanup References")]
public DCleanBase[] cleanupList;
[Header("Overrides")]
public bool overrideConnector;
public bool overrideNoDoorway;
[ContextMenu("Populate")]
public void Populate(){
cleanupList = GetComponents<DCleanBase>();
}
void Reset(){
doorway = GetComponent<Doorway>();
}
public void OnDungeonComplete(Dungeon dungeon) {
// fix for items spawning on doorways
SetBlockers(true);
DoorwayManager.Instance.AddDoorwayCleanup(this);
}
public void Cleanup(){
// start up like in original
SwitchConnectorBlocker(doorway.ConnectedDoorway != null);
foreach(var c in cleanupList)
c.Cleanup();
if (overrideNoDoorway) SwitchDoorwayGameObject(false);
// clean up like in original
foreach(var c in connectors){
if (!c.activeSelf) UnityEngine.Object.DestroyImmediate(c, false);
}
foreach(var b in blockers){
if (!b.activeSelf) UnityEngine.Object.DestroyImmediate(b, false);
}
}
public void SetBlockers(bool state){
foreach(var b in blockers) b.SetActive(state);
}
public void SwitchConnectorBlocker(bool isConnector){
if (overrideConnector) isConnector = true;
foreach(var c in connectors) c.SetActive(isConnector);
foreach(var b in blockers) b.SetActive(!isConnector);
}
public void SwitchDoorwayGameObject(bool isActive){
doorwayGameObject.SetActive(isActive);
}
}
}

View File

@ -1,314 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
using System.Security;
using System.Security.Permissions;
using DunGen.Graph;
[assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )]
namespace ScarletMansion.DunGenPatch {
public static class GeneratePath {
public static bool active => Patch.active;
//public static List<List<TileProxy>> allMainPathTiles;
public static int analTestCount = 0;
public static int analTestCountMax = 100;
public static int analAltFailCount = 0;
public static void RandomizeLineArchetypes(DungeonGenerator gen, bool randomizeMainPath){
var mainPathString = randomizeMainPath ? "main path" : "branching path";
//Plugin.logger.LogInfo($"Randomizing archetypes of {mainPathString}");
var arch = Assets.networkObjectList.archetypes;
var i = 0;
var j = 0;
var tilesetsUsed = new Dictionary<TileSet, int>();
foreach(var t in Assets.networkObjectList.tilesets){
tilesetsUsed.Add(t, 0);
}
while(j < 3 && i < arch.Count) {
var a = arch[i];
PluginConfig.Instance.branchPathSectionOneValue.UpdateValues(a);
var tiles = randomizeMainPath ? a.TileSets : a.BranchCapTileSets;
RandomizeArchetype(gen, tiles, tilesetsUsed);
++j;
++i;
}
j = 0;
while(j < 4 && i < arch.Count) {
var a = arch[i];
PluginConfig.Instance.branchPathSectionTwoValue.UpdateValues(a);
var tiles = randomizeMainPath ? a.TileSets : a.BranchCapTileSets;
RandomizeArchetype(gen, tiles, tilesetsUsed);
++j;
++i;
}
j = 0;
while(j < 3 && i < arch.Count) {
var a = arch[i];
PluginConfig.Instance.branchPathSectionThreeValue.UpdateValues(a);
var tiles = randomizeMainPath ? a.TileSets : a.BranchCapTileSets;
RandomizeArchetype(gen, tiles, tilesetsUsed);
++j;
++i;
}
/*
for(var k = 0; k < arch.Count; ++k){
Plugin.logger.LogInfo($"a{k}");
var a = arch[k];
var tiles = randomizeMainPath ? a.TileSets : a.BranchCapTileSets;
foreach(var t in tiles){
Plugin.logger.LogInfo($" {t.name}");
}
}
*/
}
public static void RandomizeArchetype(DungeonGenerator gen, List<TileSet> targetTileSet, Dictionary<TileSet, int> tilesetsUsed){
// get 3 random
var newTiles = Assets.networkObjectList.tilesets.OrderBy(t => tilesetsUsed[t] + gen.RandomStream.NextDouble()).Take(3);
var i = targetTileSet.Count - 1;
foreach(var n in newTiles){
targetTileSet[i] = n;
--i;
tilesetsUsed[n] += 1;
}
}
public static IEnumerator GenerateAlternativeMainPaths(DungeonGenerator gen) {
// the amount of extra alt. paths
var altV = PluginConfig.Instance.mainPathCountValue;
if (PluginConfig.Instance.disableBasementValue) {
altV = Mathf.Min(altV, 2);
}
var altCount = altV - 1;
if (!active || altCount == 0){
Patch.callAlternative = false;
yield return gen.Wait(gen.GenerateBranchPaths());
Patch.callAlternative = true;
yield break;
}
gen.ChangeStatus(GenerationStatus.MainPath);
var allMainPathTiles = new List<List<TileProxy>>();
allMainPathTiles.Add(gen.proxyDungeon.MainPathTiles.ToList());
// main tile is the true main room and not the fake room
// this MUST have multiple doorways as you can imainge
var mainTile = gen.proxyDungeon.MainPathTiles[1];
var mainTileDoorwayGroups = mainTile.Prefab.GetComponentInChildren<MainRoomDoorwayGroups>();
var fakeTileProxy = new DoorwayProxy(mainTile, 0, mainTile.doorways[0].DoorwayComponent, Vector3.zero, Quaternion.identity);
// what this is for needs some explaining
// the alternate main paths are really just branches that can't end early
// since they are just branches, they are affected by the branch prune setting
// as such, the final node of an alternate main path CANNOT be a node that can be pruned
// luckily, the last node is my Nodes section has tiles that won't be pruned
// so i'm just using that so the final node cannot be a target for pruning
for (var b = 0; b < altCount; ++b) {
RandomizeLineArchetypes(gen, true);
var previousTile = mainTile;
var targetLength = Mathf.RoundToInt(gen.DungeonFlow.Length.GetRandom(gen.RandomStream) * gen.LengthMultiplier);
var archetypes = new List<DungeonArchetype>(targetLength);
var newMainPathTiles = new List<TileProxy>();
newMainPathTiles.Add(mainTile);
var nodes = gen.DungeonFlow.Nodes.Skip(2);
var nodesVisited = new List<GraphNode>(nodes.Count());
// most of this code is a mix of the GenerateMainPath()
// and GenerateBranch() code
for(var t = 1; t < targetLength; ++t){
var lineDepthRatio = Mathf.Clamp01((float)t / (targetLength - 1));
var lineAtDepth = gen.DungeonFlow.GetLineAtDepth(lineDepthRatio);
if (lineAtDepth == null){
yield return gen.Wait(gen.InnerGenerate(true));
yield break;
}
if (lineAtDepth != gen.previousLineSegment){
gen.currentArchetype = lineAtDepth.GetRandomArchetype(gen.RandomStream, archetypes);
gen.previousLineSegment = lineAtDepth;
}
// terrible solution but FUCK it
// and yet it worked
// this is how my last node cannot be a target of pruning
GraphNode graphNode = null;
DungeonArchetype archetype = null;
foreach(var g in nodes) {
if (lineDepthRatio >= g.Position && !nodesVisited.Contains(g)) {
graphNode = g;
nodesVisited.Add(g);
break;
}
}
List<TileSet> useableTileSets;
if (graphNode != null) {
archetype = graphNode.NodeType == NodeType.Normal ? Assets.networkObjectList.hallwayEntranceArchetype : null;
useableTileSets = graphNode.TileSets;
} else {
archetype = gen.currentArchetype;
useableTileSets = archetype.TileSets;
}
if (t == 1){
// go to each doorway
foreach(var doorway in mainTile.doorways){
// if null or another fake, ignore
// we want the real ones
var con = doorway.ConnectedDoorway;
if (con == fakeTileProxy || con == null) continue;
// grab its corresponding group
var groups = mainTileDoorwayGroups.GrabDoorwayGroup(doorway.DoorwayComponent);
if (groups == null) continue;
// go through the list again, but this time we adding fakes to trick the AddTile()
foreach(var again in mainTile.doorways){
// if null AND its part of the group
// add the fake
if (again.ConnectedDoorway == null && groups.Contains(again.DoorwayComponent)){
again.ConnectedDoorway = fakeTileProxy;
}
}
}
}
var tileProxy = gen.AddTile(previousTile, useableTileSets, lineDepthRatio, archetype, TilePlacementResult.None);
if (tileProxy == null) {
if (!Patch.startAnalysis) Plugin.logger.LogInfo($"Alt. main branch gen failed at {b}:{lineDepthRatio}");
analAltFailCount++;
yield return gen.Wait(gen.InnerGenerate(true));
yield break;
}
// this is debug code from when a mysterious bug arised
// the culprit? the final node of the alternate main path being pruned of course
if (lineDepthRatio >= 1f){
if (!Patch.startAnalysis) Plugin.logger.LogInfo($"Alt. main branch at {b} ended with {tileProxy.PrefabTile.name}");
}
tileProxy.Placement.BranchDepth = t;
tileProxy.Placement.NormalizedBranchDepth = lineDepthRatio;
if (graphNode != null) {
tileProxy.Placement.GraphNode = graphNode;
tileProxy.Placement.GraphLine = null;
} else {
tileProxy.Placement.GraphNode = null;
tileProxy.Placement.GraphLine = lineAtDepth;
}
previousTile = tileProxy;
newMainPathTiles.Add(tileProxy);
if (gen.ShouldSkipFrame(true)) yield return gen.GetRoomPause();
}
allMainPathTiles.Add(newMainPathTiles);
}
// okay lets fix the fakes
foreach(var doorway in mainTile.doorways){
if (doorway.ConnectedDoorway == fakeTileProxy) {
doorway.ConnectedDoorway = null;
}
}
Patch.callAlternative = false;
if (!Patch.startAnalysis) Plugin.logger.LogInfo($"Created {altCount} alt. paths, creating branches now");
gen.ChangeStatus(GenerationStatus.Branching);
// this is major trickery and it works still
for(var b = 0; b < altCount + 1; ++b){
if (!Patch.startAnalysis) Plugin.logger.LogInfo($"Branch {b}");
RandomizeLineArchetypes(gen, false);
gen.proxyDungeon.MainPathTiles = allMainPathTiles[b];
/*
foreach (var t in gen.proxyDungeon.MainPathTiles){
Plugin.logger.LogInfo(t.Prefab.name);
Plugin.logger.LogInfo(t.Placement.Archetype);
}
*/
yield return gen.Wait(gen.GenerateBranchPaths());
}
Patch.callAlternative = true;
gen.proxyDungeon.MainPathTiles = allMainPathTiles[0];
if (AnalysisUpdate(gen)) yield return gen.Wait(gen.InnerGenerate(true));
}
public static DungeonArchetype ModifyMainBranchNodeArchetype(DungeonArchetype archetype, GraphNode node){
if (!Patch.active) return archetype;
if (node.NodeType == NodeType.Normal) return Assets.networkObjectList.hallwayEntranceArchetype;
return archetype;
}
public static bool AnalysisUpdate(DungeonGenerator gen){
if (Patch.startAnalysis && analTestCount <= analTestCountMax) {
var t = Patch.stopwatch.ElapsedMilliseconds;
//Patch.tileCalculations.Add(t);
//Plugin.logger.LogInfo($"C{TestCount}, T({Patch.tileCalculations.ToString()}), S{AltFailCount}, R{gen.retryCount}, D({Patch.doorwayPairs.ToString()})");
var allTiles = gen.proxyDungeon.AllTiles;
var firstTilePos = allTiles[0].Placement.position;
for(var i = 1; i < allTiles.Count; ++i){
var tile = allTiles[i];
var tilePrefab = tile.Prefab;
var prefabBasePos = tilePrefab.transform.position;
var scrap = tilePrefab.GetComponentsInChildren<RandomScrapSpawn>();
foreach(var s in scrap){
var offPos = s.transform.position - prefabBasePos;
var realPos = tile.Placement.Rotation * offPos + tile.Placement.Position;
//var floor = Mathf.FloorToInt((realPos.y + 16f) / 8f);
Patch.scrapDistance.Add(Vector3.Distance(realPos, firstTilePos));
//Patch.scrapFloors.Add(floor);
}
}
Plugin.logger.LogInfo($"C{analTestCount}, S{analAltFailCount}, R{gen.retryCount - analAltFailCount - analTestCount}\nLoot {Patch.scrapDistance.ToString()}");
analTestCount++;
Patch.stopwatch.Restart();
return true;
}
return false;
}
}
}

View File

@ -12,6 +12,7 @@ using UnityEngine;
using System.Collections; using System.Collections;
using DunGen; using DunGen;
using LethalLevelLoader; using LethalLevelLoader;
using DunGen.Graph;
namespace ScarletMansion.DunGenPatch { namespace ScarletMansion.DunGenPatch {
@ -20,10 +21,8 @@ namespace ScarletMansion.DunGenPatch {
[HarmonyPatch(typeof(RoundManager), "GenerateNewFloor")] [HarmonyPatch(typeof(RoundManager), "GenerateNewFloor")]
[HarmonyPrefix] [HarmonyPrefix]
public static void DungeonGeneratorGenerate_PrefixPrefix(){ public static void DungeonGeneratorGenerate_PrefixPrefix(){
// safety check
Plugin.logger.LogInfo("Disabling SDM logic"); Plugin.logger.LogInfo("Disabling SDM logic");
Patch.Deactivate(); Patch.Deactivate();
//Patch.ActivateAnalysis();
} }
public static void GeneratePatch(RoundManager roundManager){ public static void GeneratePatch(RoundManager roundManager){
@ -31,91 +30,6 @@ namespace ScarletMansion.DunGenPatch {
Patch.Activate(roundManager.dungeonGenerator.Generator); Patch.Activate(roundManager.dungeonGenerator.Generator);
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath")]
public static void GenerateMainPathPatch(ref DungeonGenerator __instance, ref IEnumerator __result){
if (Patch.active && Patch.callAlternative) {
GeneratePath.RandomizeLineArchetypes(__instance, true);
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(DungeonGenerator), "GenerateBranchPaths")]
public static void GenerateBranchPathsPatch(ref DungeonGenerator __instance, ref IEnumerator __result){
if (Patch.active && Patch.callAlternative) {
__result = GeneratePath.GenerateAlternativeMainPaths(__instance);
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(RoundManager), "FinishGeneratingLevel")]
public static void GenerateBranchPathsPatch(){
if (Patch.active) {
Plugin.logger.LogInfo("Alt. InnerGenerate() function complete");
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)]
public static IEnumerable<CodeInstruction> GenerateMainPathPatch(IEnumerable<CodeInstruction> instructions){
var addFunction = typeof(List<DungeonArchetype>).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
var sequence = new InstructionSequence("archetype node");
sequence.AddOperandTypeCheck(OpCodes.Ldfld, typeof(List<DungeonArchetype>));
sequence.AddBasic(OpCodes.Ldnull);
sequence.AddBasic(OpCodes.Callvirt, addFunction);
foreach(var instruction in instructions){
if (sequence.VerifyStage(instruction)){
var method = typeof(GeneratePath).GetMethod("ModifyMainBranchNodeArchetype", BindingFlags.Public | BindingFlags.Static);
yield return new CodeInstruction(OpCodes.Ldloc_S, 8);
yield return new CodeInstruction(OpCodes.Call, method);
yield return instruction;
continue;
}
yield return instruction;
}
sequence.ReportComplete();
}
/*
[HarmonyPatch(typeof(DungeonGenerator), "AddTile")]
[HarmonyPostfix]
public static void AddTilePatch(TileProxy attachTo, IEnumerable<TileSet> useableTileSets, DungeonArchetype archetype, ref TileProxy __result){
var atString = attachTo != null ? attachTo.Prefab.ToString() : "NULL";
var aString = archetype ? archetype.ToString() : "NULL";
var rString = __result != null ? __result.Prefab.ToString() : "NULL";
Plugin.logger.LogInfo($"");
Plugin.logger.LogInfo($"Attach: {atString}");
Plugin.logger.LogInfo($"ValidCount: {useableTileSets.Sum(u => u.TileWeights.Weights.Count).ToString()}");
Plugin.logger.LogInfo($"Entry: {aString}");
Plugin.logger.LogInfo($"Result: {rString}");
}
[HarmonyPatch(typeof(DungeonGenerator), "TryPlaceTile")]
[HarmonyPostfix]
public static void TryPlaceTilePatch(ref TilePlacementResult __result){
Plugin.logger.LogInfo(__result.ToString());
}
[HarmonyPatch(typeof(DoorwayPairFinder), "GetDoorwayPairs")]
[HarmonyPostfix]
public static void GetDoorwayPairsPatch(ref Queue<DoorwayPair> __result){
Plugin.logger.LogInfo(__result.Count().ToString());
}
*/
} }

View File

@ -1,59 +0,0 @@
/*
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using UnityEngine;
using HarmonyLib;
namespace ScarletMansion.DunGenPatch {
public class OptimizePatch {
[HarmonyPatch(typeof(DoorwayPairFinder), "GetDoorwayPairs")]
[HarmonyPrefix]
public static bool GetDoorwayPairsPatch(ref DoorwayPairFinder __instance, int? maxCount, ref Queue<DoorwayPair> __result){
__instance.tileOrder = __instance.CalculateOrderedListOfTiles();
var doorwayPairs = __instance.PreviousTile == null ?
__instance.GetPotentialDoorwayPairsForFirstTile() :
__instance.GetPotentialDoorwayPairsForNonFirstTile();
var num = doorwayPairs.Count();
if (maxCount != null) {
num = Mathf.Min(num, maxCount.Value);
}
__result = new Queue<DoorwayPair>(num);
var newList = OrderDoorwayPairs(doorwayPairs, num);
foreach(var item in newList){
__result.Enqueue(item);
}
return false;
}
private class DoorwayPairComparer : IComparer<DoorwayPair> {
public int Compare(DoorwayPair x, DoorwayPair y) {
var tileWeight = y.TileWeight.CompareTo(x.TileWeight);
if (tileWeight == 0) return y.DoorwayWeight.CompareTo(x.DoorwayWeight);
return tileWeight;
}
}
private static IEnumerable<DoorwayPair> OrderDoorwayPairs(IEnumerable<DoorwayPair> list, int num){
var c = list.Count();
var d = Mathf.Min(c, num);
if (d > 1) {
Patch.doorwayPairs.Add(d);
}
return list.OrderBy(x => x, new DoorwayPairComparer()).Take(num);
}
}
}
*/

View File

@ -10,6 +10,9 @@ using UnityEngine.AI;
using System.Diagnostics; using System.Diagnostics;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition; using UnityEngine.Rendering.HighDefinition;
using DunGenPlus.Collections;
using GameNetcodeStuff;
using ScarletMansion.GamePatch.Components;
namespace ScarletMansion.DunGenPatch { namespace ScarletMansion.DunGenPatch {
public static class Patch { public static class Patch {
@ -18,102 +21,70 @@ namespace ScarletMansion.DunGenPatch {
public static bool callAlternative; public static bool callAlternative;
public static DungeonGenerator generatorInstance; public static DungeonGenerator generatorInstance;
public static bool startAnalysis;
public static Stopwatch stopwatch;
public static AnalysisUtilities.Average scrapDistance;
public static AnalysisUtilities.Average scrapFloors;
//public static AnalysisUtilities.Average tileCalculations;
//public static AnalysisUtilities.Average doorwayPairs;
public static HDRenderPipelineAsset previousHDRPAsset;
public static HDRenderPipelineAsset sdmHDRPAsset;
public static void Activate(DungeonGenerator generator){ public static void Activate(DungeonGenerator generator){
active = true; active = true;
callAlternative = true; callAlternative = true;
generatorInstance = generator; generatorInstance = generator;
//startAnalysis = true;
var scale = generator.LengthMultiplier; var scale = generator.LengthMultiplier;
var bounds = GetDungeonBounds(scale);
generator.DungeonFlow.TileInjectionRules = new List<TileInjectionRule>();
generator.RestrictDungeonToBounds = true;
generator.TilePlacementBounds = bounds;
Plugin.logger.LogInfo($"Set new dungeon bounds with, {bounds.center} and {bounds.size}");
var mainPathLength = generator.DungeonFlow.Length; var mainPathLength = generator.DungeonFlow.Length;
Plugin.logger.LogInfo($"Length of main path be: {GetLength(mainPathLength, scale)}"); Plugin.logger.LogInfo($"Length of main path be: {GetLength(mainPathLength, scale)}");
GamePatch.LoadAssetsIntoLevelPatch.ModifyLevel(StartOfRound.Instance.currentLevel); GamePatch.LoadAssetsIntoLevelPatch.ModifyLevel(StartOfRound.Instance.currentLevel);
Plugin.logger.LogInfo("Updating HDRP asset: doubling max shadows request");
try {
previousHDRPAsset = QualitySettings.renderPipeline as HDRenderPipelineAsset;
sdmHDRPAsset = ScriptableObject.Instantiate(previousHDRPAsset);
var settings = sdmHDRPAsset.currentPlatformRenderPipelineSettings;
Plugin.logger.LogInfo($"maxScreenSpaceShadowSlots: {settings.hdShadowInitParams.maxScreenSpaceShadowSlots} -> {settings.hdShadowInitParams.maxScreenSpaceShadowSlots * 2}");
//Plugin.logger.LogInfo($"maxShadowRequests: {settings.hdShadowInitParams.maxShadowRequests} -> {settings.hdShadowInitParams.maxShadowRequests * 2}");
settings.hdShadowInitParams.maxScreenSpaceShadowSlots *= 2;
//settings.hdShadowInitParams.maxShadowRequests *= 2;
sdmHDRPAsset.currentPlatformRenderPipelineSettings = settings;
QualitySettings.renderPipeline = sdmHDRPAsset;
} catch (Exception e) {
Plugin.logger.LogError("Failed to update HDRP asset");
Plugin.logger.LogError(e.ToString());
}
if (startAnalysis) ActivateAnalysis();
}
public static void ActivateAnalysis(){
stopwatch = new Stopwatch();
stopwatch.Start();
scrapDistance = new AnalysisUtilities.Average();
scrapFloors = new AnalysisUtilities.Average();
//tileCalculations = new AnalysisUtilities.Average();
//doorwayPairs = new AnalysisUtilities.Average();
}
public static Bounds GetDungeonBounds(float scale){
var width = PluginConfig.Instance.dunGenWidthBaseValue;
var length = PluginConfig.Instance.dunGenLengthBaseValue;
var widthFac = PluginConfig.Instance.dunGenWidthMultiFactorValue;
var lengthFac = PluginConfig.Instance.dunGenLengthMultiFactorValue;
var totalwidth = width + (width * (scale - 1) * widthFac);
var totallength = length + (length * (scale - 1) * lengthFac);
//var
var offset = new Vector3(0f, 4f, totallength * 0.5f - 8f);
var size = new Vector3(totalwidth, 40f, totallength);
return new Bounds(offset, size);
} }
public static string GetLength(IntRange range, float multi){ public static string GetLength(IntRange range, float multi){
return $"({range.Min * multi} - {range.Max * multi})"; return $"({range.Min * multi} - {range.Max * multi})";
} }
public static void Deactivate(){ public static void Deactivate(bool ignoreScarletPlayer = false){
active = false; active = false;
callAlternative = false; callAlternative = false;
generatorInstance = null; generatorInstance = null;
GamePatch.JesterAIPatch.active = false; GamePatch.JesterAIPatch.active = false;
if (previousHDRPAsset && QualitySettings.renderPipeline == sdmHDRPAsset) { if (ignoreScarletPlayer) return;
Plugin.logger.LogInfo("Restoring original HDRP asset");
QualitySettings.renderPipeline = previousHDRPAsset; var localPlayer = StartOfRound.Instance.localPlayerController;
previousHDRPAsset = null; var scarletPlayer = ScarletPlayerControllerB.GetScarletPlayerScript(localPlayer);
sdmHDRPAsset = null; if (scarletPlayer != null){
scarletPlayer.stabbedSelf = false;
scarletPlayer.fellInPit = false;
}
}
public static void UpdateDunGenExtenderProperties(DunGenExtenderProperties props) {
props.MainPathCount = PluginConfig.Instance.mainPathCountValue;
if (PluginConfig.Instance.disableBasementValue) {
props.MainPathCount = Mathf.Min(props.MainPathCount, 2);
}
var mayorTileSet = PluginConfig.Instance.disableBasementValue ? Assets.networkObjectList.mayorVanillaTileset : Assets.networkObjectList.mayorRegularTileset;
Assets.dungeon.Nodes[1].TileSets = new List<DunGen.TileSet>() { mayorTileSet };
props.MainRoomTilePrefab = mayorTileSet.TileWeights.Weights[0].Value;
props.DungeonSizeBase = new Vector3(PluginConfig.Instance.dunGenWidthBaseValue, props.DungeonSizeBase.y, PluginConfig.Instance.dunGenLengthBaseValue);
props.DungeonSizeFactor = new Vector3(PluginConfig.Instance.dunGenWidthMultiFactorValue, props.DungeonSizeFactor.y, PluginConfig.Instance.dunGenLengthMultiFactorValue);
var i = 0;
while(i < 3) {
PluginConfig.Instance.branchPathSectionOneValue.UpdateValues(props.LineRandomizerArchetypes[i]);
++i;
}
while(i < 7) {
PluginConfig.Instance.branchPathSectionTwoValue.UpdateValues(props.LineRandomizerArchetypes[i]);
++i;
}
while(i < 10) {
PluginConfig.Instance.branchPathSectionThreeValue.UpdateValues(props.LineRandomizerArchetypes[i]);
++i;
} }
} }

View File

@ -1,24 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using HarmonyLib;
using UnityEngine;
namespace ScarletMansion.DunGenPatch {
public class PostProcessPatch {
[HarmonyPostfix]
[HarmonyPatch(typeof(DungeonGenerator), "PostProcess")]
public static void GenerateBranchPathsPatch(ref DungeonGenerator __instance){
if (Patch.active) {
var value = __instance.RandomStream.Next(999);
GamePatch.Props.SpawnSyncedObjectCycle.UpdateCycle(value);
}
}
}
}

View File

@ -0,0 +1,60 @@
using DunGen;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components
{
public class PathOpenUpParent : MonoBehaviour, IDungeonCompleteReceiver {
[System.Serializable]
public class Chokepoint {
public PathOpenup[] paths;
public void UpdatePath(System.Random sysRandom){
var count = sysRandom.Next(1, Mathf.Min(paths.Length, 2));
var length = paths.Length;
var items = new int[length];
for(var i = 0; i < length; i++){
items[i] = i;
}
Utility.Shuffle(sysRandom, items);
var j = 0;
while(j < 1) {
paths[j].UpdatePath(PathOpenup.State.Active);
++j;
}
while(j < length) {
paths[j].UpdatePath(PathOpenup.State.UnActive);
++j;
}
}
}
public Chokepoint[] chokepoints;
public PathOpenup[] paths;
void Reset(){
paths = GetComponentsInChildren<PathOpenup>();
}
public void OnDungeonComplete(Dungeon dungeon) {
var anyChanges = true;
var dunRandom = DunGenPatch.Patch.generatorInstance.RandomStream;
var sysRandom = new System.Random(dunRandom.Next());
foreach(var c in chokepoints) {
c.UpdatePath(sysRandom);
}
foreach(var c in paths) {
c.CleanUp();
}
}
}
}

View File

@ -0,0 +1,40 @@
using DunGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components {
public class PathOpenup : MonoBehaviour {
public GameObject[] connectorGameObjects;
public GameObject[] blockerGameObjects;
public enum State {
NotSet,
UnActive,
Active
}
private State state;
public void UpdatePath(State newState){
if (newState < state) return;
var connect = newState == State.Active;
foreach(var connectorGameObject in connectorGameObjects) connectorGameObject.SetActive(connect);
foreach(var blockerGameObject in blockerGameObjects) blockerGameObject.SetActive(!connect);
state = newState;
}
public void CleanUp() {
var targets = state == State.Active ? blockerGameObjects : connectorGameObjects;
foreach(var t in targets) Destroy(t);
}
}
}

View File

@ -182,11 +182,9 @@ namespace ScarletMansion.GamePatch.Components {
if (bonusEnemy == null) return; if (bonusEnemy == null) return;
try { try {
var enemy = bonusEnemy.enemy; var enemy = bonusEnemy.enemy;
var enemyIndex = bonusEnemy.index;
if (enemyIndex > -1){ if (bonusEnemy.index > -1){
var pos = vent.transform.position; var pos = vent.transform.position;
var dir = target.transform.position - pos; var dir = target.transform.position - pos;
dir.y = 0f; dir.y = 0f;
@ -196,7 +194,7 @@ namespace ScarletMansion.GamePatch.Components {
bonusEnemy.ApplySpawnLogic(); bonusEnemy.ApplySpawnLogic();
roundmanager.currentEnemyPower += enemy.PowerLevel; roundmanager.currentEnemyPower += enemy.PowerLevel;
var spawnedEnemy = roundmanager.SpawnEnemyGameObject(vent.transform.position, y, enemyIndex); var spawnedEnemy = ScarletNetworkManagerUtility.CreateEnemyWithRef(bonusEnemy, vent.transform.position, y);
ScarletNetworkManager.Instance.RequestEvilSkinApply(spawnedEnemy, enemy.name.ToLowerInvariant()); ScarletNetworkManager.Instance.RequestEvilSkinApply(spawnedEnemy, enemy.name.ToLowerInvariant());
} }
@ -223,6 +221,7 @@ namespace ScarletMansion.GamePatch.Components {
public void OnDungeonComplete(Dungeon dungeon) { public void OnDungeonComplete(Dungeon dungeon) {
AngerManager.Instance.AddBedroom(this); AngerManager.Instance.AddBedroom(this);
AngerManager.Instance.AddRoomOfInterest(transform);
var parent = GetComponentInParent<Tile>(); var parent = GetComponentInParent<Tile>();
lights = parent.GetComponentsInChildren<Light>(); lights = parent.GetComponentsInChildren<Light>();

View File

@ -39,10 +39,10 @@ namespace ScarletMansion.GamePatch.Components {
} }
} }
var timeOfDay = TimeOfDay.Instance; var time = Utility.GetTime();
var totalMinutes = (int)(timeOfDay.normalizedTimeOfDay * (60f * timeOfDay.numberOfHours)) + 360; var totalMinutes = time.totalMinutes;
var hours = Mathf.FloorToInt(totalMinutes / 60f); var hours = time.hours;
var minutes = totalMinutes % 60; var minutes = time.minutes;
var timeChanged = lastTotalMinutes != totalMinutes; var timeChanged = lastTotalMinutes != totalMinutes;
var hourChanged = lastHour != hours; var hourChanged = lastHour != hours;

View File

@ -13,6 +13,9 @@ namespace ScarletMansion.GamePatch.Components {
[Header("Door Reference")] [Header("Door Reference")]
public DoorLock door; public DoorLock door;
public bool doorState;
public bool overrideLock;
public bool overrideUnlock;
[Header("Personal Refs")] [Header("Personal Refs")]
public NavMeshObstacle obstacle; public NavMeshObstacle obstacle;
@ -25,18 +28,40 @@ namespace ScarletMansion.GamePatch.Components {
AngerManager.Instance.AddDoor(this); AngerManager.Instance.AddDoor(this);
} }
[ServerRpc] [ClientRpc]
public void LockDoorServerRpc(){ public void LockDoorClientRpc(){
LockDoorClientRpc(); doorState = true;
} ReevalulateDoorState();
[ServerRpc]
public void UnlockDoorServerRpc(){
UnlockDoorClientRpc();
} }
[ClientRpc] [ClientRpc]
public void LockDoorClientRpc(){ public void UnlockDoorClientRpc(){
doorState = false;
ReevalulateDoorState();
}
[ClientRpc]
public void LockDoorOverrideClientRpc(bool state){
overrideLock = state;
ReevalulateDoorState();
}
[ClientRpc]
public void UnlockDoorOverrideClientRpc(bool state){
overrideUnlock = state;
ReevalulateDoorState();
}
private void ReevalulateDoorState(){
var state = doorState;
if (overrideUnlock) state = false;
if (overrideLock) state = true;
if (state) LockDoorCall();
else UnlockDoorCall();
}
private void LockDoorCall(){
if (door){ if (door){
door.isDoorOpened = false; door.isDoorOpened = false;
door.navMeshObstacle.enabled = true; door.navMeshObstacle.enabled = true;
@ -54,8 +79,7 @@ namespace ScarletMansion.GamePatch.Components {
ps.Play(); ps.Play();
} }
[ClientRpc] private void UnlockDoorCall() {
public void UnlockDoorClientRpc(){
if (door){ if (door){
door.doorTrigger.interactable = true; door.doorTrigger.interactable = true;
door.isLocked = previousDoorLockValue; door.isLocked = previousDoorLockValue;

View File

@ -6,7 +6,9 @@ using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using Unity.Netcode; using Unity.Netcode;
using ScarletMansion.GamePatch.Enemies; using ScarletMansion.GamePatch.Enemies;
using System.Security.Permissions;
[assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )]
namespace ScarletMansion.GamePatch.Components { namespace ScarletMansion.GamePatch.Components {
public class ScarletDoorLock : DoorLock { public class ScarletDoorLock : DoorLock {
@ -86,7 +88,8 @@ namespace ScarletMansion.GamePatch.Components {
{ typeof(SpringManAI), (e) => e.currentBehaviourStateIndex == 0 }, { typeof(SpringManAI), (e) => e.currentBehaviourStateIndex == 0 },
{ typeof(KnightVariant), (e) => e.currentBehaviourStateIndex == 0 }, { typeof(KnightVariant), (e) => e.currentBehaviourStateIndex == 0 },
{ typeof(ButlerEnemyAI), (e) => e.currentBehaviourStateIndex <= 1 }, { typeof(ButlerEnemyAI), (e) => e.currentBehaviourStateIndex <= 1 },
{ typeof(MaidVariant), (e) => e.currentBehaviourStateIndex <= 1 } { typeof(MaidVariant), (e) => e.currentBehaviourStateIndex <= 1 },
{ typeof(KnightGhostVariant), (e) => false }
}; };
static readonly Dictionary<Type, float> EnemyDoorDamagePerSecond = new Dictionary<Type, float>(){ static readonly Dictionary<Type, float> EnemyDoorDamagePerSecond = new Dictionary<Type, float>(){
@ -104,7 +107,8 @@ namespace ScarletMansion.GamePatch.Components {
{ typeof(SpringManAI), 12.5f }, { typeof(SpringManAI), 12.5f },
{ typeof(KnightVariant), 12.5f }, { typeof(KnightVariant), 12.5f },
{ typeof(ButlerEnemyAI), 50f }, { typeof(ButlerEnemyAI), 50f },
{ typeof(MaidVariant), 50f } { typeof(MaidVariant), 50f },
{ typeof(KnightGhostVariant), 9999f }
}; };
public bool IsInOpenDoorNormallyState(EnemyAI enemy){ public bool IsInOpenDoorNormallyState(EnemyAI enemy){

View File

@ -8,42 +8,31 @@ using Unity.Netcode;
using GameNetcodeStuff; using GameNetcodeStuff;
namespace ScarletMansion.GamePatch.Components { namespace ScarletMansion.GamePatch.Components {
public class ScarletFrame : NetworkBehaviour { public class ScarletFrame : MonoBehaviour {
//public MeshRenderer renderer; public float visualUpdate = 0.2f;
//public Material[] materials; private float visualUpdateCurrent = 0f;
public void OnInteract(PlayerControllerB player){ void Start(){
var direction = player.transform.position - transform.position; visualUpdateCurrent = UnityEngine.Random.value * visualUpdate;
direction.y = 0f;
ChangeDirection(direction);
ChangeDirectionServerRpc(direction);
} }
/* void Update() {
[ClientRpc] visualUpdateCurrent += Time.deltaTime;
public void UpdateMaterialClientRpc(int value){ if (visualUpdateCurrent > visualUpdate) {
var mats = new List<Material>(2); LookAtLocalPlayer();
renderer.GetMaterials(mats); visualUpdateCurrent = 0f;
}
mats[1] = materials[value % materials.Length];
renderer.SetMaterials(mats);
}
*/
[ServerRpc(RequireOwnership = false)]
public void ChangeDirectionServerRpc(Vector3 direction){
ChangeDirectionClientRpc(direction);
} }
[ClientRpc] void LookAtLocalPlayer(){
public void ChangeDirectionClientRpc(Vector3 direction){ var localPlayer = StartOfRound.Instance.localPlayerController;
ChangeDirection(direction); if (localPlayer && !localPlayer.isPlayerDead) {
} var direction = localPlayer.transform.position - transform.position;
direction.y = 0f;
public void ChangeDirection(Vector3 direction){ transform.rotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.LookRotation(direction); }
} }
} }

View File

@ -9,12 +9,45 @@ using GameNetcodeStuff;
namespace ScarletMansion.GamePatch.Components { namespace ScarletMansion.GamePatch.Components {
public class ScarletPlayerControllerB : MonoBehaviour { public class ScarletPlayerControllerB : MonoBehaviour {
public static Dictionary<PlayerControllerB, ScarletPlayerControllerB> playerControllers;
// self
public PlayerControllerB player; public PlayerControllerB player;
// animation
public AnimatorOverrideController playerOverrideController;
// second-chance states
public bool stabbedSelf;
public bool fellInPit;
public static ScarletPlayerControllerB GetScarletPlayerScript(PlayerControllerB player) {
if (!playerControllers.TryGetValue(player, out var scarlet)) {
scarlet = player.GetComponent<ScarletPlayerControllerB>();
if (scarlet == null) {
Plugin.logger.LogError($"Couldn't find scarlet player script for {player}. Kinda bad");
return null;
}
Plugin.logger.LogMessage($"Scarlet player script for {player} was not initially registered. We good now but like why?");
playerControllers.Add(player, scarlet);
}
return scarlet;
}
public static void InitializeScarletScripts() {
playerControllers = new Dictionary<PlayerControllerB, ScarletPlayerControllerB>();
}
public void Initialize(PlayerControllerB player){ public void Initialize(PlayerControllerB player){
this.player = player; this.player = player;
CreateHelmetForFlashlight(player, Assets.flashlight, 0); CreateHelmetForFlashlight(player, Assets.flashlight, 0);
CreateHelmetForFlashlight(player, Assets.flashlightBB, 1); CreateHelmetForFlashlight(player, Assets.flashlightBB, 1);
}
public void Register(){
if (!playerControllers.ContainsKey(player))
playerControllers.Add(player, this);
} }
public static void CreateHelmetForFlashlight(PlayerControllerB player, Assets.Flashlight flashlight, int index){ public static void CreateHelmetForFlashlight(PlayerControllerB player, Assets.Flashlight flashlight, int index){

View File

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components {
public class ScarletRadio : NetworkBehaviour{
[Header("Networked Values")]
public int volume;
public bool playing;
public int audioIndex;
[Header("Songs")]
public AudioClip[] audioClips;
[Header("References")]
public AudioSource audioSource;
public float maxVolume = 1f;
public Transform onOffSwitchTransform;
public Transform volumeSwitchTransform;
public Transform volumeDialTransform;
public Transform tuneSwitchTransform;
private float onOffSwitchTransformX;
private float volumeSwitchTransformX;
private float volumeDialTransformX;
private float onOffSwitchTransforX;
[Header("Switch Values")]
public Vector2 onOffSwitchRotation;
public Vector2 volumeSwitchRotation;
public Vector2 volumeDialRotation;
public Vector2 tuneSwitchRotation;
void Start() {
audioSource.volume = maxVolume * (volume * 0.1f);
}
void RotateTransformTo(Transform dial, ref float current, float towards){
current = Mathf.Lerp(current, towards, Time.deltaTime * 2f);
var v = new Vector3(current, 0f, 0f);
dial.localEulerAngles = v;
}
void RotateTransformTo(Transform dial, ref float current, Vector2 lerpVector, float lerp){
RotateTransformTo(dial, ref current, Mathf.Lerp(lerpVector.x, lerpVector.y, lerp));
}
void Update(){
RotateTransformTo(onOffSwitchTransform, ref onOffSwitchTransformX, playing ? onOffSwitchRotation.y : onOffSwitchRotation.x);
RotateTransformTo(volumeSwitchTransform, ref volumeSwitchTransformX, volumeSwitchRotation, volume * 0.1f);
RotateTransformTo(volumeDialTransform, ref volumeDialTransformX, volumeDialRotation, volume * 0.1f);
RotateTransformTo(tuneSwitchTransform, ref onOffSwitchTransforX, tuneSwitchRotation, (float)audioIndex / (audioClips.Length - 1));
}
public void ToggleOnOffSwitch(){
ToggleOnOffSwitchServerRpc();
}
public void ToggleVolumeSwitchLeft() {
ToggleVolumeSwitchServerRpc(-1);
}
public void ToggleVolumeSwitchRight() {
ToggleVolumeSwitchServerRpc(1);
}
public void ToggleSongSwitch(){
ToggleSongSwitchServerRpc();
}
[ServerRpc(RequireOwnership = false)]
public void ToggleOnOffSwitchServerRpc(){
ToggleOnOffSwitchClientRpc(!playing);
}
[ClientRpc]
public void ToggleOnOffSwitchClientRpc(bool playing){
this.playing = playing;
if (playing) AttemptTurnOn();
else AttemptTurnOff();
}
[ServerRpc(RequireOwnership = false)]
public void ToggleVolumeSwitchServerRpc(int count){
ToggleVolumeSwitchClientRpc(Mathf.Clamp(volume + count, 0, 10));
}
[ClientRpc]
public void ToggleVolumeSwitchClientRpc(int volume){
this.volume = volume;
audioSource.volume = maxVolume * (volume * 0.1f);
}
[ServerRpc(RequireOwnership = false)]
public void ToggleSongSwitchServerRpc(){
ToggleSongSwitchClientRpc((audioIndex + 1) % audioClips.Length);
}
[ClientRpc]
public void ToggleSongSwitchClientRpc(int audioIndex){
this.audioIndex = audioIndex;
if (playing && audioSource.isPlaying) audioSource.Stop();
audioSource.clip = audioClips[audioIndex];
AttemptTurnOn();
}
public void AttemptTurnOff(){
if (!playing && audioSource.isPlaying) audioSource.Stop();
}
public void AttemptTurnOn(){
if (playing && !audioSource.isPlaying) audioSource.Play();
}
}
}

View File

@ -29,7 +29,27 @@ namespace ScarletMansion.GamePatch.Components
if (audioClipIndex == -1) comp.statusEffectAudioIndex = 0; if (audioClipIndex == -1) comp.statusEffectAudioIndex = 0;
comp.statusEffectAudioIndex = audioClipIndex; comp.statusEffectAudioIndex = audioClipIndex;
if (comp.isSinking) return; if (comp.isSinking) {
// teleporting them out for a second time in life
// won't be easy though kek
var scarletPlayer = ScarletPlayerControllerB.GetScarletPlayerScript(comp);
if (scarletPlayer != null && !scarletPlayer.fellInPit && comp.sinkingValue >= 0.9f && comp.health > 20) {
var selfPos = scarletPlayer.transform.position;
var farthestAINode = RoundManager.Instance.insideAINodes
.Select(n => n.transform.position)
.OrderByDescending(n => (selfPos - n).magnitude).FirstOrDefault();
comp.TeleportPlayer(farthestAINode);
var damage = ScarletNetworkManagerUtility.GetCriticalDamageToPlayer(comp, false);
comp.DamagePlayer(damage, false, true, CauseOfDeath.Suffocation);
ScarletNetworkManager.Instance.CreateSpawnAudioPrefab(farthestAINode, comp.actualClientId);
StopSinkingLocalPlayer(comp);
scarletPlayer.fellInPit = true;
}
return;
}
if (sinkingLocalPlayer){ if (sinkingLocalPlayer){
if (!comp.CheckConditionsForSinkingInQuicksand()) { if (!comp.CheckConditionsForSinkingInQuicksand()) {

View File

@ -0,0 +1,74 @@
using DunGen;
using ScarletMansion.GamePatch.Managers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components.TreasureRoom {
public class TreasureRoomTimedOpen : MonoBehaviour, IDungeonCompleteReceiver {
[Header("References")]
public GameObject timeGameObject;
public TextMeshPro timeTextMesh;
[Header("Values")]
public Vector2Int hourRange;
public bool opened;
private int hourSelected;
private int mintuesSelected;
private ScarletDoor treasureDoorLock;
public void Update(){
if (treasureDoorLock == null) return;
if (!StartOfRound.Instance.IsHost || opened) return;
var time = Utility.GetTime();
if (time.hours > hourSelected || (time.hours >= hourSelected && time.minutes >= mintuesSelected)) {
treasureDoorLock.LockDoorOverrideClientRpc(false);
opened = true;
Plugin.logger.LogInfo($"Opening cause {time.hours}:{time.minutes} > {hourSelected}:{mintuesSelected}");
}
}
public void OnDungeonComplete(Dungeon dungeon) {
AngerManager.Instance.AddRoomOfInterest(transform);
if (!StartOfRound.Instance.IsHost) return;
var tile = GetComponentInParent<Tile>();
var doorways = tile.UsedDoorways;
foreach(var d in doorways) {
var neighboorTile = d.ConnectedDoorway.Tile.gameObject.name.ToLowerInvariant();
//Plugin.logger.LogInfo(neighboorTile);
if (neighboorTile.Contains("treasure")){
StartCoroutine(BeginTreasureRoomProcess(d));
return;
}
}
}
public IEnumerator BeginTreasureRoomProcess(Doorway doorway){
yield return new WaitForSecondsRealtime(4f);
Plugin.logger.LogInfo("Setting up lock for treasure room");
treasureDoorLock = AngerManager.Instance.GetScarletDoor(doorway.transform.position);
treasureDoorLock.LockDoorOverrideClientRpc(true);
hourSelected = UnityEngine.Random.Range(hourRange.x, hourRange.y);
mintuesSelected = UnityEngine.Random.Range(0, 3) * 15;
timeGameObject.SetActive(true);
timeTextMesh.text = $"{hourSelected}:{mintuesSelected}";
Plugin.logger.LogInfo($"Opening at {hourSelected}:{mintuesSelected}");
}
}
}

View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Unity.Netcode;
using UnityEngine.AI;
using GameNetcodeStuff;
using ScarletMansion.GamePatch.Components;
namespace ScarletMansion {
public class KnightGhostVariant : EnemyAI {
public float maxChaseSpeed = 14.5f;
public float slowChaseSpeed = 6f;
private float currentAnimSpeed = 1f;
public Collider mainCollider;
public MeshRenderer[] knightMeshRenderers;
public AudioClip[] ramAudioClips;
public override void Start(){
base.Start();
if (IsOwner && KnightSpawnManager.Instance) {
var index = KnightSpawnManager.Instance.GetSpawnPointIndex();
if (index == -1) return;
SyncKnightReplacementClientRpc(index);
}
}
public override void DoAIInterval() {
base.DoAIInterval();
movingTowardsTargetPlayer = true;
if (targetPlayer == null || !targetPlayer.IsOwner) return;
if (isEnemyDead) return;
var targetOutOfMap = !PlayerIsTargetable(targetPlayer);
if (targetOutOfMap){
CallDisappear();
return;
}
var targetLookingAtMe = !Physics.Linecast(transform.position + Vector3.up * 0.5f, targetPlayer.gameplayCamera.transform.position, StartOfRound.Instance.collidersAndRoomMaskAndDefault) && Vector3.Distance(base.transform.position, targetPlayer.transform.position) < 30f;
var newBehaviourState = targetLookingAtMe ? 1 : 0;
if (newBehaviourState != currentBehaviourStateIndex) {
SwitchToBehaviourState(newBehaviourState);
}
}
public override void Update() {
base.Update();
if (isEnemyDead) return;
var trueSpeed = currentBehaviourStateIndex == 0 ? maxChaseSpeed : slowChaseSpeed;
if (IsOwner) {
agent.speed = Mathf.MoveTowards(agent.speed, trueSpeed, 4.5f * Time.deltaTime);
}
currentAnimSpeed = Mathf.Lerp(currentAnimSpeed, trueSpeed * 0.4597f, 4f * Time.deltaTime);
creatureAnimator.SetFloat("walkSpeed", currentAnimSpeed);
}
public override void OnCollideWithPlayer(Collider other) {
base.OnCollideWithPlayer(other);
var playerControllerB = MeetsStandardPlayerCollisionConditions(other, false, false);
if (playerControllerB != null && playerControllerB.IsOwner && playerControllerB == targetPlayer) {
var damage = ScarletNetworkManagerUtility.GetCriticalDamageToPlayer(playerControllerB, false);
playerControllerB.DamagePlayer(damage, true, true, CauseOfDeath.Mauling, 1, false, default(Vector3));
playerControllerB.JumpToFearLevel(1f, true);
RoundManager.PlayRandomClip(creatureVoice, ramAudioClips, false, 1f, 0);
CallDisappear();
}
}
[ClientRpc]
public void SyncKnightReplacementClientRpc(int index){
Plugin.logger.LogInfo($"Spawning ghost knight at {index}");
try {
var target = KnightSpawnManager.Instance.GetSpawnPointTransform(index);
target.gameObject.SetActive(false);
transform.position = target.position;
transform.rotation = target.rotation;
serverPosition = target.position;
if (agent == null) agent = GetComponentInChildren<NavMeshAgent>();
agent.Warp(target.position);
if (IsOwner) {
SyncPositionToClients();
}
} catch (Exception e){
Plugin.logger.LogError($"Tried to ghost knight spawn at {index}, but completely failed");
Plugin.logger.LogError(e);
}
}
public void FindAndTunnelPlayer(Vector3 searchPosition){
var validPlayers = StartOfRound.Instance.allPlayerScripts.Where(p => p != null && PlayerIsTargetable(p));
if (validPlayers.Count() == 0) {
Plugin.logger.LogWarning("Could not find valid target to tunnel");
return;
}
var closestPlayer = validPlayers
.OrderBy(p => Vector3.SqrMagnitude(p.transform.position - searchPosition))
.FirstOrDefault();
if (closestPlayer != null) {
ChangeOwnershipOfEnemy(closestPlayer.actualClientId);
TunnelPlayerClientRpc(closestPlayer);
}
}
[ClientRpc]
public void TunnelPlayerClientRpc(NetworkBehaviourReference playerRef){
if (playerRef.TryGet<PlayerControllerB>(out var player)){
targetPlayer = player;
Plugin.logger.LogInfo($"Targeting {player.playerUsername} for death");
if (targetPlayer.IsOwner) return;
// other clients will only see eyes of death
foreach(var m in knightMeshRenderers) {
m.enabled = false;
}
} else {
Plugin.logger.LogWarning("Could not find target player through reference");
}
}
private bool calledDisappear = false;
public void CallDisappear(){
if (calledDisappear) return;
Plugin.logger.LogInfo("Killing ghost knight");
calledDisappear = true;
mainCollider.enabled = false;
mainCollider.gameObject.SetActive(false);
ScarletNetworkManager.Instance.CreateSpawnAudioPrefab(transform.position, targetPlayer.actualClientId);
DisappearServerRpc();
}
[ServerRpc(RequireOwnership = false)]
public void DisappearServerRpc(){
KillEnemy(true);
}
}
}

View File

@ -185,6 +185,8 @@ namespace ScarletMansion.GamePatch.Enemies {
public Transform knifeRenderTransform; public Transform knifeRenderTransform;
public EnemyType knightGhostEnemy;
public AudioSource chaseMusicStart; public AudioSource chaseMusicStart;
// Token: 0x04000111 RID: 273 // Token: 0x04000111 RID: 273
@ -265,6 +267,9 @@ namespace ScarletMansion.GamePatch.Enemies {
knifeRender.SetActive(false); knifeRender.SetActive(false);
if (IsServer) { if (IsServer) {
var enemy = ScarletNetworkManagerUtility.CreateEnemyWithType<KnightGhostVariant>(knightGhostEnemy, transform.position, 0f);
enemy.FindAndTunnelPlayer(transform.position);
var newKnife = Object.Instantiate(knifePrefab, base.transform.position + Vector3.up * 0.5f, Quaternion.identity, RoundManager.Instance.spawnedScrapContainer); var newKnife = Object.Instantiate(knifePrefab, base.transform.position + Vector3.up * 0.5f, Quaternion.identity, RoundManager.Instance.spawnedScrapContainer);
newKnife.GetComponent<NetworkObject>().Spawn(); newKnife.GetComponent<NetworkObject>().Spawn();
newKnife.GetComponent<ScarletKnife>().LinkKnifeToMaidServerRpc(this); newKnife.GetComponent<ScarletKnife>().LinkKnifeToMaidServerRpc(this);

View File

@ -48,19 +48,12 @@ namespace ScarletMansion.GamePatch {
[HarmonyPrefix] [HarmonyPrefix]
public static void StartOfRound_Start(ref StartOfRound __instance) { public static void StartOfRound_Start(ref StartOfRound __instance) {
ScarletYukariTrigger.audioClipIndex = -1; ScarletYukariTrigger.audioClipIndex = -1;
PlayerAnimatorStateHelper.ClearAnimators(); ScarletPlayerControllerB.InitializeScarletScripts();
__instance.StartCoroutine(WaitForNetworkObject(__instance, CreateNetworkManager)); __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 // safety cleaning
DunGenPatch.Patch.Deactivate(); DunGenPatch.Patch.Deactivate(true);
//ScarletLightingManager.Clean(); //ScarletLightingManager.Clean();
FixMapReferences(__instance); FixMapReferences(__instance);
@ -72,28 +65,12 @@ namespace ScarletMansion.GamePatch {
statusEffectClips.Add(Assets.networkObjectList.sinkingAudioClip); statusEffectClips.Add(Assets.networkObjectList.sinkingAudioClip);
__instance.statusEffectClips = statusEffectClips.ToArray(); __instance.statusEffectClips = statusEffectClips.ToArray();
// DunGenAnalyis.Analysis(Assets.dungeon, __instance, RoundManager.Instance);
} }
[HarmonyPatch(typeof(RoundManager), "Awake")] [HarmonyPatch(typeof(RoundManager), "Awake")]
[HarmonyPrefix] [HarmonyPrefix]
public static void RoundManagerAwakePatch(ref RoundManager __instance) { public static void RoundManagerAwakePatch(ref RoundManager __instance) {
FixDungeonPrefabValues(__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")] [HarmonyPatch(typeof(RoundManager), "Start")]
@ -110,24 +87,6 @@ namespace ScarletMansion.GamePatch {
return true; 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){ public static void FixMapReferences(StartOfRound round){
try { try {
@ -188,7 +147,7 @@ namespace ScarletMansion.GamePatch {
var butlerPrefab = butlerItem.enemyType.enemyPrefab; var butlerPrefab = butlerItem.enemyType.enemyPrefab;
var butlerScript = butlerPrefab.GetComponent<ButlerEnemyAI>(); var butlerScript = butlerPrefab.GetComponent<ButlerEnemyAI>();
FixParticleSystemMaterialAndChildren(maidScript.stabBloodParticle, butlerScript.stabBloodParticle); Utility.FixParticleSystemMaterialAndChildren(maidScript.stabBloodParticle, butlerScript.stabBloodParticle);
LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, maid.terminalNode, maid.terminalKeyword); LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, maid.terminalNode, maid.terminalKeyword);
} }
@ -300,8 +259,8 @@ namespace ScarletMansion.GamePatch {
scarletScript.hitSFX = knifeScript.hitSFX; scarletScript.hitSFX = knifeScript.hitSFX;
scarletScript.swingSFX = knifeScript.swingSFX; scarletScript.swingSFX = knifeScript.swingSFX;
FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle); Utility.FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle);
FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle); Utility.FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle);
} }
if (Assets.flashlight.item == null) { if (Assets.flashlight.item == null) {
@ -347,12 +306,6 @@ namespace ScarletMansion.GamePatch {
var prefabs = networkmanager.NetworkConfig.Prefabs.Prefabs; var prefabs = networkmanager.NetworkConfig.Prefabs.Prefabs;
var prefabFixList = Assets.networkObjectList.toFixGameObjects; 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"); var steeldoor = prefabs.FirstOrDefault(p => p.Prefab.name.ToLowerInvariant() == "fancydoormapmodel");
if (GameReadNullCheck(steeldoor, "FancyDoorMapModel", "SDM doors will have missing icons and sounds")){ if (GameReadNullCheck(steeldoor, "FancyDoorMapModel", "SDM doors will have missing icons and sounds")){
var interact = steeldoor.Prefab.GetComponentInChildren<InteractTrigger>(); var interact = steeldoor.Prefab.GetComponentInChildren<InteractTrigger>();
@ -404,61 +357,19 @@ namespace ScarletMansion.GamePatch {
} }
private static IEnumerator UpdateNetworkConfig(RoundManager roundManager){ private static IEnumerator UpdateNetworkConfig(RoundManager roundManager){
while(PluginConfig.Synced == false){ while(PluginConfig.Synced == false){
yield return null; 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[] { Lights.ScarletLightCleanup.weights = new float[] {
PluginConfig.Instance.lightsSpawnZeroWeightValue, PluginConfig.Instance.lightsSpawnZeroWeightValue,
PluginConfig.Instance.lightsSpawnOneWeightValue, PluginConfig.Instance.lightsSpawnOneWeightValue,
PluginConfig.Instance.lightsSpawnTwoWeightValue 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(); 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) { DungeonFlow.GlobalPropSettings GetGlobalPropSetting(int id) {
foreach(var p in Assets.dungeon.GlobalProps){ foreach(var p in Assets.dungeon.GlobalProps){
if (p.ID == id ) return p; if (p.ID == id ) return p;
@ -474,19 +385,5 @@ namespace ScarletMansion.GamePatch {
Plugin.logger.LogInfo("Set networked config values"); 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;
}
}
}
}
} }
} }

View File

@ -1,124 +0,0 @@
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<ulong, PlayerAnimatorStateHelper> 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<ulong, PlayerAnimatorStateHelper>();
}
//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<KeyValuePair<AnimationClip, AnimationClip>> {
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<AnimationClip, AnimationClip>(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;
}
}
}

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using GameNetcodeStuff; using GameNetcodeStuff;
using OdinSerializer; using OdinSerializer;
using ScarletMansion.GamePatch.Components;
using ScarletMansion.GamePatch.Enemies; using ScarletMansion.GamePatch.Enemies;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
@ -119,9 +120,20 @@ namespace ScarletMansion.GamePatch.Items
// buff // buff
if (buffed) { if (buffed) {
IHittable target;
int damage;
// prevent the player from one-shotting their friends // prevent the player from one-shotting their friends
var target = component is PlayerControllerB ? previousPlayerHeldBy as IHittable : component; var targetPlayer = component as PlayerControllerB;
target.Hit(50, forward, previousPlayerHeldBy, playHitSFX: true); if (targetPlayer != null) {
target = previousPlayerHeldBy;
damage = GetDamageToPlayer(previousPlayerHeldBy);
} else {
target = component;
damage = 40;
}
target.Hit(damage, forward, previousPlayerHeldBy, playHitSFX: true);
UnBuffShovelServerRpc(); UnBuffShovelServerRpc();
} }
// normal knife // normal knife
@ -152,6 +164,15 @@ namespace ScarletMansion.GamePatch.Items
} }
} }
public int GetDamageToPlayer(PlayerControllerB player) {
var scarletPlayer = ScarletPlayerControllerB.GetScarletPlayerScript(player);
if (scarletPlayer == null) return 85;
var forceKill = scarletPlayer.stabbedSelf;
scarletPlayer.stabbedSelf = true;
return ScarletNetworkManagerUtility.GetCriticalDamageToPlayer(player, forceKill);
}
[ServerRpc] [ServerRpc]
public void HitShovelServerRpc(int hitSurfaceID) { public void HitShovelServerRpc(int hitSurfaceID) {
HitShovelClientRpc(hitSurfaceID); HitShovelClientRpc(hitSurfaceID);
@ -187,9 +208,8 @@ namespace ScarletMansion.GamePatch.Items
player.activatingItem = true; player.activatingItem = true;
yield return new WaitForSeconds(0.25f); yield return new WaitForSeconds(0.25f);
var dmg = PluginConfig.Instance.maidKnifeSelfDamageValue; var damage = GetDamageToPlayer(player);
var realDmg = Mathf.Max(dmg, Mathf.RoundToInt(player.health * (dmg / 100f))); player.DamagePlayer(damage, true, true, CauseOfDeath.Stabbing);
player.DamagePlayer(realDmg, true, true, CauseOfDeath.Stabbing);
bloodParticle.Play(withChildren: true); bloodParticle.Play(withChildren: true);
RoundManager.PlayRandomClip(knifeAudio, hitSFX); RoundManager.PlayRandomClip(knifeAudio, hitSFX);

View File

@ -1,4 +1,5 @@
using GameNetcodeStuff; using GameNetcodeStuff;
using ScarletMansion.GamePatch.Components;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -19,10 +20,9 @@ namespace ScarletMansion.GamePatch.Items {
public AudioSource musicAS; public AudioSource musicAS;
[Header("Extra")] [Header("Extra")]
public PlayerControllerB previouslyHeldBy;
public Transform prefabPivot; public Transform prefabPivot;
public ScanNodeProperties scanNode; public ScanNodeProperties scanNode;
public AnimatorOverrideController SnowGlobeOverride;
public string SnowGlobeOriginalHoldClipName;
public AnimationClip SnowGlobeOverrideHoldClip; public AnimationClip SnowGlobeOverrideHoldClip;
[System.Serializable] [System.Serializable]
@ -61,51 +61,45 @@ namespace ScarletMansion.GamePatch.Items {
scanNode.headerText = $"{currentType.name} Snow Globe"; scanNode.headerText = $"{currentType.name} Snow Globe";
} }
private PlayerAnimatorStateHelper GetAnimator(PlayerControllerB player) { public override void GrabItem() {
if (player != null && player.playerBodyAnimator != null) { base.GrabItem();
return PlayerAnimatorStateHelper.TryAddAnimator(player.actualClientId, player.playerBodyAnimator); previouslyHeldBy = playerHeldBy;
} ReplaceOrPutBackAnimationClip(previouslyHeldBy, true);
return null;
} }
public override void EquipItem() { public override void EquipItem() {
base.EquipItem(); base.EquipItem();
previouslyHeldBy = playerHeldBy;
var animator = GetAnimator(playerHeldBy); ReplaceOrPutBackAnimationClip(previouslyHeldBy, true);
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); ToggleParticleRenderer(true);
} }
public override void PocketItem() { 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(); base.PocketItem();
if (previouslyHeldBy != null) {
// Disable Particles renderer ReplaceOrPutBackAnimationClip(previouslyHeldBy, false);
}
ToggleParticleRenderer(false); ToggleParticleRenderer(false);
} }
public override void DiscardItem() { public override void DiscardItem() {
var animator = GetAnimator(playerHeldBy); if (previouslyHeldBy != null) {
if (animator != null) { ReplaceOrPutBackAnimationClip(previouslyHeldBy, false);
animator.SaveAnimatorStates();
animator.RestoreOriginalAnimatorController();
Plugin.logger.LogInfo("Animator restored for player: " + playerHeldBy.playerBodyAnimator.gameObject.name);
} }
base.DiscardItem(); base.DiscardItem();
} }
public void ReplaceOrPutBackAnimationClip(PlayerControllerB player, bool _override) {
if (player == null) return;
var playerOverrideController = ScarletPlayerControllerB.GetScarletPlayerScript(player).playerOverrideController;
if (playerOverrideController == null) return;
if (_override) playerOverrideController["HoldClipboard"] = SnowGlobeOverrideHoldClip;
else playerOverrideController["HoldClipboard"] = null;
}
public override void ItemActivate(bool used, bool buttonDown = true) { public override void ItemActivate(bool used, bool buttonDown = true) {
base.ItemActivate(used, buttonDown); base.ItemActivate(used, buttonDown);
if (!activated) { if (!activated) {

View File

@ -85,7 +85,12 @@ namespace ScarletMansion.GamePatch {
return TranspilerUtilities.InjectMethod(instructions, itemsInjection, "SpawnScrapInLevel", 4); return TranspilerUtilities.InjectMethod(instructions, itemsInjection, "SpawnScrapInLevel", 4);
} }
[HarmonyTranspiler]
[HarmonyPatch(typeof(DunGenPlus.Patches.RoundManagerPatch), "waitForScrapToSpawnToSyncPatch")]
public static IEnumerable<CodeInstruction> waitForScrapToSpawnToSyncPatch(IEnumerable<CodeInstruction> instructions){
return TranspilerUtilities.InjectMethod(instructions, itemsInjection, "waitForScrapToSpawnToSyncPatch", 1);
}
public static List<SpawnableEnemyWithRarity> lastEnemiesRarity; public static List<SpawnableEnemyWithRarity> lastEnemiesRarity;
public static List<SpawnableEnemyWithRarity> currentEnemiesRarity; public static List<SpawnableEnemyWithRarity> currentEnemiesRarity;

View File

@ -23,7 +23,10 @@ namespace ScarletMansion.GamePatch.Managers {
//public Dictionary<EnemyAI, int> angeredEnemies; //public Dictionary<EnemyAI, int> angeredEnemies;
public int level; public int level;
public List<Transform> roomsOfInterest;
public List<ScarletBedroom> bedrooms; public List<ScarletBedroom> bedrooms;
public List<ScarletDoor> doors; public List<ScarletDoor> doors;
public List<ScarletLight> lights; public List<ScarletLight> lights;
@ -32,6 +35,8 @@ namespace ScarletMansion.GamePatch.Managers {
void Awake(){ void Awake(){
Instance = this; Instance = this;
roomsOfInterest = new List<Transform>();
bedrooms = new List<ScarletBedroom>(); bedrooms = new List<ScarletBedroom>();
doors = new List<ScarletDoor>(); doors = new List<ScarletDoor>();
lights = new List<ScarletLight>(); lights = new List<ScarletLight>();
@ -91,10 +96,14 @@ namespace ScarletMansion.GamePatch.Managers {
bedrooms.Add(b); bedrooms.Add(b);
} }
public void AddRoomOfInterest(Transform t) {
roomsOfInterest.Add(t);
}
public void AddDoor(ScarletDoor d){ public void AddDoor(ScarletDoor d){
// I want to get only doors that are revelant // I want to get only doors that are revelant
foreach(var b in bedrooms){ foreach(var t in roomsOfInterest){
var dist = Vector3.SqrMagnitude(d.transform.position - b.transform.position); var dist = Vector3.SqrMagnitude(d.transform.position - t.position);
if (dist < 16f * 16f){ if (dist < 16f * 16f){
doors.Add(d); doors.Add(d);
return; return;

View File

@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
using ScarletMansion.DunGenPatch.Doorways;
using Unity.AI.Navigation;
using UnityEngine.AI;
using DunGen.Adapters;
namespace ScarletMansion.GamePatch.Managers {
public class DoorwayManager : MonoBehaviour {
public static DoorwayManager Instance { get; private set; }
public static ActionList onMainEntranceTeleportSpawnedEvent = new ActionList("onMainEntranceTeleportSpawned");
public List<DoorwayCleanup> doorwayCleanup;
public void Awake(){
Instance = this;
doorwayCleanup = new List<DoorwayCleanup>();
}
/*
private NavMeshSurface surface;
private GameObject meshGameObject;
private MeshFilter meshFilter;
void Update(){
if (surface == null){
surface = FindObjectOfType<NavMeshSurface>();
}
if (surface != null){
if (meshFilter == null){
meshGameObject = new GameObject("Nav Mesh V");
meshFilter = meshGameObject.AddComponent<MeshFilter>();
var renderer = meshGameObject.AddComponent<MeshRenderer>();
renderer.materials = new Material[1];
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
}
var mesh = new Mesh();
var triangulation = NavMesh.CalculateTriangulation();
mesh.SetVertices(triangulation.vertices);
mesh.SetIndices(triangulation.indices, MeshTopology.Triangles, 0);
meshGameObject.transform.position = new Vector3(0f, 0.05f, 0f);
meshFilter.mesh = mesh;
}
}
*/
public void AddDoorwayCleanup(DoorwayCleanup dw){
doorwayCleanup.Add(dw);
}
public static void onMainEntranceTeleportSpawnedFunction(){
if (Instance && DunGenPatch.Patch.active) {
var doorwayCleanups = Instance.doorwayCleanup;
foreach(var d in doorwayCleanups){
d.SetBlockers(false);
d.Cleanup();
}
try{
var dungeonGen = RoundManager.Instance.dungeonGenerator;
var navmesh = dungeonGen.transform.parent.GetComponentInChildren<UnityNavMeshAdapter>();
navmesh.Run(dungeonGen.Generator);
Plugin.logger.LogInfo("Rebuild nav mesh");
} catch (Exception e){
Plugin.logger.LogError("Failed to rebuild nav mesh");
Plugin.logger.LogError(e.ToString());
}
}
}
}
}

View File

@ -23,8 +23,6 @@ namespace ScarletMansion {
} }
public void OnDungeonComplete(Dungeon dungeon) { public void OnDungeonComplete(Dungeon dungeon) {
// IDK KNOW IF I CAN TRUST THIS TO BE THE SAME FOR ALL CLIENTS
// but probably
var points = dungeon.GetComponentsInChildren<KnightSpawnPoint>(); var points = dungeon.GetComponentsInChildren<KnightSpawnPoint>();
spawnPoints = points.ToList(); spawnPoints = points.ToList();
for(var i = 0; i < spawnPoints.Count; ++i){ for(var i = 0; i < spawnPoints.Count; ++i){

View File

@ -10,6 +10,7 @@ using GameNetcodeStuff;
using ScarletMansion.GamePatch.Items; using ScarletMansion.GamePatch.Items;
using ScarletMansion.GamePatch; using ScarletMansion.GamePatch;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using static LethalLevelLoader.ExtendedEvent;
namespace ScarletMansion { namespace ScarletMansion {
@ -301,16 +302,77 @@ namespace ScarletMansion {
} }
public void CreateSpawnAudioPrefab(Vector3 positon, ulong playerId) {
CreateSpawnAudioPrefabServerRpc(positon, playerId);
CreateSpawnAudioPrefab(positon);
}
[ServerRpc(RequireOwnership = false)]
public void CreateSpawnAudioPrefabServerRpc(Vector3 position, ulong playerId){
CreateSpawnAudioPrefabClientRpc(position, playerId);
}
[ClientRpc]
public void CreateSpawnAudioPrefabClientRpc(Vector3 position, ulong playerId){
if (StartOfRound.Instance.localPlayerController.actualClientId == playerId) return;
CreateSpawnAudioPrefab(position);
}
private void CreateSpawnAudioPrefab(Vector3 position) {
var copy = Instantiate(Assets.networkObjectList.yukariSpawnPrefab, position, Quaternion.identity);
var audioSource = copy.GetComponentInChildren<AudioSource>();
audioSource.time = 0.5f;
Destroy(copy, 5f);
}
} }
public static class ScarletNetworkManagerUtility { public static class ScarletNetworkManagerUtility {
public static int GetCriticalDamageToPlayer(PlayerControllerB player, bool forceKill) {
if (player.health > 20 && !forceKill) {
return player.health - 15;
}
return player.health;
}
public static int GetFlashlightId(FlashlightItem flashlightItem){ public static int GetFlashlightId(FlashlightItem flashlightItem){
var flashlight = Assets.GetFlashlight(flashlightItem.itemProperties); var flashlight = Assets.GetFlashlight(flashlightItem.itemProperties);
if (flashlight != null) return flashlight.itemId; if (flashlight != null) return flashlight.itemId;
return -1; return -1;
} }
public static NetworkObjectReference CreateEnemyWithRef(EnemyReferenceSpawnLogic enemy, Vector3 position, float yRotation) {
var roundManager = RoundManager.Instance;
if (roundManager == null || !roundManager.IsHost) return default;
return roundManager.SpawnEnemyGameObject(position, yRotation, enemy.index);
}
public static T CreateEnemyWithRef<T>(EnemyReferenceSpawnLogic enemy, Vector3 position, float yRotation) where T: EnemyAI {
var spawnedEnemy = CreateEnemyWithRef(enemy, position, yRotation);
if (spawnedEnemy.TryGet(out var obj)) {
return obj.GetComponentInChildren<T>();
}
return null;
}
public static NetworkObjectReference CreateEnemyWithType(EnemyType enemy, Vector3 position, float yRotation) {
var roundManager = RoundManager.Instance;
if (roundManager == null || !roundManager.IsHost) return default;
return roundManager.SpawnEnemyGameObject(position, yRotation, -1, enemy);
}
public static T CreateEnemyWithType<T>(EnemyType enemy, Vector3 position, float yRotation) where T: EnemyAI {
var spawnedEnemy = CreateEnemyWithType(enemy, position, yRotation);
if (spawnedEnemy.TryGet(out var obj)) {
return obj.GetComponentInChildren<T>();
}
return null;
}
public static bool CreateFlashlight(PlayerControllerB player, FlashlightItem flashLight, FlandreCrystal crystal){ public static bool CreateFlashlight(PlayerControllerB player, FlashlightItem flashLight, FlandreCrystal crystal){
var color = crystal.colorIndex; var color = crystal.colorIndex;
var position = crystal.transform.position + Vector3.up * 0.25f; var position = crystal.transform.position + Vector3.up * 0.25f;

View File

@ -14,14 +14,26 @@ namespace ScarletMansion.GamePatch {
[HarmonyPatch(typeof(PlayerControllerB), "Awake")] [HarmonyPatch(typeof(PlayerControllerB), "Awake")]
[HarmonyPrefix] [HarmonyPrefix]
public static void AwakePatch(ref PlayerControllerB __instance){ public static void AwakePatch(ref PlayerControllerB __instance){
if (__instance.GetComponent<ScarletPlayerControllerB>() == null) { var currentComp = __instance.GetComponent<ScarletPlayerControllerB>();
if (currentComp == null) {
Plugin.logger.LogInfo($"Adding Scarlet player script to player {__instance.playerClientId}"); Plugin.logger.LogInfo($"Adding Scarlet player script to player {__instance.playerClientId}");
var comp = __instance.gameObject.AddComponent<ScarletPlayerControllerB>(); var comp = __instance.gameObject.AddComponent<ScarletPlayerControllerB>();
comp.Initialize(__instance); comp.Initialize(__instance);
comp.Register();
} else { } else {
currentComp.Register();
Plugin.logger.LogWarning($"Player {__instance.playerClientId} already has Scarlet player script. Skipping. YOU CAN PROBABLY IGNORE THIS!"); Plugin.logger.LogWarning($"Player {__instance.playerClientId} already has Scarlet player script. Skipping. YOU CAN PROBABLY IGNORE THIS!");
} }
} }
public static void UpdatePatch(ref PlayerControllerB __instance) {
if (GameNetworkManager.Instance.localPlayerController == null) return;
var controller = ScarletPlayerControllerB.GetScarletPlayerScript(__instance);
if (controller != null) return;
controller.playerOverrideController = new AnimatorOverrideController(__instance.playerBodyAnimator.runtimeAnimatorController);
__instance.playerBodyAnimator.runtimeAnimatorController = controller.playerOverrideController;
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace ScarletMansion.GamePatch.Props {
if (randomizePosition){ if (randomizePosition){
var x = (float)randomStream.NextDouble() * randomPositionRange; var x = (float)randomStream.NextDouble() * randomPositionRange;
var z = (float)randomStream.NextDouble() * randomPositionRange; var z = (float)randomStream.NextDouble() * randomPositionRange;
transform.localPosition = new Vector3(x, 0f, z); transform.localPosition += new Vector3(x, 0f, z);
} }
if (randomizeRotation){ if (randomizeRotation){

View File

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using DunGen;
namespace ScarletMansion.GamePatch.Props {
public class SpawnSyncedObjectCycle : MonoBehaviour, IDungeonCompleteReceiver {
public static int cycle;
public static Dictionary<int, int> cycleDictionary;
public SpawnSyncedObject spawn;
public int id;
public List<GameObject> props = new List<GameObject>();
void Reset(){
spawn = GetComponent<SpawnSyncedObject>();
}
public static void UpdateCycle(int value){
Plugin.logger.LogInfo($"Updating SpawnSyncedObject start cycle to {value}");
cycle = value;
cycleDictionary = new Dictionary<int, int>();
}
public int GetCycle(int id){
if (!cycleDictionary.TryGetValue(id, out var value)){
value = cycle;
cycleDictionary.Add(id, value);
}
cycleDictionary[id] = value + 1;
Plugin.logger.LogInfo($"Cycle{id}: {value}");
return value;
}
public void OnDungeonComplete(Dungeon dungeon) {
var index = GetCycle(id) % props.Count;
var prefab = props[index];
spawn.spawnPrefab = prefab;
}
}
}

View File

@ -50,11 +50,5 @@ namespace ScarletMansion.GamePatch {
return count * PluginConfig.Instance.mapHazardsMultiplierValue; return count * PluginConfig.Instance.mapHazardsMultiplierValue;
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(RoundManager), "SetPowerOffAtStart")]
public static void SetPowerOffAtStartPatch(){
DoorwayManager.onMainEntranceTeleportSpawnedEvent.Call();
}
} }
} }

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.GamePatch.Weathers {
public class SDMWeatherManager : MonoBehaviour {
[Header("Rainy")]
public GameObject rainGameObject;
public ParticleSystem rainParticleSystem;
public AudioSource rainAudioSource;
// fix itself
void Start() {
var timeofDay = TimeOfDay.Instance;
if (timeofDay == null) {
Plugin.logger.LogError("How is TimeOfDay null?");
return;
}
var rainEffect = timeofDay.effects.FirstOrDefault(x => x.name == "rainy");
var rainGameObject = rainEffect.effectObject;
Utility.FixParticleSystemMaterialAndChildren(rainParticleSystem, rainGameObject.GetComponentInChildren<ParticleSystem>());
rainAudioSource.clip = rainGameObject.GetComponentInChildren<AudioSource>().clip;
rainAudioSource.Play();
Plugin.logger.LogInfo("Fixed Rainy weather references");
}
}
}

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using DunGen; using DunGen;
using DunGen.Graph;
namespace ScarletMansion { namespace ScarletMansion {
@ -22,14 +23,12 @@ namespace ScarletMansion {
public List<GameObject> toFixGameObjects; public List<GameObject> toFixGameObjects;
public List<Item> items; public List<Item> items;
public List<Item> scrapItems; public List<Item> scrapItems;
public List<EnemyType> enemies;
[Header("DunGen")] [Header("DunGen")]
public TileSet mayorRegularTileset; public TileSet mayorRegularTileset;
public TileSet mayorVanillaTileset; public TileSet mayorVanillaTileset;
public DungeonArchetype hallwayEntranceArchetype; public GraphNode gardenEntranceGraphNode;
public List<DungeonArchetype> archetypes;
public List<TileSet> tilesets;
[Header("Main Prefabs")] [Header("Main Prefabs")]
public GameObject scarletNetworkManager; public GameObject scarletNetworkManager;
@ -37,5 +36,6 @@ namespace ScarletMansion {
[Header("Mis References")] [Header("Mis References")]
public Material ghostMaterial; public Material ghostMaterial;
public AudioClip sinkingAudioClip; public AudioClip sinkingAudioClip;
public GameObject yukariSpawnPrefab;
} }
} }

View File

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
namespace ScarletMansion.ModPatch {
public class AdvancedCompanyPatch : ModPatch {
public override string warningMessage => "AC compability will stay but if it breaks in the future, I'm not fixing it. Apologies.";
public AdvancedCompanyPatch(string guid) : base(guid) { }
public override void AddPatch() {
try {
var desiredTypeString = "AdvancedCompany.Game.Manager+Moons, AdvancedCompany";
var moonManagerType = System.Type.GetType(desiredTypeString);
var GetInsideEnemiesAC = moonManagerType.GetMethod("GetInsideEnemies", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
var GetLootTableAC = moonManagerType.GetMethod("GetLootTable", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
var GetScrapAmountAC = moonManagerType.GetMethod("GetScrapAmountModifier", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
GamePatch.LoadAssetsIntoLevelPatch.enemiesInjection.instructions.Add(new CodeInstruction(OpCodes.Call, GetInsideEnemiesAC));
//GamePatch.LoadAssetsIntoLevelPatch.itemsInjection.instructions.Add(new CodeInstruction(OpCodes.Call, GetLootTableAC));
GamePatch.RoundManagerPatch.scrapInjection.instructions.Add(new CodeInstruction(OpCodes.Call, GetScrapAmountAC));
} catch (System.Exception e) {
Plugin.logger.LogError("Failed to setup patching for Advanced Company. Custom enemies and items for SDM will not spawn, but bugs should not happen maybe possibly?");
Plugin.logger.LogError(e);
}
}
}
}

View File

@ -14,27 +14,27 @@ using ConfigEntryBundleInt = ScarletMansion.PluginConfig.ConfigEntryBundle<int>;
using ConfigEntryBundleFloat = ScarletMansion.PluginConfig.ConfigEntryBundle<float>; using ConfigEntryBundleFloat = ScarletMansion.PluginConfig.ConfigEntryBundle<float>;
using ConfigEntryBundleString = ScarletMansion.PluginConfig.ConfigEntryBundle<string>; using ConfigEntryBundleString = ScarletMansion.PluginConfig.ConfigEntryBundle<string>;
using ConfigEntryBundleBool = ScarletMansion.PluginConfig.ConfigEntryBundle<bool>; using ConfigEntryBundleBool = ScarletMansion.PluginConfig.ConfigEntryBundle<bool>;
using HarmonyLib;
using LethalConfig.MonoBehaviours.Components;
using UnityEngine.UI;
using LethalConfig.MonoBehaviours;
namespace ScarletMansion.ModPatch { namespace ScarletMansion.ModPatch {
public class LethalConfigPatch : ModPatch { public class LethalConfigPatch : ModPatch {
public const string section = "_Presets"; public const string section = "_Presets";
public const string descriptionPrefix = "These are a set of preset config values for your convience. Your config values will automatically update to these preset values every time SDM is loaded. To disable this feature, set this value to Custom."; public const string descriptionPrefix = "These are a set of preset config values for your convience. Your config values will automatically update to these preset values every time SDM is loaded. These config values are a brighter shade of red. To disable this feature, set this value to Custom.";
public const string requiresNewLobby = "Requires a lobby restart for the values to be updated."; public const string requiresNewLobby = "Requires a lobby restart for the values to be updated.";
public override string version => "1.4.2";
public LethalConfigPatch(string guid) : base(guid) { } public LethalConfigPatch(string guid) : base(guid) { }
public static void ForceUIUpdate(){ public static void ForceUIUpdate(){
try { try {
var desiredTypeString = "LethalConfig.MonoBehaviours.ConfigMenu, LethalConfig"; var configMenuObject = GameObject.FindObjectOfType<ConfigMenu>();
var configMenuType = Type.GetType(desiredTypeString); configMenuObject.Close(false);
var configMenuObject = GameObject.FindObjectOfType(configMenuType); configMenuObject.Open();
var closeFunction = configMenuType.GetMethod("Close", BindingFlags.Instance | BindingFlags.Public);
var openFunction = configMenuType.GetMethod("Open", BindingFlags.Instance | BindingFlags.Public);
closeFunction.Invoke(configMenuObject, new object[] {false} );
openFunction.Invoke(configMenuObject, new object[] {} );
} catch (Exception e) { } catch (Exception e) {
Plugin.logger.LogWarning("Could not force Lethal Config UI update"); Plugin.logger.LogWarning("Could not force Lethal Config UI update");
Plugin.logger.LogError(e.ToString()); Plugin.logger.LogError(e.ToString());
@ -151,6 +151,41 @@ namespace ScarletMansion.ModPatch {
CreatePresetConfig(PluginConfig.lcDungeonLightingPreset.config, PresetConfig.dungeonLightingChangeList); CreatePresetConfig(PluginConfig.lcDungeonLightingPreset.config, PresetConfig.dungeonLightingChangeList);
AutoGenerateConfigs(PluginConfig.lcDungeonGenerationPreset, PluginConfig.lcDungeonLightingPreset); AutoGenerateConfigs(PluginConfig.lcDungeonGenerationPreset, PluginConfig.lcDungeonLightingPreset);
try {
Plugin.Instance.harmony.PatchAll(typeof(LethalConfigPatch));
} catch (Exception e) {
Plugin.logger.LogWarning("Tried to HarmonyPatch LethalConfig but failed. Nothing bad will happen but report this bug to dev.");
Plugin.logger.LogError(e.ToString());
}
}
[HarmonyPatch(typeof(LethalConfig.MonoBehaviours.ConfigList), "LoadConfigsForMod")]
[HarmonyPostfix]
public static void LoadConfigsForModPatch(ref LethalConfig.MonoBehaviours.ConfigList __instance, ref LethalConfig.Mods.Mod mod) {
try {
if (mod.ModInfo.Guid == Plugin.modGUID) {
var genChanges = PresetConfig.defaultGeneration.GetAllConfigEntries();
var lightChanges = PresetConfig.defaultLighting.GetAllConfigEntries();
foreach(Transform child in __instance.listContainerObject.transform){
var controller = child.GetComponentInChildren<ModConfigController>();
if (controller == null) continue;
var config = controller.BaseConfigItem.BaseConfigEntry;
if (genChanges.Contains(config) || lightChanges.Contains(config)) {
foreach(var image in controller.GetComponentsInChildren<Graphic>()) {
image.color = Utility.BrightenColor(image.color, new Color(0.2f, 0.05f, 0.05f));
}
}
}
}
} catch (Exception e) {
Plugin.logger.LogWarning("Tried to change the label colors of LethalConfig but failed. Nothing bad will happen but report this bug to dev.");
Plugin.logger.LogError(e.ToString());
}
} }
} }

View File

@ -9,13 +9,13 @@ using BepInEx.Bootstrap;
namespace ScarletMansion.ModPatch { namespace ScarletMansion.ModPatch {
public class ModCompability { public class ModCompability {
public const string advancedCompanyGuid = "com.potatoepet.AdvancedCompany"; //public const string advancedCompanyGuid = "com.potatoepet.AdvancedCompany";
public const string lethalConfigGuid = "ainavt.lc.lethalconfig"; public const string lethalConfigGuid = "ainavt.lc.lethalconfig";
public const string facilityMeldownGuid = "me.loaforc.facilitymeltdown"; public const string facilityMeldownGuid = "me.loaforc.facilitymeltdown";
public const string reserveFlashlightGuid = "FlipMods.ReservedFlashlightSlot"; public const string reserveFlashlightGuid = "FlipMods.ReservedFlashlightSlot";
public static readonly ModPatch[] modPatches = new ModPatch[] { public static readonly ModPatch[] modPatches = new ModPatch[] {
new AdvancedCompanyPatch(advancedCompanyGuid), //new AdvancedCompanyPatch(advancedCompanyGuid),
new LethalConfigPatch(lethalConfigGuid), new LethalConfigPatch(lethalConfigGuid),
new FacilityMeltdownPatch(facilityMeldownGuid), new FacilityMeltdownPatch(facilityMeldownGuid),
new ReservedItemSlotPatch(reserveFlashlightGuid) new ReservedItemSlotPatch(reserveFlashlightGuid)

View File

@ -23,16 +23,17 @@ namespace ScarletMansion {
[BepInDependency("imabatby.lethallevelloader", "1.2.0.3")] [BepInDependency("imabatby.lethallevelloader", "1.2.0.3")]
[BepInDependency("evaisa.lethallib", "0.13.2")] [BepInDependency("evaisa.lethallib", "0.13.2")]
[BepInDependency("dev.ladyalice.dungenplus", "1.0.0")]
[BepInDependency(ModCompability.advancedCompanyGuid, BepInDependency.DependencyFlags.SoftDependency)] //[BepInDependency(ModCompability.advancedCompanyGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.lethalConfigGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.lethalConfigGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.facilityMeldownGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.facilityMeldownGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.reserveFlashlightGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.reserveFlashlightGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInProcess("Lethal Company.exe")] [BepInProcess("Lethal Company.exe")]
public class Plugin : BaseUnityPlugin { public class Plugin : BaseUnityPlugin {
public const string modGUID = "ImoutoSama.ScarletMansion"; public const string modGUID = "dev.ladyalice.scarletmansion";
private const string modName = "Scarlet Mansion"; private const string modName = "Scarlet Mansion";
private const string modVersion = "1.3.21"; private const string modVersion = "1.3.22";
public readonly Harmony harmony = new Harmony(modGUID); public readonly Harmony harmony = new Harmony(modGUID);
@ -66,10 +67,7 @@ namespace ScarletMansion {
harmony.PatchAll(typeof(ShotgunItemPatch)); harmony.PatchAll(typeof(ShotgunItemPatch));
harmony.PatchAll(typeof(ShovelPatch)); harmony.PatchAll(typeof(ShovelPatch));
//harmony.PatchAll(typeof(OptimizePatch));
harmony.PatchAll(typeof(DoorwayConnectionPatch));
harmony.PatchAll(typeof(GeneratePathPatch)); harmony.PatchAll(typeof(GeneratePathPatch));
harmony.PatchAll(typeof(PostProcessPatch));
harmony.PatchAll(typeof(PluginConfig)); harmony.PatchAll(typeof(PluginConfig));
@ -130,7 +128,10 @@ namespace ScarletMansion {
Assets.dungeonExtended = extendedDungeon; Assets.dungeonExtended = extendedDungeon;
extendedDungeon.DungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch); extendedDungeon.DungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch);
DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction); //DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction);
DunGenPlus.API.AddDunGenExtender(Assets.dungeon, Assets.dunGenExtender);
Assets.dunGenExtender.Events.OnModifyDunGenExtenderProperties.AddListener(DunGenPatch.Patch.UpdateDunGenExtenderProperties);
} }
void SetupForNetcodePatcher(){ void SetupForNetcodePatcher(){

View File

@ -253,20 +253,10 @@ namespace ScarletMansion {
new AcceptableValueRange<int>(0, 999) 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 float lootMultiplierValue;
public int crystalWeightValue; public int crystalWeightValue;
public int crystalBrokenWeightValue; public int crystalBrokenWeightValue;
public int snowGlobeWeightValue; public int snowGlobeWeightValue;
public int maidKnifeSelfDamageValue;
// enemies // enemies
public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>( public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>(

View File

@ -240,11 +240,20 @@ namespace ScarletMansion {
return str; return str;
} }
public HashSet<ConfigEntryBase> GetAllConfigEntries(){
var set = new HashSet<ConfigEntryBase>();
foreach(var a in changes.SelectMany(c => c.configBases)){
set.Add(a);
}
return set;
}
} }
public abstract class Change { public abstract class Change {
public abstract void Update(); public abstract void Update();
public abstract bool IsEqual(Change target); public abstract bool IsEqual(Change target);
public abstract IEnumerable<ConfigEntryBase> configBases { get; }
} }
public abstract class ChangeType<T> : Change { public abstract class ChangeType<T> : Change {
@ -252,6 +261,12 @@ namespace ScarletMansion {
public ConfigEntry<T> configEntry => configBundle.config; public ConfigEntry<T> configEntry => configBundle.config;
public T value; public T value;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return configEntry;
}
}
public ChangeType(PluginConfig.ConfigEntryBundle<T> configBundle){ public ChangeType(PluginConfig.ConfigEntryBundle<T> configBundle){
this.configBundle = configBundle; this.configBundle = configBundle;
this.value = configBundle.defaultValue; this.value = configBundle.defaultValue;
@ -296,6 +311,13 @@ namespace ScarletMansion {
public ChangeInt min; public ChangeInt min;
public ChangeInt max; public ChangeInt max;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return min.configEntry;
yield return max.configEntry;
}
}
public ChangeMinMaxInt(PluginConfig.ConfigEntryBundleMinMax<int> configBundle){ public ChangeMinMaxInt(PluginConfig.ConfigEntryBundleMinMax<int> configBundle){
min = new ChangeInt(configBundle.min); min = new ChangeInt(configBundle.min);
max = new ChangeInt(configBundle.max); max = new ChangeInt(configBundle.max);
@ -326,6 +348,13 @@ namespace ScarletMansion {
public ChangeFloat min; public ChangeFloat min;
public ChangeFloat max; public ChangeFloat max;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return min.configEntry;
yield return max.configEntry;
}
}
public ChangeMinMaxFloat(PluginConfig.ConfigEntryBundleMinMax<float> configBundle){ public ChangeMinMaxFloat(PluginConfig.ConfigEntryBundleMinMax<float> configBundle){
min = new ChangeFloat(configBundle.min); min = new ChangeFloat(configBundle.min);
max = new ChangeFloat(configBundle.max); max = new ChangeFloat(configBundle.max);
@ -356,6 +385,13 @@ namespace ScarletMansion {
public ChangeMinMaxInt count; public ChangeMinMaxInt count;
public ChangeMinMaxInt depth; public ChangeMinMaxInt depth;
public override IEnumerable<ConfigEntryBase> configBases {
get {
foreach(var a in count.configBases) yield return a;
foreach(var a in depth.configBases) yield return a;
}
}
public ChangeBranchingPath(PluginConfig.ConfigEntryBundleBranchingPath configBundle){ public ChangeBranchingPath(PluginConfig.ConfigEntryBundleBranchingPath configBundle){
count = new ChangeMinMaxInt(configBundle.count); count = new ChangeMinMaxInt(configBundle.count);
depth = new ChangeMinMaxInt(configBundle.depth); depth = new ChangeMinMaxInt(configBundle.depth);

View File

@ -37,9 +37,6 @@
<Reference Include="AdvancedCompany"> <Reference Include="AdvancedCompany">
<HintPath>..\..\..\Libraries\AdvancedCompany.dll</HintPath> <HintPath>..\..\..\Libraries\AdvancedCompany.dll</HintPath>
</Reference> </Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\Libraries\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass"> <Reference Include="Assembly-CSharp-firstpass">
<HintPath>..\..\..\Libraries\Assembly-CSharp-firstpass.dll</HintPath> <HintPath>..\..\..\Libraries\Assembly-CSharp-firstpass.dll</HintPath>
</Reference> </Reference>
@ -52,11 +49,14 @@
<Reference Include="BepInEx.Harmony"> <Reference Include="BepInEx.Harmony">
<HintPath>..\..\..\Libraries\BepInEx.Harmony.dll</HintPath> <HintPath>..\..\..\Libraries\BepInEx.Harmony.dll</HintPath>
</Reference> </Reference>
<Reference Include="DunGenPlus">
<HintPath>..\..\..\Libraries\DunGenPlus.dll</HintPath>
</Reference>
<Reference Include="FacilityMeltdown"> <Reference Include="FacilityMeltdown">
<HintPath>..\..\..\Libraries\FacilityMeltdown.dll</HintPath> <HintPath>..\..\..\Libraries\FacilityMeltdown.dll</HintPath>
</Reference> </Reference>
<Reference Include="LethalConfig"> <Reference Include="LethalConfig-publicized">
<HintPath>..\..\..\Libraries\LethalConfig.dll</HintPath> <HintPath>..\..\..\Libraries\LethalConfig-publicized.dll</HintPath>
</Reference> </Reference>
<Reference Include="LethalLevelLoader"> <Reference Include="LethalLevelLoader">
<HintPath>..\..\..\Libraries\LethalLevelLoader.dll</HintPath> <HintPath>..\..\..\Libraries\LethalLevelLoader.dll</HintPath>
@ -150,33 +150,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Assets.cs" /> <Compile Include="Assets.cs" />
<Compile Include="DunGenAnalyis.cs" />
<Compile Include="DunGenPatch\Components\DoorwayConnectionSisterChain.cs" />
<Compile Include="DunGenPatch\Components\DoorwayConnectionSisterRuleInfo.cs" />
<Compile Include="DunGenPatch\Components\MainRoomDoorwayGroups.cs" />
<Compile Include="DunGenPatch\Components\RemoveConnectorIfConnectedDoorwayBasic.cs" />
<Compile Include="DunGenPatch\Components\RemoveGameObjectsBasedOnCBSelected.cs" />
<Compile Include="DunGenPatch\Components\RemoveGameObjectsBasedOnConnectedDoorway.cs" />
<Compile Include="DunGenPatch\Components\SwitchConnectorBlockerBasedOnCBSelected.cs" />
<Compile Include="DunGenPatch\DoorwayConnectionPatch.cs" />
<Compile Include="DunGenPatch\DoorwayConnectionSisterRule.cs" />
<Compile Include="DunGenPatch\Doorways\DCleanBase.cs" />
<Compile Include="DunGenPatch\Doorways\DCleanConnectorBlockerBasedOnSelected.cs" />
<Compile Include="DunGenPatch\Doorways\DCleanDoorwayCompare.cs" />
<Compile Include="DunGenPatch\Doorways\DCleanRemoveDoorwayBasedOnConnectedDoor.cs" />
<Compile Include="DunGenPatch\Doorways\DCleanRemoveDoorwayBasedOnSelected.cs" />
<Compile Include="DunGenPatch\Doorways\DCleanRemoveGameObjectsBasedOnConnectedDoor.cs" />
<Compile Include="DunGenPatch\Doorways\DoorwayCleanup.cs" />
<Compile Include="DunGenPatch\GeneratePath.cs" />
<Compile Include="DunGenPatch\GeneratePathPatch.cs" /> <Compile Include="DunGenPatch\GeneratePathPatch.cs" />
<Compile Include="DunGenPatch\OptimizePatch.cs" />
<Compile Include="DunGenPatch\Patch.cs" /> <Compile Include="DunGenPatch\Patch.cs" />
<Compile Include="DunGenPatch\PostProcessPatch.cs" />
<Compile Include="GamePatch\Components\FloorCleanup.cs" /> <Compile Include="GamePatch\Components\FloorCleanup.cs" />
<Compile Include="GamePatch\Components\FloorCleanUpParent.cs" /> <Compile Include="GamePatch\Components\FloorCleanUpParent.cs" />
<Compile Include="GamePatch\Components\KnightSpawnPoint.cs" /> <Compile Include="GamePatch\Components\KnightSpawnPoint.cs" />
<Compile Include="GamePatch\Components\Lights\ScarletLight.cs" /> <Compile Include="GamePatch\Components\Lights\ScarletLight.cs" />
<Compile Include="GamePatch\Components\Lights\ScarletLightCleanup.cs" /> <Compile Include="GamePatch\Components\Lights\ScarletLightCleanup.cs" />
<Compile Include="GamePatch\Components\PathOpenup.cs" />
<Compile Include="GamePatch\Components\PathOpenUpParent.cs" />
<Compile Include="GamePatch\Components\ScarletBedroom.cs" /> <Compile Include="GamePatch\Components\ScarletBedroom.cs" />
<Compile Include="GamePatch\Components\ScarletClock.cs" /> <Compile Include="GamePatch\Components\ScarletClock.cs" />
<Compile Include="GamePatch\Components\ScarletDoor.cs" /> <Compile Include="GamePatch\Components\ScarletDoor.cs" />
@ -187,9 +169,12 @@
<Compile Include="GamePatch\Components\ScarletLighting.cs" /> <Compile Include="GamePatch\Components\ScarletLighting.cs" />
<Compile Include="GamePatch\Components\ScarletPlayerControllerB.cs" /> <Compile Include="GamePatch\Components\ScarletPlayerControllerB.cs" />
<Compile Include="GamePatch\Components\ScarletProp.cs" /> <Compile Include="GamePatch\Components\ScarletProp.cs" />
<Compile Include="GamePatch\Components\ScarletRadio.cs" />
<Compile Include="GamePatch\Components\ScarletVent.cs" /> <Compile Include="GamePatch\Components\ScarletVent.cs" />
<Compile Include="GamePatch\Components\ScarletYukariTrigger.cs" /> <Compile Include="GamePatch\Components\ScarletYukariTrigger.cs" />
<Compile Include="GamePatch\Components\TreasureRoom\TreasureRoomTimedOpen.cs" />
<Compile Include="GamePatch\DoorLockPatch.cs" /> <Compile Include="GamePatch\DoorLockPatch.cs" />
<Compile Include="GamePatch\Enemies\KnightGhostVariant.cs" />
<Compile Include="GamePatch\Enemies\KnightVariant.cs" /> <Compile Include="GamePatch\Enemies\KnightVariant.cs" />
<Compile Include="GamePatch\Enemies\MaidVariant.cs" /> <Compile Include="GamePatch\Enemies\MaidVariant.cs" />
<Compile Include="GamePatch\EnemyVentPatch.cs" /> <Compile Include="GamePatch\EnemyVentPatch.cs" />
@ -204,7 +189,6 @@
<Compile Include="GamePatch\InitPatch.cs" /> <Compile Include="GamePatch\InitPatch.cs" />
<Compile Include="GamePatch\Items\FlandreCrystal.cs" /> <Compile Include="GamePatch\Items\FlandreCrystal.cs" />
<Compile Include="GamePatch\Items\IScarletItem.cs" /> <Compile Include="GamePatch\Items\IScarletItem.cs" />
<Compile Include="GamePatch\Items\PlayerAnimatorStateHelper.cs" />
<Compile Include="GamePatch\Items\ScarletFlashlight.cs" /> <Compile Include="GamePatch\Items\ScarletFlashlight.cs" />
<Compile Include="GamePatch\Items\ScarletKnife.cs" /> <Compile Include="GamePatch\Items\ScarletKnife.cs" />
<Compile Include="GamePatch\Items\ScarletPainting.cs" /> <Compile Include="GamePatch\Items\ScarletPainting.cs" />
@ -212,7 +196,6 @@
<Compile Include="GamePatch\JesterAIPatch.cs" /> <Compile Include="GamePatch\JesterAIPatch.cs" />
<Compile Include="GamePatch\LoadAssetsIntoLevelPatch.cs" /> <Compile Include="GamePatch\LoadAssetsIntoLevelPatch.cs" />
<Compile Include="GamePatch\Managers\AngerManager.cs" /> <Compile Include="GamePatch\Managers\AngerManager.cs" />
<Compile Include="GamePatch\Managers\DoorwayManager.cs" />
<Compile Include="GamePatch\Managers\KnightSpawnManager.cs" /> <Compile Include="GamePatch\Managers\KnightSpawnManager.cs" />
<Compile Include="GamePatch\Managers\ScarletLightingManager.cs" /> <Compile Include="GamePatch\Managers\ScarletLightingManager.cs" />
<Compile Include="GamePatch\Managers\ScarletNetworkManager.cs" /> <Compile Include="GamePatch\Managers\ScarletNetworkManager.cs" />
@ -227,14 +210,13 @@
<Compile Include="GamePatch\Props\RandomPrefabBasicBase.cs" /> <Compile Include="GamePatch\Props\RandomPrefabBasicBase.cs" />
<Compile Include="GamePatch\Props\RandomPrefabCycle.cs" /> <Compile Include="GamePatch\Props\RandomPrefabCycle.cs" />
<Compile Include="GamePatch\Props\RandomPrefabWithScale.cs" /> <Compile Include="GamePatch\Props\RandomPrefabWithScale.cs" />
<Compile Include="GamePatch\Props\SpawnSyncedObjectCycle.cs" />
<Compile Include="GamePatch\RoundManagerPatch.cs" /> <Compile Include="GamePatch\RoundManagerPatch.cs" />
<Compile Include="GamePatch\ScarletLightPatch.cs" /> <Compile Include="GamePatch\ScarletLightPatch.cs" />
<Compile Include="GamePatch\ShotgunItemPatch.cs" /> <Compile Include="GamePatch\ShotgunItemPatch.cs" />
<Compile Include="GamePatch\ShovelPatch.cs" /> <Compile Include="GamePatch\ShovelPatch.cs" />
<Compile Include="GamePatch\Weathers\SDMWeatherManager.cs" />
<Compile Include="LoadingPatch\NetworkObjectListScriptableObject.cs" /> <Compile Include="LoadingPatch\NetworkObjectListScriptableObject.cs" />
<Compile Include="MainMenuUpdate.cs" /> <Compile Include="MainMenuUpdate.cs" />
<Compile Include="ModPatch\AdvancedCompanyPatch.cs" />
<Compile Include="ModPatch\FacilityMeltdownPatch.cs" /> <Compile Include="ModPatch\FacilityMeltdownPatch.cs" />
<Compile Include="ModPatch\LethalConfigPatch.cs" /> <Compile Include="ModPatch\LethalConfigPatch.cs" />
<Compile Include="ModPatch\ModCompability.cs" /> <Compile Include="ModPatch\ModCompability.cs" />

View File

@ -83,6 +83,10 @@ namespace ScarletMansion {
seq.Add((i) => i.opcode == opcode && i.operand == operand); seq.Add((i) => i.opcode == opcode && i.operand == operand);
} }
public void AddBasicLocal(OpCode opcode, int operand){
seq.Add((i) => i.opcode == opcode && (i.operand as LocalBuilder).LocalIndex == operand);
}
public void AddOperandTypeCheck(OpCode opcode, Type operandType){ public void AddOperandTypeCheck(OpCode opcode, Type operandType){
seq.Add((i) => { seq.Add((i) => {
var fieldInfo = i.operand as FieldInfo; var fieldInfo = i.operand as FieldInfo;
@ -117,6 +121,7 @@ namespace ScarletMansion {
public bool VerifyStage(CodeInstruction current){ public bool VerifyStage(CodeInstruction current){
var s = seq[stage]; var s = seq[stage];
if (s.Invoke(current)) { if (s.Invoke(current)) {
//Plugin.logger.LogInfo($"{name}({stage}): current.ToString()");
stage++; stage++;
} else { } else {
stage = 0; stage = 0;

View File

@ -211,6 +211,16 @@ namespace ScarletMansion {
} }
} }
public static (int totalMinutes, int hours, int minutes) GetTime(){
var timeOfDay = TimeOfDay.Instance;
if (timeOfDay == null) return (0, 0, 0);
var totalMinutes = (int)(timeOfDay.normalizedTimeOfDay * (60f * timeOfDay.numberOfHours)) + 360;
var hours = Mathf.FloorToInt(totalMinutes / 60f);
var minutes = totalMinutes % 60;
return (totalMinutes, hours, minutes);
}
public static List<SpawnableItemWithRarity> GetDungeonItems(){ public static List<SpawnableItemWithRarity> GetDungeonItems(){
return LoadAssetsIntoLevelPatch.GetItems(RoundManager.Instance.currentLevel.spawnableScrap); return LoadAssetsIntoLevelPatch.GetItems(RoundManager.Instance.currentLevel.spawnableScrap);
} }
@ -312,6 +322,34 @@ namespace ScarletMansion {
return method; return method;
} }
public static Color BrightenColor(Color color, float amount) {
var diff = (Color.white - color) * amount;
return new Color(color.r + diff.r, color.g + diff.g, color.b + diff.b, color.a);
}
public static Color BrightenColor(Color color, Color amount) {
var diff = (Color.white - color) * amount;
return new Color(color.r + diff.r, color.g + diff.g, color.b + diff.b, color.a);
}
public static void FixParticleSystemMaterial(ParticleSystemRenderer toFixParticleSystem, ParticleSystemRenderer copyParticleSystem) {
toFixParticleSystem.sharedMaterial = copyParticleSystem.sharedMaterial;
}
public static void FixParticleSystemMaterial(ParticleSystem toFixParticleSystem, ParticleSystem copyParticleSystem) {
FixParticleSystemMaterial(toFixParticleSystem.GetComponent<ParticleSystemRenderer>(), copyParticleSystem.GetComponent<ParticleSystemRenderer>());
}
public 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 GameObject GetParentWithNetworkObject(GameObject g){ public static GameObject GetParentWithNetworkObject(GameObject g){

View File

@ -36,7 +36,7 @@ namespace ScarletMansionMimicsPatch {
public override bool IsMyInteriorLoaded => ScarletMansion.DunGenPatch.Patch.active; public override bool IsMyInteriorLoaded => ScarletMansion.DunGenPatch.Patch.active;
public override void OnMimicCreated(MimicDoor mimicDoor, Doorway doorway) { public override void OnMimicCreated(MimicDoor mimicDoor, Doorway doorway) {
var c = doorway.transform.parent.GetComponentInChildren<ScarletMansion.DunGenPatch.Doorways.DoorwayCleanup>(); var c = doorway.transform.parent.GetComponentInChildren<DunGenPlus.Components.DoorwayCleanup>();
if (c != null) { if (c != null) {
c.overrideConnector = true; c.overrideConnector = true;
c.overrideNoDoorway = true; c.overrideNoDoorway = true;

View File

@ -19,12 +19,12 @@ namespace ScarletMansionMimicsPatch {
[BepInPlugin(modGUID, modName, modVersion)] [BepInPlugin(modGUID, modName, modVersion)]
[BepInDependency("ImoutoSama.ScarletMansion", "1.3.12")] [BepInDependency("dev.ladyalice.scarletmansion", "1.3.22")]
[BepInDependency(targetModGUID, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(targetModGUID, BepInDependency.DependencyFlags.SoftDependency)]
public class Plugin : BaseUnityPlugin { public class Plugin : BaseUnityPlugin {
public const string modGUID = "ImoutoSama.ScarletMansionMimicsPatch"; public const string modGUID = "dev.ladyalice.scarletmansion.mimicspatch";
private const string modName = "Scarlet Mansion Mimics Patch"; private const string modName = "Scarlet Mansion Mimics Patch";
private const string modVersion = "1.0.0"; private const string modVersion = "1.0.0";

View File

@ -40,6 +40,9 @@
<Reference Include="BepInEx"> <Reference Include="BepInEx">
<HintPath>..\..\..\Libraries\BepInEx.dll</HintPath> <HintPath>..\..\..\Libraries\BepInEx.dll</HintPath>
</Reference> </Reference>
<Reference Include="DunGenPlus">
<HintPath>..\..\..\Libraries\DunGenPlus.dll</HintPath>
</Reference>
<Reference Include="Mimics"> <Reference Include="Mimics">
<HintPath>..\..\..\Libraries\Mimics.dll</HintPath> <HintPath>..\..\..\Libraries\Mimics.dll</HintPath>
</Reference> </Reference>