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 ScarletMansion.GamePatch.Items;
using static ScarletMansion.Assets;
using DunGenPlus;
namespace ScarletMansion {
public static class Assets {
@ -26,6 +27,7 @@ namespace ScarletMansion {
// main assets
public static AssetBundle MainAssetBundle = null;
public static DungeonFlow dungeon;
public static DunGenExtender dunGenExtender;
public static NetworkObjectListScriptableObject networkObjectList;
public static AudioClip entranceAudioClip;
@ -33,11 +35,6 @@ namespace ScarletMansion {
public static ExtendedMod extendedMod;
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
public class Enemy {
@ -187,6 +184,7 @@ namespace ScarletMansion {
dungeon = Load<DungeonFlow>("SDMLevel");
networkObjectList = Load<NetworkObjectListScriptableObject>("SDMList");
dunGenExtender = Load<DunGenExtender>("DunGenExtender");
entranceAudioClip = Load<AudioClip>("entrance_ogg");
knight = new Enemy(
@ -232,6 +230,11 @@ namespace ScarletMansion {
globalItems.Add(flashlight);
globalItems.Add(flashlightBB);
foreach(var e in networkObjectList.enemies) {
Enemies.RegisterEnemy(e, 0, Levels.LevelTypes.None, null, null);
NetworkPrefabs.RegisterNetworkPrefab(e.enemyPrefab);
}
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 DunGen;
using LethalLevelLoader;
using DunGen.Graph;
namespace ScarletMansion.DunGenPatch {
@ -20,10 +21,8 @@ namespace ScarletMansion.DunGenPatch {
[HarmonyPatch(typeof(RoundManager), "GenerateNewFloor")]
[HarmonyPrefix]
public static void DungeonGeneratorGenerate_PrefixPrefix(){
// safety check
Plugin.logger.LogInfo("Disabling SDM logic");
Patch.Deactivate();
//Patch.ActivateAnalysis();
}
public static void GeneratePatch(RoundManager roundManager){
@ -31,91 +30,6 @@ namespace ScarletMansion.DunGenPatch {
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 UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using DunGenPlus.Collections;
using GameNetcodeStuff;
using ScarletMansion.GamePatch.Components;
namespace ScarletMansion.DunGenPatch {
public static class Patch {
@ -18,102 +21,70 @@ namespace ScarletMansion.DunGenPatch {
public static bool callAlternative;
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){
active = true;
callAlternative = true;
generatorInstance = generator;
//startAnalysis = true;
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;
Plugin.logger.LogInfo($"Length of main path be: {GetLength(mainPathLength, scale)}");
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){
return $"({range.Min * multi} - {range.Max * multi})";
}
public static void Deactivate(){
public static void Deactivate(bool ignoreScarletPlayer = false){
active = false;
callAlternative = false;
generatorInstance = null;
GamePatch.JesterAIPatch.active = false;
if (previousHDRPAsset && QualitySettings.renderPipeline == sdmHDRPAsset) {
Plugin.logger.LogInfo("Restoring original HDRP asset");
if (ignoreScarletPlayer) return;
QualitySettings.renderPipeline = previousHDRPAsset;
previousHDRPAsset = null;
sdmHDRPAsset = null;
var localPlayer = StartOfRound.Instance.localPlayerController;
var scarletPlayer = ScarletPlayerControllerB.GetScarletPlayerScript(localPlayer);
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;
try {
var enemy = bonusEnemy.enemy;
var enemyIndex = bonusEnemy.index;
if (enemyIndex > -1){
if (bonusEnemy.index > -1){
var pos = vent.transform.position;
var dir = target.transform.position - pos;
dir.y = 0f;
@ -196,7 +194,7 @@ namespace ScarletMansion.GamePatch.Components {
bonusEnemy.ApplySpawnLogic();
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());
}
@ -223,6 +221,7 @@ namespace ScarletMansion.GamePatch.Components {
public void OnDungeonComplete(Dungeon dungeon) {
AngerManager.Instance.AddBedroom(this);
AngerManager.Instance.AddRoomOfInterest(transform);
var parent = GetComponentInParent<Tile>();
lights = parent.GetComponentsInChildren<Light>();

View File

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

View File

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

View File

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

View File

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

View File

@ -9,14 +9,47 @@ using GameNetcodeStuff;
namespace ScarletMansion.GamePatch.Components {
public class ScarletPlayerControllerB : MonoBehaviour {
public static Dictionary<PlayerControllerB, ScarletPlayerControllerB> playerControllers;
// self
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){
this.player = player;
CreateHelmetForFlashlight(player, Assets.flashlight, 0);
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){
try {
var helmetLights = player.allHelmetLights.ToList();

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;
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 (!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 EnemyType knightGhostEnemy;
public AudioSource chaseMusicStart;
// Token: 0x04000111 RID: 273
@ -265,6 +267,9 @@ namespace ScarletMansion.GamePatch.Enemies {
knifeRender.SetActive(false);
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);
newKnife.GetComponent<NetworkObject>().Spawn();
newKnife.GetComponent<ScarletKnife>().LinkKnifeToMaidServerRpc(this);

View File

@ -48,19 +48,12 @@ namespace ScarletMansion.GamePatch {
[HarmonyPrefix]
public static void StartOfRound_Start(ref StartOfRound __instance) {
ScarletYukariTrigger.audioClipIndex = -1;
PlayerAnimatorStateHelper.ClearAnimators();
ScarletPlayerControllerB.InitializeScarletScripts();
__instance.StartCoroutine(WaitForNetworkObject(__instance, CreateNetworkManager));
/*
foreach(var p in __instance.levels){
var totalScrap = p.spawnableScrap.Sum(s => s.rarity);
var totalEnemy = p.Enemies.Sum(s => s.rarity);
Plugin.logger.LogInfo($"{p.PlanetName}: S{totalScrap}, E{totalEnemy}");
}*/
// safety cleaning
DunGenPatch.Patch.Deactivate();
DunGenPatch.Patch.Deactivate(true);
//ScarletLightingManager.Clean();
FixMapReferences(__instance);
@ -72,28 +65,12 @@ namespace ScarletMansion.GamePatch {
statusEffectClips.Add(Assets.networkObjectList.sinkingAudioClip);
__instance.statusEffectClips = statusEffectClips.ToArray();
// DunGenAnalyis.Analysis(Assets.dungeon, __instance, RoundManager.Instance);
}
[HarmonyPatch(typeof(RoundManager), "Awake")]
[HarmonyPrefix]
public static void RoundManagerAwakePatch(ref RoundManager __instance) {
FixDungeonPrefabValues(__instance);
/*
foreach(var d in __instance.dungeonFlowTypes) {
Plugin.logger.LogInfo("--------");
Plugin.logger.LogInfo($"{d.name}: {d.Length.ToString()}");
Plugin.logger.LogInfo($"{d.BranchMode}: {d.BranchCount.ToString()}");
foreach(var l in d.Lines){
Plugin.logger.LogInfo($" {l.Position}, {l.Length}");
foreach(var a in l.DungeonArchetypes){
Plugin.logger.LogInfo($" {a.BranchCount.ToString()}, {a.BranchingDepth.ToString()}");
}
}
Plugin.logger.LogInfo("--------\n");
}
*/
}
[HarmonyPatch(typeof(RoundManager), "Start")]
@ -110,24 +87,6 @@ namespace ScarletMansion.GamePatch {
return true;
}
private static void FixParticleSystemMaterial(ParticleSystemRenderer toFixParticleSystem, ParticleSystemRenderer copyParticleSystem) {
toFixParticleSystem.sharedMaterial = copyParticleSystem.sharedMaterial;
}
private static void FixParticleSystemMaterial(ParticleSystem toFixParticleSystem, ParticleSystem copyParticleSystem) {
FixParticleSystemMaterial(toFixParticleSystem.GetComponent<ParticleSystemRenderer>(), copyParticleSystem.GetComponent<ParticleSystemRenderer>());
}
private static void FixParticleSystemMaterialAndChildren(ParticleSystem toFixParticleSystem, ParticleSystem copyParticleSystem) {
FixParticleSystemMaterial(toFixParticleSystem, copyParticleSystem);
for(var i = 0; i < toFixParticleSystem.transform.childCount && i < copyParticleSystem.transform.childCount; ++i) {
var child0 = toFixParticleSystem.transform.GetChild(i).GetComponent<ParticleSystemRenderer>();
var child1 = copyParticleSystem.transform.GetChild(i).GetComponent<ParticleSystemRenderer>();
FixParticleSystemMaterial(child0, child1);
}
}
public static void FixMapReferences(StartOfRound round){
try {
@ -188,7 +147,7 @@ namespace ScarletMansion.GamePatch {
var butlerPrefab = butlerItem.enemyType.enemyPrefab;
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);
}
@ -300,8 +259,8 @@ namespace ScarletMansion.GamePatch {
scarletScript.hitSFX = knifeScript.hitSFX;
scarletScript.swingSFX = knifeScript.swingSFX;
FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle);
FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle);
Utility.FixParticleSystemMaterialAndChildren(scarletScript.bloodParticle, knifeScript.bloodParticle);
Utility.FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle);
}
if (Assets.flashlight.item == null) {
@ -347,12 +306,6 @@ namespace ScarletMansion.GamePatch {
var prefabs = networkmanager.NetworkConfig.Prefabs.Prefabs;
var prefabFixList = Assets.networkObjectList.toFixGameObjects;
//foreach(var p in prefabs){
// var hasInteract = p.Prefab.GetComponentInChildren<InteractTrigger>();
// var interStr = hasInteract ? "YES" : "";
// Plugin.logger.LogInfo($"{p.Prefab.name}: {interStr}");
//}
var steeldoor = prefabs.FirstOrDefault(p => p.Prefab.name.ToLowerInvariant() == "fancydoormapmodel");
if (GameReadNullCheck(steeldoor, "FancyDoorMapModel", "SDM doors will have missing icons and sounds")){
var interact = steeldoor.Prefab.GetComponentInChildren<InteractTrigger>();
@ -404,61 +357,19 @@ namespace ScarletMansion.GamePatch {
}
private static IEnumerator UpdateNetworkConfig(RoundManager roundManager){
while(PluginConfig.Synced == false){
yield return null;
}
/*
var list = ExtendedDungeonMapLoad.GetCustomMoons(PluginConfig.Instance.customMapsValue, PluginConfig.Instance.customMapDefaultWeightValue);
ExtendedDungeonMapLoad.ClearLastCustomMoonEntryList();
// overriding with dis
if (list.Count > 0){
Plugin.logger.LogInfo("Overring default snow moons. Loading with custom moon list");
ExtendedDungeonMapLoad.AddToExtendedDungeonFlow(list);
}
else {
Plugin.logger.LogInfo("Loading SDM to default snow moons");
Assets.dineEntry.SetRarity(PluginConfig.Instance.dungeonSnowWeightValue);
Assets.rendEntry.SetRarity(PluginConfig.Instance.dungeonSnowWeightValue);
Assets.titanEntry.SetRarity(PluginConfig.Instance.dungeonTitanWeightValue);
var dun = Assets.dungeonExtended;
Assets.dineEntry.Add(dun);
Assets.rendEntry.Add(dun);
Assets.titanEntry.Add(dun);
}
*/
Lights.ScarletLightCleanup.weights = new float[] {
PluginConfig.Instance.lightsSpawnZeroWeightValue,
PluginConfig.Instance.lightsSpawnOneWeightValue,
PluginConfig.Instance.lightsSpawnTwoWeightValue
//PluginConfig.Instance.lightsSpawnThreeWeightValue
};
/*
foreach(var item in Assets.items){
item.UpdateStringWithRarity();
}
// hack
foreach(var level in PatchedContent.ExtendedLevels) {
Plugin.logger.LogInfo($"{level.name}: {level.AuthorName}");
var hackCall = typeof(ItemManager).GetMethod("InjectCustomItemsIntoLevelViaDynamicRarity", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
hackCall.Invoke(null, new object[] { level, true } );
}
*/
//Assets.dungeonExtended.DynamicDungeonSizeMinMax = new Vector2(PluginConfig.Instance.dunGenMultiplierValue.min, PluginConfig.Instance.dunGenMultiplierValue.max);
Assets.dungeon.Length = PluginConfig.Instance.mainPathLengthValue.GetDungenIntRange();
var mayorTileSet = PluginConfig.Instance.disableBasementValue ? Assets.networkObjectList.mayorVanillaTileset : Assets.networkObjectList.mayorRegularTileset;
Assets.dungeon.Nodes[1].TileSets = new List<DunGen.TileSet>() { mayorTileSet };
DungeonFlow.GlobalPropSettings GetGlobalPropSetting(int id) {
foreach(var p in Assets.dungeon.GlobalProps){
if (p.ID == id ) return p;
@ -474,19 +385,5 @@ namespace ScarletMansion.GamePatch {
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 GameNetcodeStuff;
using OdinSerializer;
using ScarletMansion.GamePatch.Components;
using ScarletMansion.GamePatch.Enemies;
using Unity.Netcode;
using UnityEngine;
@ -119,9 +120,20 @@ namespace ScarletMansion.GamePatch.Items
// buff
if (buffed) {
IHittable target;
int damage;
// prevent the player from one-shotting their friends
var target = component is PlayerControllerB ? previousPlayerHeldBy as IHittable : component;
target.Hit(50, forward, previousPlayerHeldBy, playHitSFX: true);
var targetPlayer = component as PlayerControllerB;
if (targetPlayer != null) {
target = previousPlayerHeldBy;
damage = GetDamageToPlayer(previousPlayerHeldBy);
} else {
target = component;
damage = 40;
}
target.Hit(damage, forward, previousPlayerHeldBy, playHitSFX: true);
UnBuffShovelServerRpc();
}
// 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]
public void HitShovelServerRpc(int hitSurfaceID) {
HitShovelClientRpc(hitSurfaceID);
@ -187,9 +208,8 @@ namespace ScarletMansion.GamePatch.Items
player.activatingItem = true;
yield return new WaitForSeconds(0.25f);
var dmg = PluginConfig.Instance.maidKnifeSelfDamageValue;
var realDmg = Mathf.Max(dmg, Mathf.RoundToInt(player.health * (dmg / 100f)));
player.DamagePlayer(realDmg, true, true, CauseOfDeath.Stabbing);
var damage = GetDamageToPlayer(player);
player.DamagePlayer(damage, true, true, CauseOfDeath.Stabbing);
bloodParticle.Play(withChildren: true);
RoundManager.PlayRandomClip(knifeAudio, hitSFX);

View File

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

View File

@ -85,6 +85,11 @@ namespace ScarletMansion.GamePatch {
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;

View File

@ -23,7 +23,10 @@ namespace ScarletMansion.GamePatch.Managers {
//public Dictionary<EnemyAI, int> angeredEnemies;
public int level;
public List<Transform> roomsOfInterest;
public List<ScarletBedroom> bedrooms;
public List<ScarletDoor> doors;
public List<ScarletLight> lights;
@ -32,6 +35,8 @@ namespace ScarletMansion.GamePatch.Managers {
void Awake(){
Instance = this;
roomsOfInterest = new List<Transform>();
bedrooms = new List<ScarletBedroom>();
doors = new List<ScarletDoor>();
lights = new List<ScarletLight>();
@ -91,10 +96,14 @@ namespace ScarletMansion.GamePatch.Managers {
bedrooms.Add(b);
}
public void AddRoomOfInterest(Transform t) {
roomsOfInterest.Add(t);
}
public void AddDoor(ScarletDoor d){
// I want to get only doors that are revelant
foreach(var b in bedrooms){
var dist = Vector3.SqrMagnitude(d.transform.position - b.transform.position);
foreach(var t in roomsOfInterest){
var dist = Vector3.SqrMagnitude(d.transform.position - t.position);
if (dist < 16f * 16f){
doors.Add(d);
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) {
// IDK KNOW IF I CAN TRUST THIS TO BE THE SAME FOR ALL CLIENTS
// but probably
var points = dungeon.GetComponentsInChildren<KnightSpawnPoint>();
spawnPoints = points.ToList();
for(var i = 0; i < spawnPoints.Count; ++i){

View File

@ -10,6 +10,7 @@ using GameNetcodeStuff;
using ScarletMansion.GamePatch.Items;
using ScarletMansion.GamePatch;
using ScarletMansion.GamePatch.Components;
using static LethalLevelLoader.ExtendedEvent;
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 int GetCriticalDamageToPlayer(PlayerControllerB player, bool forceKill) {
if (player.health > 20 && !forceKill) {
return player.health - 15;
}
return player.health;
}
public static int GetFlashlightId(FlashlightItem flashlightItem){
var flashlight = Assets.GetFlashlight(flashlightItem.itemProperties);
if (flashlight != null) return flashlight.itemId;
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){
var color = crystal.colorIndex;
var position = crystal.transform.position + Vector3.up * 0.25f;

View File

@ -14,14 +14,26 @@ namespace ScarletMansion.GamePatch {
[HarmonyPatch(typeof(PlayerControllerB), "Awake")]
[HarmonyPrefix]
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}");
var comp = __instance.gameObject.AddComponent<ScarletPlayerControllerB>();
comp.Initialize(__instance);
comp.Register();
} else {
currentComp.Register();
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){
var x = (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){

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;
}
[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 UnityEngine;
using DunGen;
using DunGen.Graph;
namespace ScarletMansion {
@ -22,14 +23,12 @@ namespace ScarletMansion {
public List<GameObject> toFixGameObjects;
public List<Item> items;
public List<Item> scrapItems;
public List<EnemyType> enemies;
[Header("DunGen")]
public TileSet mayorRegularTileset;
public TileSet mayorVanillaTileset;
public DungeonArchetype hallwayEntranceArchetype;
public List<DungeonArchetype> archetypes;
public List<TileSet> tilesets;
public GraphNode gardenEntranceGraphNode;
[Header("Main Prefabs")]
public GameObject scarletNetworkManager;
@ -37,5 +36,6 @@ namespace ScarletMansion {
[Header("Mis References")]
public Material ghostMaterial;
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 ConfigEntryBundleString = ScarletMansion.PluginConfig.ConfigEntryBundle<string>;
using ConfigEntryBundleBool = ScarletMansion.PluginConfig.ConfigEntryBundle<bool>;
using HarmonyLib;
using LethalConfig.MonoBehaviours.Components;
using UnityEngine.UI;
using LethalConfig.MonoBehaviours;
namespace ScarletMansion.ModPatch {
public class LethalConfigPatch : ModPatch {
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 override string version => "1.4.2";
public LethalConfigPatch(string guid) : base(guid) { }
public static void ForceUIUpdate(){
try {
var desiredTypeString = "LethalConfig.MonoBehaviours.ConfigMenu, LethalConfig";
var configMenuType = Type.GetType(desiredTypeString);
var configMenuObject = GameObject.FindObjectOfType(configMenuType);
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[] {} );
var configMenuObject = GameObject.FindObjectOfType<ConfigMenu>();
configMenuObject.Close(false);
configMenuObject.Open();
} catch (Exception e) {
Plugin.logger.LogWarning("Could not force Lethal Config UI update");
Plugin.logger.LogError(e.ToString());
@ -151,6 +151,41 @@ namespace ScarletMansion.ModPatch {
CreatePresetConfig(PluginConfig.lcDungeonLightingPreset.config, PresetConfig.dungeonLightingChangeList);
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 {
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 facilityMeldownGuid = "me.loaforc.facilitymeltdown";
public const string reserveFlashlightGuid = "FlipMods.ReservedFlashlightSlot";
public static readonly ModPatch[] modPatches = new ModPatch[] {
new AdvancedCompanyPatch(advancedCompanyGuid),
//new AdvancedCompanyPatch(advancedCompanyGuid),
new LethalConfigPatch(lethalConfigGuid),
new FacilityMeltdownPatch(facilityMeldownGuid),
new ReservedItemSlotPatch(reserveFlashlightGuid)

View File

@ -23,16 +23,17 @@ namespace ScarletMansion {
[BepInDependency("imabatby.lethallevelloader", "1.2.0.3")]
[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.facilityMeldownGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.reserveFlashlightGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInProcess("Lethal Company.exe")]
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 modVersion = "1.3.21";
private const string modVersion = "1.3.22";
public readonly Harmony harmony = new Harmony(modGUID);
@ -66,10 +67,7 @@ namespace ScarletMansion {
harmony.PatchAll(typeof(ShotgunItemPatch));
harmony.PatchAll(typeof(ShovelPatch));
//harmony.PatchAll(typeof(OptimizePatch));
harmony.PatchAll(typeof(DoorwayConnectionPatch));
harmony.PatchAll(typeof(GeneratePathPatch));
harmony.PatchAll(typeof(PostProcessPatch));
harmony.PatchAll(typeof(PluginConfig));
@ -130,7 +128,10 @@ namespace ScarletMansion {
Assets.dungeonExtended = extendedDungeon;
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(){

View File

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

View File

@ -240,11 +240,20 @@ namespace ScarletMansion {
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 void Update();
public abstract bool IsEqual(Change target);
public abstract IEnumerable<ConfigEntryBase> configBases { get; }
}
public abstract class ChangeType<T> : Change {
@ -252,6 +261,12 @@ namespace ScarletMansion {
public ConfigEntry<T> configEntry => configBundle.config;
public T value;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return configEntry;
}
}
public ChangeType(PluginConfig.ConfigEntryBundle<T> configBundle){
this.configBundle = configBundle;
this.value = configBundle.defaultValue;
@ -296,6 +311,13 @@ namespace ScarletMansion {
public ChangeInt min;
public ChangeInt max;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return min.configEntry;
yield return max.configEntry;
}
}
public ChangeMinMaxInt(PluginConfig.ConfigEntryBundleMinMax<int> configBundle){
min = new ChangeInt(configBundle.min);
max = new ChangeInt(configBundle.max);
@ -326,6 +348,13 @@ namespace ScarletMansion {
public ChangeFloat min;
public ChangeFloat max;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return min.configEntry;
yield return max.configEntry;
}
}
public ChangeMinMaxFloat(PluginConfig.ConfigEntryBundleMinMax<float> configBundle){
min = new ChangeFloat(configBundle.min);
max = new ChangeFloat(configBundle.max);
@ -356,6 +385,13 @@ namespace ScarletMansion {
public ChangeMinMaxInt count;
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){
count = new ChangeMinMaxInt(configBundle.count);
depth = new ChangeMinMaxInt(configBundle.depth);

View File

@ -37,9 +37,6 @@
<Reference Include="AdvancedCompany">
<HintPath>..\..\..\Libraries\AdvancedCompany.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\Libraries\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>..\..\..\Libraries\Assembly-CSharp-firstpass.dll</HintPath>
</Reference>
@ -52,11 +49,14 @@
<Reference Include="BepInEx.Harmony">
<HintPath>..\..\..\Libraries\BepInEx.Harmony.dll</HintPath>
</Reference>
<Reference Include="DunGenPlus">
<HintPath>..\..\..\Libraries\DunGenPlus.dll</HintPath>
</Reference>
<Reference Include="FacilityMeltdown">
<HintPath>..\..\..\Libraries\FacilityMeltdown.dll</HintPath>
</Reference>
<Reference Include="LethalConfig">
<HintPath>..\..\..\Libraries\LethalConfig.dll</HintPath>
<Reference Include="LethalConfig-publicized">
<HintPath>..\..\..\Libraries\LethalConfig-publicized.dll</HintPath>
</Reference>
<Reference Include="LethalLevelLoader">
<HintPath>..\..\..\Libraries\LethalLevelLoader.dll</HintPath>
@ -150,33 +150,15 @@
</ItemGroup>
<ItemGroup>
<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\OptimizePatch.cs" />
<Compile Include="DunGenPatch\Patch.cs" />
<Compile Include="DunGenPatch\PostProcessPatch.cs" />
<Compile Include="GamePatch\Components\FloorCleanup.cs" />
<Compile Include="GamePatch\Components\FloorCleanUpParent.cs" />
<Compile Include="GamePatch\Components\KnightSpawnPoint.cs" />
<Compile Include="GamePatch\Components\Lights\ScarletLight.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\ScarletClock.cs" />
<Compile Include="GamePatch\Components\ScarletDoor.cs" />
@ -187,9 +169,12 @@
<Compile Include="GamePatch\Components\ScarletLighting.cs" />
<Compile Include="GamePatch\Components\ScarletPlayerControllerB.cs" />
<Compile Include="GamePatch\Components\ScarletProp.cs" />
<Compile Include="GamePatch\Components\ScarletRadio.cs" />
<Compile Include="GamePatch\Components\ScarletVent.cs" />
<Compile Include="GamePatch\Components\ScarletYukariTrigger.cs" />
<Compile Include="GamePatch\Components\TreasureRoom\TreasureRoomTimedOpen.cs" />
<Compile Include="GamePatch\DoorLockPatch.cs" />
<Compile Include="GamePatch\Enemies\KnightGhostVariant.cs" />
<Compile Include="GamePatch\Enemies\KnightVariant.cs" />
<Compile Include="GamePatch\Enemies\MaidVariant.cs" />
<Compile Include="GamePatch\EnemyVentPatch.cs" />
@ -204,7 +189,6 @@
<Compile Include="GamePatch\InitPatch.cs" />
<Compile Include="GamePatch\Items\FlandreCrystal.cs" />
<Compile Include="GamePatch\Items\IScarletItem.cs" />
<Compile Include="GamePatch\Items\PlayerAnimatorStateHelper.cs" />
<Compile Include="GamePatch\Items\ScarletFlashlight.cs" />
<Compile Include="GamePatch\Items\ScarletKnife.cs" />
<Compile Include="GamePatch\Items\ScarletPainting.cs" />
@ -212,7 +196,6 @@
<Compile Include="GamePatch\JesterAIPatch.cs" />
<Compile Include="GamePatch\LoadAssetsIntoLevelPatch.cs" />
<Compile Include="GamePatch\Managers\AngerManager.cs" />
<Compile Include="GamePatch\Managers\DoorwayManager.cs" />
<Compile Include="GamePatch\Managers\KnightSpawnManager.cs" />
<Compile Include="GamePatch\Managers\ScarletLightingManager.cs" />
<Compile Include="GamePatch\Managers\ScarletNetworkManager.cs" />
@ -227,14 +210,13 @@
<Compile Include="GamePatch\Props\RandomPrefabBasicBase.cs" />
<Compile Include="GamePatch\Props\RandomPrefabCycle.cs" />
<Compile Include="GamePatch\Props\RandomPrefabWithScale.cs" />
<Compile Include="GamePatch\Props\SpawnSyncedObjectCycle.cs" />
<Compile Include="GamePatch\RoundManagerPatch.cs" />
<Compile Include="GamePatch\ScarletLightPatch.cs" />
<Compile Include="GamePatch\ShotgunItemPatch.cs" />
<Compile Include="GamePatch\ShovelPatch.cs" />
<Compile Include="GamePatch\Weathers\SDMWeatherManager.cs" />
<Compile Include="LoadingPatch\NetworkObjectListScriptableObject.cs" />
<Compile Include="MainMenuUpdate.cs" />
<Compile Include="ModPatch\AdvancedCompanyPatch.cs" />
<Compile Include="ModPatch\FacilityMeltdownPatch.cs" />
<Compile Include="ModPatch\LethalConfigPatch.cs" />
<Compile Include="ModPatch\ModCompability.cs" />

View File

@ -83,6 +83,10 @@ namespace ScarletMansion {
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){
seq.Add((i) => {
var fieldInfo = i.operand as FieldInfo;
@ -117,6 +121,7 @@ namespace ScarletMansion {
public bool VerifyStage(CodeInstruction current){
var s = seq[stage];
if (s.Invoke(current)) {
//Plugin.logger.LogInfo($"{name}({stage}): current.ToString()");
stage++;
} else {
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(){
return LoadAssetsIntoLevelPatch.GetItems(RoundManager.Instance.currentLevel.spawnableScrap);
}
@ -312,6 +322,34 @@ namespace ScarletMansion {
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){

View File

@ -36,7 +36,7 @@ namespace ScarletMansionMimicsPatch {
public override bool IsMyInteriorLoaded => ScarletMansion.DunGenPatch.Patch.active;
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) {
c.overrideConnector = true;
c.overrideNoDoorway = true;

View File

@ -19,12 +19,12 @@ namespace ScarletMansionMimicsPatch {
[BepInPlugin(modGUID, modName, modVersion)]
[BepInDependency("ImoutoSama.ScarletMansion", "1.3.12")]
[BepInDependency("dev.ladyalice.scarletmansion", "1.3.22")]
[BepInDependency(targetModGUID, BepInDependency.DependencyFlags.SoftDependency)]
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 modVersion = "1.0.0";

View File

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