Config refactor to split dungeon generation and the mod's universal features.

This commit is contained in:
LadyAliceMargatroid 2025-01-11 01:49:35 -08:00
parent d690b69a26
commit 83d0d8a737
44 changed files with 2001 additions and 1664 deletions

View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35122.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DungeonFoyer", "DungeonFoyer\DungeonFoyer.csproj", "{A0E41AED-7C98-4A4D-B09B-1E59375420D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A0E41AED-7C98-4A4D-B09B-1E59375420D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0E41AED-7C98-4A4D-B09B-1E59375420D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0E41AED-7C98-4A4D-B09B-1E59375420D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0E41AED-7C98-4A4D-B09B-1E59375420D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A173D2B7-7098-4906-881E-72A9A6F15BEC}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A0E41AED-7C98-4A4D-B09B-1E59375420D9}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DungeonFoyer</RootNamespace>
<AssemblyName>DungeonFoyer</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="0Harmony">
<HintPath>..\..\..\Libraries\0Harmony.dll</HintPath>
</Reference>
<Reference Include="BepInEx">
<HintPath>..\..\..\Libraries\BepInEx.dll</HintPath>
</Reference>
<Reference Include="BepInEx.Harmony">
<HintPath>..\..\..\Libraries\BepInEx.Harmony.dll</HintPath>
</Reference>
<Reference Include="LethalConfig">
<HintPath>..\..\..\Libraries\LethalConfig.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="UnityEngine">
<HintPath>..\..\..\Libraries\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Libraries\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Libraries\UnityEngine.ImageConversionModule.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,55 @@
using BepInEx;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using UnityEngine;
using System.Reflection;
using BepInEx.Logging;
using HarmonyLib.Tools;
namespace DungeonFoyer {
[BepInDependency("ainavt.lc.lethalconfig", "1.4.2")]
[BepInPlugin(modGUID, modName, modVersion)]
public class Plugin : BaseUnityPlugin {
public const string modGUID = "dev.ladyalice.scarletmansion.foyer";
private const string modName = "Scarlet Foyer";
private const string modVersion = "1.0.0";
public static ManualLogSource logger { get; internal set; }
void Awake(){
logger = BepInEx.Logging.Logger.CreateLogSource(modGUID);
logger.LogInfo($"Plugin {modName} has been added!");
// the entire purpose this plugin is so I have a unique assembly for Lethal Config
var assemblyPath = Assembly.GetExecutingAssembly().Location;
var path = Path.Combine(Path.GetDirectoryName(assemblyPath), "foyer.png");
var texture2D = LoadTexture(path);
var icon = Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), Vector2.zero);
LethalConfig.LethalConfigManager.SetModIcon(icon);
LethalConfig.LethalConfigManager.SetModDescription("This is the config for the Scarlet Devil Mansion's Foyer dungeon variant.");
}
Texture2D LoadTexture(string FilePath) {
Texture2D Tex2D;
byte[] FileData;
if (File.Exists(FilePath)){
FileData = File.ReadAllBytes(FilePath);
Tex2D = new Texture2D(2, 2);
Tex2D.LoadImage(FileData);
return Tex2D;
}
return null;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// アセンブリに関する一般情報は以下を通して制御されます
// 制御されます。アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("DungeonFoyer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DungeonFoyer")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから
// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、
// その型の ComVisible 属性を true に設定してください。
[assembly: ComVisible(false)]
// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
[assembly: Guid("a0e41aed-7c98-4a4d-b09b-1e59375420d9")]
// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
//
// メジャー バージョン
// マイナー バージョン
// ビルド番号
// リビジョン
//
// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます
// 既定値にすることができます:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -25,6 +25,7 @@ namespace ScarletMansionMimicsPatch {
public static void Activate() { public static void Activate() {
MimicsAPI.GetAPI().RegisterMimicEventHandler(new Patch.SDMMimicEventHandler()); MimicsAPI.GetAPI().RegisterMimicEventHandler(new Patch.SDMMimicEventHandler());
//Mimics.Mimics.InteriorWhitelist.Add();
Plugin.Instance.harmony.PatchAll(typeof(Patch)); Plugin.Instance.harmony.PatchAll(typeof(Patch));
} }

View File

@ -12,6 +12,7 @@ using LethalLib.Modules;
using LethalLevelLoader; using LethalLevelLoader;
using DunGenPlus; using DunGenPlus;
using ScarletMansion.ModPatch; using ScarletMansion.ModPatch;
using ScarletMansion.Configs;
namespace ScarletMansion { namespace ScarletMansion {
public static class Assets { public static class Assets {
@ -25,15 +26,10 @@ namespace ScarletMansion {
// main assets // main assets
public static AssetBundle MainAssetBundle = null; public static AssetBundle MainAssetBundle = null;
public static DungeonFlow dungeon; public static ExtendedMod extendedMod;
public static DunGenExtender dunGenExtender;
public static NetworkObjectListScriptableObject networkObjectList; public static NetworkObjectListScriptableObject networkObjectList;
public static AudioClip entranceAudioClip; public static AudioClip entranceAudioClip;
// extended dungeon values
public static ExtendedMod extendedMod;
public static ExtendedDungeonFlow dungeonExtended;
// enemy values // enemy values
public class Enemy { public class Enemy {
@ -66,9 +62,9 @@ namespace ScarletMansion {
public Item item; public Item item;
private int _itemId; private int _itemId;
public Func<PluginConfig.Item> configItemEntryFunc; public Func<ConfigItem> configItemEntryFunc;
public GlobalItem(Item item, Func<PluginConfig.Item> configItemEntryFunc) { public GlobalItem(Item item, Func<ConfigItem> configItemEntryFunc) {
this.item = item; this.item = item;
_itemId = -1; _itemId = -1;
this.configItemEntryFunc = configItemEntryFunc; this.configItemEntryFunc = configItemEntryFunc;
@ -94,7 +90,7 @@ namespace ScarletMansion {
} }
} }
public PluginConfig.Item GetConfigItemEntry(){ public ConfigItem GetConfigItemEntry(){
return configItemEntryFunc(); return configItemEntryFunc();
} }
} }
@ -102,9 +98,9 @@ namespace ScarletMansion {
public class ScrapItem : GlobalItem { public class ScrapItem : GlobalItem {
public bool SpawnsOnMap => configScrapItemEntryFunc != null; public bool SpawnsOnMap => configScrapItemEntryFunc != null;
public Func<PluginConfig.ScrapItem> configScrapItemEntryFunc; public Func<ConfigScrapItem> configScrapItemEntryFunc;
public ScrapItem(Item item, Func<PluginConfig.ScrapItem> configScrapItemEntryFunc) : base (item, configScrapItemEntryFunc){ public ScrapItem(Item item, Func<ConfigScrapItem> configScrapItemEntryFunc) : base (item, configScrapItemEntryFunc){
this.configScrapItemEntryFunc = configScrapItemEntryFunc; this.configScrapItemEntryFunc = configScrapItemEntryFunc;
} }
@ -124,7 +120,7 @@ namespace ScarletMansion {
return item; return item;
} }
public PluginConfig.ScrapItem GetConfigScrapItemEntry(){ public ConfigScrapItem GetConfigScrapItemEntry(){
return configScrapItemEntryFunc(); return configScrapItemEntryFunc();
} }
@ -138,7 +134,7 @@ namespace ScarletMansion {
public int lethalHelmetIndex = -1; public int lethalHelmetIndex = -1;
public int scarletHelmetIndex = -1; public int scarletHelmetIndex = -1;
public Flashlight(string baseName, int lethalHelmetIndex, Func<PluginConfig.Item> configEntryFunc) : base(null, configEntryFunc) { public Flashlight(string baseName, int lethalHelmetIndex, Func<ConfigItem> configEntryFunc) : base(null, configEntryFunc) {
assetName = $"Scarlet{baseName}"; assetName = $"Scarlet{baseName}";
displayName = $"D. {baseName}"; displayName = $"D. {baseName}";
this.lethalHelmetIndex = lethalHelmetIndex; this.lethalHelmetIndex = lethalHelmetIndex;
@ -157,10 +153,11 @@ namespace ScarletMansion {
public static List<GlobalItem> globalItems; public static List<GlobalItem> globalItems;
public static List<ScrapItem> scrapItems; public static List<ScrapItem> scrapItems;
public static Dictionary<string, Func<PluginConfig.ScrapItem>> itemConfigTable = new Dictionary<string, Func<PluginConfig.ScrapItem>>(){ public static Dictionary<string, Func<ConfigScrapItem>> itemConfigTable = new Dictionary<string, Func<ConfigScrapItem>>(){
{ "Deco. crystal", () => PluginConfig.Instance.crystalValue }, { "Deco. crystal", () => ConfigMain.Instance.crystalValue },
{ "Shattered deco. crystal", () => PluginConfig.Instance.crystalBrokenValue }, { "Shattered deco. crystal", () => ConfigMain.Instance.crystalBrokenValue },
{ "Doll Snow Globe", () => PluginConfig.Instance.snowGlobeValue } { "Doll Snow Globe", () => ConfigMain.Instance.snowGlobeValue },
{ "Gohei", () => ConfigMain.Instance.goheiValue }
}; };
@ -211,9 +208,9 @@ namespace ScarletMansion {
} }
dungeon = Load<DungeonFlow>("SDMLevel"); Plugin.ConfigFoyer.dungeon = Load<DungeonFlow>("SDMLevel");
Plugin.ConfigFoyer.dunGenExtender = Load<DunGenExtender>("DunGenExtender");
networkObjectList = Load<NetworkObjectListScriptableObject>("SDMList"); networkObjectList = Load<NetworkObjectListScriptableObject>("SDMList");
dunGenExtender = Load<DunGenExtender>("DunGenExtender");
entranceAudioClip = Load<AudioClip>("entrance_ogg"); entranceAudioClip = Load<AudioClip>("entrance_ogg");
knight = new Enemy( knight = new Enemy(
@ -248,12 +245,12 @@ namespace ScarletMansion {
Plugin.logger.LogDebug($"Scrap Item {i.itemName} registered"); Plugin.logger.LogDebug($"Scrap Item {i.itemName} registered");
} }
key = new GlobalItem(networkObjectList.items[0], () => PluginConfig.Instance.scarletKeyValue); key = new GlobalItem(networkObjectList.items[0], () => ConfigMain.Instance.scarletKeyValue);
globalItems.Add(key); globalItems.Add(key);
Items.RegisterItem(key.item); Items.RegisterItem(key.item);
flashlight = new Flashlight("Pro Flashlight", 0, () => PluginConfig.Instance.decoProFlashlightValue); flashlight = new Flashlight("Pro Flashlight", 0, () => ConfigMain.Instance.decoProFlashlightValue);
flashlightBB = new Flashlight("Flashlight", 1, () => PluginConfig.Instance.decoFlashlightValue); flashlightBB = new Flashlight("Flashlight", 1, () => ConfigMain.Instance.decoFlashlightValue);
globalItems.Add(flashlight); globalItems.Add(flashlight);
globalItems.Add(flashlightBB); globalItems.Add(flashlightBB);
@ -265,7 +262,7 @@ namespace ScarletMansion {
onAssetsLoadEvent.Call(); onAssetsLoadEvent.Call();
} }
public static Func<PluginConfig.ScrapItem> GetItemConfigFunction(Item item){ public static Func<ConfigScrapItem> GetItemConfigFunction(Item item){
var name = item.itemName; var name = item.itemName;
if (itemConfigTable.TryGetValue(name, out var result)){ if (itemConfigTable.TryGetValue(name, out var result)){
return result; return result;

View File

@ -0,0 +1,355 @@
using BepInEx.Configuration;
using DunGen.Graph;
using DunGenPlus;
using LethalLevelLoader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ScarletMansion.Configs {
public class ConfigDungeon<T> : ConfigManager<T>, IConfigDungeon where T: ConfigDungeon<T> {
public DungeonFlow dungeon;
public DunGenExtender dunGenExtender;
public ExtendedDungeonFlow dungeonExtended;
public ConfigDungeon(ConfigFile cfg) : base(cfg) { }
// ----------------------------
// BOUNDING BOX
// ----------------------------
public static ConfigEntryBundle<int> dunGenWidthBase = new ConfigEntryBundle<int>(
dungeonGenerationBoundingBoxPrefix,
"Width Base",
120,
"The width (left-to-right) of the dungeon's bounding box.\nThe dungeon is generated within this bounding box and cannot generate tiles outside of it.\nIncreasing this value will make the dungeon less compact but less likely to fail generation and vice versa.\nDecreasing this value too much can make it impossible for the dungeon to generate.",
null,
new AcceptableValueRange<int>(40, 200)
);
public static ConfigEntryBundle<int> dunGenLengthBase = new ConfigEntryBundle<int>(
dungeonGenerationBoundingBoxPrefix,
"Length Base",
80,
"The length (forward-to-back) of the dungeon's bounding box.\nThe dungeon is generated within this bounding box and cannot generate tiles outside of it.\nIncreasing this value will make the dungeon less compact but less likely to fail generation and vice versa.\nDecreasing this value too much can make it impossible for the dungeon to generate.",
null,
new AcceptableValueRange<int>(40, 200)
);
public static ConfigEntryBundle<float> dunGenWidthMultiFactor = new ConfigEntryBundle<float>(
dungeonGenerationBoundingBoxPrefix,
"Width Multiplier Factor",
0.5f,
"The width (left-to-right) of the dungeon's bounding box increases based on the dungeon's size. That additional width is multiplied by this value.\nThe exact formula is width = base + (base * (size - 1) * factor).\nIncreasing this value will make the dungeon less compact but less likely to fail generation on bigger moons.\nSetting this value to 0 means that the base width will be a constant value no matter how small or big the moon may be.",
null,
new AcceptableValueRange<float>(0f, 4f)
);
public static ConfigEntryBundle<float> dunGenLengthMultiFactor = new ConfigEntryBundle<float>(
dungeonGenerationBoundingBoxPrefix,
"Length Multiplier Factor",
0.3333333f,
"The length (forward-to-back) of the dungeon's bounding box increases based on the dungeon's size. That additional length is multiplied by this value.\nThe exact formula is length = base + (base * (size - 1) * factor).\nIncreasing this value will make the dungeon less compact but less likely to fail generation on bigger moons.\nSetting this value to 0 means that the base length will be a constant value no matter how small or big the moon may be.",
null,
new AcceptableValueRange<float>(0f, 4f)
);
public int dunGenWidthBaseValue;
public int dunGenLengthBaseValue;
public float dunGenWidthMultiFactorValue;
public float dunGenLengthMultiFactorValue;
public int GetDunGenWidthBase => dunGenWidthBaseValue;
public int GetDunGenLengthBase => dunGenLengthBaseValue;
public float GetDunGenWidthMultiFactor => dunGenWidthMultiFactorValue;
public float GetDunGenLengthMultiFactor => dunGenLengthMultiFactorValue;
// ----------------------------
// MAIN PATH
// ----------------------------
public static ConfigEntryBundle<int> mainPathCount = new ConfigEntryBundle<int>(
dungeonGenerationMPathsPrefix,
"Main Path Count",
3,
"The amount of main paths that the modified dungeon generation code must generate.\nDecreasing this value will make it much less likely to fail generation but will lower the length consistency of branching paths.\nSetting the value to 1 will revert back to the vanilla dungeon generation.",
null,
new AcceptableValueRange<int>(1, 3)
);
private static readonly string _mainPathLengthPostMessage = "Decreasing this value will make the dungeon less likely to fail generation but will lower the length consistency of branching paths and vice versa.\nIncreasing this value too much can make it impossible for the dungeon to generate.";
public static ConfigEntryBundleMinMax<int> mainPathLength = new ConfigEntryBundleMinMax<int>(
dungeonGenerationMPathsPrefix,
"Main Path Length Min",
"Main Path Length Max",
3,
4,
$"The minimum allowed length of the main path. This value is multiplied by the dungeon's size.\n\n{_mainPathLengthPostMessage}",
$"The maximum allowed length of the main path. This value is multiplied by the dungeon's size.\n\n{_mainPathLengthPostMessage}",
null,
new AcceptableValueRange<int>(3, 20)
);
public int mainPathCountValue;
public ConfigIntRange mainPathLengthValue = new ConfigIntRange("main path length");
public int GetMainPathCount => mainPathCountValue;
public ConfigIntRange GetMainPathLength => mainPathLengthValue;
// ----------------------------
// BRANCHING PATH
// ----------------------------
public static ConfigEntryBundle<bool> branchMultiSim = new ConfigEntryBundle<bool>(
dungeonGenerationBPathPrefix,
"Use Branch Path Multi Sim",
true,
"If enabled, the dungeon generation will prioritize branch paths that connect to already generated tiles.\nThis increases the chance of circular/looping paths. Slows dungeon generation times a bit at the end.\n\nOnly works when MainPathCount is more than 1.",
null
);
public static ConfigEntryBundle<int> branchSimCount = new ConfigEntryBundle<int>(
dungeonGenerationBPathPrefix,
"Branch Path Simlation Count",
5,
"The maximum number of valid branch paths that the dungeon generation will simulate before choosing the best path.\nIncreasing this value gives the dungeon generation a higher chance of finding a good path but impacts dungeon generation times and vice versa.",
null,
new AcceptableValueRange<int>(1, 10)
);
public bool branchMultiSimValue;
public int branchSimCountValue;
public bool GetBranchMultiSim => branchMultiSimValue;
public int GetBranchSimCount => branchSimCountValue;
// ----------------------------
// BRANCHING PATH DETAILED
// ----------------------------
private static readonly string _branchPathSectionOneMessage = "Section 1 consists of the first 30% tiles of the main path (including the mayor entrance tile).";
private static readonly string _branchPathSectionTwoMessage = "Section 2 consists of the middle tiles (30% - 70%) of the main path.";
private static readonly string _branchPathSectionThreeMessage = "Section 3 consists of the last 30% tiles of the main path.";
public static ConfigEntryBundleBranchingPath branchPathSectionOne = new ConfigEntryBundleBranchingPath(
dungeonGenerationBPathOnePrefix, 1, _branchPathSectionOneMessage, 4, 6, 1, 2
);
public static ConfigEntryBundleBranchingPath branchPathSectionTwo = new ConfigEntryBundleBranchingPath(
dungeonGenerationBPathTwoPrefix, 2, _branchPathSectionTwoMessage, 2, 3, 1, 2
);
public static ConfigEntryBundleBranchingPath branchPathSectionThree = new ConfigEntryBundleBranchingPath(
dungeonGenerationBPathThreePrefix, 3, _branchPathSectionThreeMessage, 1, 2, 0, 1
);
public ConfigBranchingPathRange branchPathSectionOneValue = new ConfigBranchingPathRange("section one");
public ConfigBranchingPathRange branchPathSectionTwoValue = new ConfigBranchingPathRange("section two");
public ConfigBranchingPathRange branchPathSectionThreeValue = new ConfigBranchingPathRange("section three");
public ConfigBranchingPathRange GetBranchPathSectionOne => branchPathSectionOneValue;
public ConfigBranchingPathRange GetBranchPathSectionTwo => branchPathSectionTwoValue;
public ConfigBranchingPathRange GetBranchPathSectionThree => branchPathSectionThreeValue;
// ----------------------------
// LIGHTING
// ----------------------------
public static ConfigEntryBundle<int> hallwayLightsWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"Hallway Lights Weight",
150,
"The weight for a hallway wall lamp to appear on its respective walls. With the default weight of 150 against the weight of the empty wall of 50, there is a 75% chance that a wall lamp will spawn. Setting the weight to 100 will give you a 67% chance, 450=90% chance, and so on.",
null,
new AcceptableValueRange<int>(0, 999)
);
public static ConfigEntryBundle<int> ceilingLightsWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"Chandelier Lights Weight",
150,
"The weight for a chandelier to appear on its respective ceilings. With the default weight of 150 against the weight of the empty ceiling of 50, there is a 75% chance that a chandelier will spawn. Setting the weight to 100 will give you a 67% chance, 450=90% chance, and so on.",
null,
new AcceptableValueRange<int>(0, 999)
);
public static ConfigEntryBundle<int> lightsSpawnZeroWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"0 Lights Weight",
1,
"The weight that none of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [1, 7, 2], there is a 10% chance that even in a room filled with lamps, none of them will emit light.",
new ConfigEntryBundleExtraParameters(VerifyLightingValues, false),
new AcceptableValueRange<int>(0, 99)
);
public static ConfigEntryBundle<int> lightsSpawnOneWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"1 Light Weight",
7,
"The weight that only 1 of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [1, 7, 2], there is a 70% chance that even in a room filled with lamps, only one of them will emit light.",
new ConfigEntryBundleExtraParameters(VerifyLightingValues, false),
new AcceptableValueRange<int>(0, 99)
);
public static ConfigEntryBundle<int> lightsSpawnTwoWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"2 Lights Weight",
2,
"The weight that only 2 of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [1, 7, 2], there is a 20% chance that even in a room filled with lamps, only two of them will emit light.",
new ConfigEntryBundleExtraParameters(VerifyLightingValues, true),
new AcceptableValueRange<int>(0, 99)
);
public int hallwayLightsWeightValue;
public int ceilingLightsWeightValue;
public int lightsSpawnZeroWeightValue;
public int lightsSpawnOneWeightValue;
public int lightsSpawnTwoWeightValue;
public int GetHallwayLightsWeight => hallwayLightsWeightValue;
public int GetCeilingLightsWeight => ceilingLightsWeightValue;
public int GetLightsSpawnZeroWeight => lightsSpawnZeroWeightValue;
public int GetLightsSpawnOneWeight => lightsSpawnOneWeightValue;
public int GetLightsSpawnTwoWeight => lightsSpawnTwoWeightValue;
public static void VerifyLightingValues(T instance){
if (instance.lightsSpawnZeroWeightValue == 0f && instance.lightsSpawnOneWeightValue == 0f && instance.lightsSpawnTwoWeightValue == 0f){
instance.lightsSpawnZeroWeightValue = 1;
}
Plugin.logger.LogDebug("Verifying lightsSpawn 0/1/2 values");
}
// ----------------------------
// BALANCE
// ----------------------------
public static ConfigEntryBundle<float> lootMultiplier = new ConfigEntryBundle<float>(
dungeonBalancePrefix,
"Loot Multiplier",
1.2f,
"Multiplies the total amount of loot for the dungeon.",
null,
new AcceptableValueRange<float>(0.25f, 4f)
);
public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>(
dungeonEnemiesPrefix,
"Map Hazards Multiplier",
1.6f,
"Multiplies the total amount of map hazards (landmines, turrets) for the dungeon.",
null,
new AcceptableValueRange<float>(0.25f, 4f)
);
public static ConfigEntryBundle<int> minIndoorEnemySpawnCount = new ConfigEntryBundle<int>(
dungeonEnemiesPrefix,
"Minimum Indoor Enemy Spawn Count",
1,
"Increases the minimum amount of indoor enemies that spawn with each spawn wave. For reference, Eclipse is +3.",
null,
new AcceptableValueRange<int>(0, 3)
);
public static ConfigEntryBundle<int> paintingCount = new ConfigEntryBundle<int>(
dungeonPaintingEventPrefix,
"Painting Count",
2,
"The maximum amount of demonic paintings that spawn in the dungeon.",
null,
new AcceptableValueRange<int>(0, 10)
);
public static ConfigEntryBundleMinMax<int> paintingExtraLoot = new ConfigEntryBundleMinMax<int>(
dungeonPaintingEventPrefix,
"Painting Extra Loot Min",
"Painting Extra Loot Max",
0,
2,
"The minimum allowed amount of extra loot that spawns after grabbing the demonic painting.",
"The maximum allowed amount of extra loot that spawns after grabbing the demonic painting.",
null,
new AcceptableValueRange<int>(0, 3)
);
public float lootMultiplierValue;
public float mapHazardsMultiplierValue;
public int minIndoorEnemySpawnCountValue;
public int paintingCountValue;
public ConfigIntRange paintingExtraLootValue = new ConfigIntRange("painting extra loot");
public float GetLootMultiplier => lootMultiplierValue;
public float GetMapHazardsMultiplier => mapHazardsMultiplierValue;
public int GetMinIndoorEnemySpawnCount => minIndoorEnemySpawnCountValue;
public int GetPaintingCount => paintingCountValue;
public ConfigIntRange GetPaintingExtraLoot => paintingExtraLootValue;
// ----------------------------
// TREASURE ROOM
// ----------------------------
public static ConfigEntryBundle<int> treasureRoomCount = new ConfigEntryBundle<int>(
dungeonTreasureRoomPrefix,
"Treasure Room Count",
2,
"The maximum amount of treasure rooms that spawn in the dungeon.",
null,
new AcceptableValueRange<int>(0, 10)
);
public static ConfigEntryBundleMinMax<int> treasureRoomLoot = new ConfigEntryBundleMinMax<int>(
dungeonTreasureRoomPrefix,
"Treasure Room Loot Min",
"Treasure Room Loot Max",
2,
3,
"The minimum allowed amount of loot that spawns in the treasure room.",
"The maximum allowed amount of loot that spawns in the treasure room.",
null,
new AcceptableValueRange<int>(0, 6)
);
public int treasureRoomCountValue;
public ConfigIntRange treasureRoomLootValue = new ConfigIntRange("treasure room loot");
public int GetTreasureRoomCount => treasureRoomCountValue;
public ConfigIntRange GetTreasureRoomLoot => treasureRoomLootValue;
// Preset helper functions idk
public static ConfigEntryBundle<DungeonGeneration> dungeonGenerationPreset = new ConfigEntryBundle<DungeonGeneration>(
presetsPrefix,
"Preset Dungeon Generation",
DungeonGeneration.Default,
$"Automatically updates config values based on this preset string. If you want to disable this, change this value to \"Custom\".\nPossible values are ({GetEnumNames<DungeonGeneration>()}).\nPlease use the mod LethalConfig for more information on these presets.",
new ConfigEntryBundleExtraParameters((c) => UpdateFromPresetValue((int)dungeonGenerationPreset.config.Value, dungeonGenerationChangeList), true)
);
public static ConfigEntryBundle<DungeonLighting> dungeonLightingPreset = new ConfigEntryBundle<DungeonLighting>(
presetsPrefix,
"Preset Dungeon Lighting",
DungeonLighting.Default,
$"Automatically updates config values based on this preset string. If you want to disable this, change this value to \"Custom\".\nPossible values are ({GetEnumNames<DungeonLighting>()}).\nPlease use the mod LethalConfig for more information on these presets.",
new ConfigEntryBundleExtraParameters((c) => UpdateFromPresetValue((int)dungeonLightingPreset.config.Value, dungeonLightingChangeList), true)
);
public static List<ChangeList> dungeonGenerationChangeList = new List<ChangeList>();
public static List<ChangeList> dungeonLightingChangeList = new List<ChangeList>();
public static string GetEnumNames<T>() where T: Enum {
var str = string.Empty;
var enums = Enum.GetNames(typeof(T)).Select(e => $"\"{e}\"");
return string.Join(", ", enums);
}
public static void UpdateFromPresetValue(int value, List<ChangeList> changeList){
if (value >= 0 && value < changeList.Count) {
var target = changeList[value];
target.UpdateChanges();
}
}
}
}

View File

@ -0,0 +1,171 @@
using BepInEx.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ScarletMansion.Configs {
public class ConfigDungeonFoyer : ConfigDungeon<ConfigDungeonFoyer> {
public ConfigDungeonFoyer(ConfigFile cfg) : base(cfg) {
AutoGenerateOnChangeConfig(cfg, dungeonGenerationPreset, dungeonLightingPreset);
CreateDungeonGenerationChangeList();
CreateDungeonLightingChangeList();
dungeonGenerationPreset.Bind(cfg, this);
dungeonLightingPreset.Bind(cfg, this);
}
public static void CreateDungeonGenerationChangeList(){
var customChangeList = new ChangeList("Custom", "Disables auto loading of preset config values.");
var defaultGeneration = new ChangeList(
"Default",
"The default generation values. Intended for lobbies with 3+ players.",
new ChangeInt ( dunGenWidthBase, 120 ),
new ChangeInt ( dunGenLengthBase, 80 ),
new ChangeFloat ( dunGenWidthMultiFactor, 0.5f ),
new ChangeFloat ( dunGenLengthMultiFactor, 0.3333333f ),
new ChangeInt ( mainPathCount, 3 ),
new ChangeMinMaxInt ( mainPathLength, 3, 4 ),
new ChangeBranchingPath( branchPathSectionOne, 4, 6, 1, 2 ),
new ChangeBranchingPath( branchPathSectionTwo, 2, 3, 1, 2 ),
new ChangeBranchingPath( branchPathSectionThree, 1, 2, 0, 1 ),
new ChangeFloat ( lootMultiplier, 1.2f ),
new ChangeFloat( mapHazardsMultiplier, 1.6f ),
new ChangeInt( minIndoorEnemySpawnCount, 1 ),
new ChangeInt ( paintingCount, 2 ),
new ChangeMinMaxInt( paintingExtraLoot, 0, 2 ),
new ChangeInt( treasureRoomCount, 2 ),
new ChangeMinMaxInt( treasureRoomLoot, 2, 3 )
);
var smallGeneration = new ChangeList(
"Small",
"A smaller variation of the default generation values. Intended for lobbies with 1-3 players.",
defaultGeneration,
new ChangeInt ( mainPathCount, 2 ),
new ChangeFloat ( lootMultiplier, 1.1f ),
new ChangeFloat ( mapHazardsMultiplier, 1.3f )
);
var moreLootGeneration = new ChangeList(
"More Loot, More Danger",
"Increases the amount of loot, map hazards, and starting enemies in the mansion. Intended for lobbies with 3+ players.",
defaultGeneration,
new ChangeFloat ( lootMultiplier, 1.4f ),
new ChangeFloat ( mapHazardsMultiplier, 2.2f ),
new ChangeInt ( minIndoorEnemySpawnCount, 2 ),
new ChangeInt ( paintingCount, 3 ),
new ChangeMinMaxInt( paintingExtraLoot, 1 , 3 ),
new ChangeInt( treasureRoomCount, 3 ),
new ChangeMinMaxInt( treasureRoomLoot, 3, 4 )
);
var bitMoreLootGeneration = new ChangeList(
"Bit More Loot, Bit More Danger",
"Increases the amount of loot, map hazards, and starting enemies in the mansion a bit. Intended for lobbies with 1-3 players.",
smallGeneration,
new ChangeFloat ( lootMultiplier, 1.3f ),
new ChangeFloat ( mapHazardsMultiplier, 1.9f ),
new ChangeInt ( minIndoorEnemySpawnCount, 2 ),
new ChangeInt ( paintingCount, 3 ),
new ChangeInt( treasureRoomCount, 3 )
);
var vanillaGeneration = new ChangeList(
"Vanilla",
"Changes the dungeon generation to match closer to a vanilla dungeon.",
defaultGeneration,
//new ChangeMinMaxFloat ( PluginConfig.dunGenMultiplier, float.MaxValue, 10f ),
new ChangeInt ( dunGenWidthBase, 200 ),
new ChangeInt ( dunGenLengthBase, 200 ),
new ChangeFloat ( dunGenWidthMultiFactor, 4f ),
new ChangeFloat ( dunGenLengthMultiFactor, 4f ),
new ChangeInt ( mainPathCount, 1 ),
new ChangeMinMaxInt ( mainPathLength, 6, 8 ),
new ChangeBranchingPath( branchPathSectionOne, 3, 5, 2, 3 ),
new ChangeBranchingPath( branchPathSectionTwo, 2, 3, 1, 2 ),
new ChangeBranchingPath( branchPathSectionThree, 1, 2, 0, 1 ),
new ChangeFloat ( lootMultiplier, 1f ),
new ChangeFloat ( mapHazardsMultiplier, 1f ),
new ChangeInt ( minIndoorEnemySpawnCount, 0 )
);
dungeonGenerationChangeList = new List<ChangeList>() {
customChangeList,
defaultGeneration,
smallGeneration,
moreLootGeneration,
bitMoreLootGeneration,
vanillaGeneration
};
}
public static void CreateDungeonLightingChangeList(){
var customChangeList = new ChangeList("Custom", "Disables auto loading of preset config values.");
var defaultLighting = new ChangeList(
"Default",
"The default lighting values.",
new ChangeInt ( hallwayLightsWeight, 150 ),
new ChangeInt ( ceilingLightsWeight, 150 ),
new ChangeInt ( lightsSpawnZeroWeight, 1 ),
new ChangeInt ( lightsSpawnOneWeight, 8 ),
new ChangeInt ( lightsSpawnTwoWeight, 2 )
);
var brightLighting = new ChangeList(
"Bright",
"Makes light sources much more common.",
new ChangeInt ( hallwayLightsWeight, 450 ),
new ChangeInt ( ceilingLightsWeight, 450 ),
new ChangeInt ( lightsSpawnZeroWeight, 0 ),
new ChangeInt ( lightsSpawnOneWeight, 7 ),
new ChangeInt ( lightsSpawnTwoWeight, 3 )
);
var darkLighting = new ChangeList(
"Dark",
"Makes light sources much less common.",
new ChangeInt ( hallwayLightsWeight, 50 ),
new ChangeInt ( ceilingLightsWeight, 50 ),
new ChangeInt ( lightsSpawnZeroWeight, 2 ),
new ChangeInt ( lightsSpawnOneWeight, 8 ),
new ChangeInt ( lightsSpawnTwoWeight, 0 )
);
dungeonLightingChangeList = new List<ChangeList>() {
customChangeList,
defaultLighting,
brightLighting,
darkLighting
};
}
}
}

View File

@ -0,0 +1,131 @@
using BepInEx.Configuration;
using ScarletMansion.GamePatch.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ScarletMansion.Configs {
public class ConfigMain : ConfigManager<ConfigMain> {
public ConfigMain(ConfigFile cfg) : base(cfg) {
AutoGenerateOnChangeConfig(cfg);
}
public static ConfigEntryBundleItem decoFlashlight = new ConfigEntryBundleItem(dungeonItemPrefix, "Deco Pro Flashlight ", "Deco Pro Flashlight", 0, 0);
public static ConfigEntryBundleItem decoProFlashlight = new ConfigEntryBundleItem(dungeonItemPrefix, "Deco Flashlight ", "Deco Flashlight", 0, 0);
public static ConfigEntryBundleItem scarletKey = new ConfigEntryBundleItem(dungeonItemPrefix, "Scarlet Key ", "Scarlet Key", 0, 0);
public ConfigItem decoFlashlightValue = new ConfigItem("Deco Flashlight");
public ConfigItem decoProFlashlightValue = new ConfigItem("Deco Pro Flashlight");
public ConfigItem scarletKeyValue = new ConfigItem("Scarlet Key");
public static ConfigEntryBundleScrapItem crystal = new ConfigEntryBundleScrapItem("Dungeon Item Deco Crystal", "Decorative Crystal", 110, 150, 50);
public static ConfigEntryBundleScrapItem crystalBroken = new ConfigEntryBundleScrapItem("Dungeon Item Shattered Deco Crystal", "Shattered Decorative Crystal", 60, 80, 5);
public static ConfigEntryBundleScrapItem snowGlobe = new ConfigEntryBundleScrapItem("Dungeon Item Snow Globe", "Snow Globe", 150, 320, 40);
public static ConfigEntryBundleScrapItem gohei = new ConfigEntryBundleScrapItem("Dungeon Item Gohei", "Gohei", 70, 160, 60);
public ConfigScrapItem crystalValue = new ConfigScrapItem("Crystal");
public ConfigScrapItem crystalBrokenValue = new ConfigScrapItem("Shattered Crystal");
public ConfigScrapItem snowGlobeValue = new ConfigScrapItem("Snow Globe");
public ConfigScrapItem goheiValue = new ConfigScrapItem("Gohei");
public static ConfigEntryBundleSpawnableEnemy knightEnemy = new ConfigEntryBundleSpawnableEnemy("Dungeon Enemy Knight", "Knight", "Coil-head", 0, 13f);
public static ConfigEntryBundleSpawnableEnemy maidEnemy = new ConfigEntryBundleSpawnableEnemy("Dungeon Enemy Maid", "Maid", "Butler", 6, 5.5f);
public static ConfigEntryBundleEnemy revEnemy = new ConfigEntryBundleEnemy("Dungeon Enemy Revenant", "Revenant", 0, 14.5f);
public ConfigSpawnableEnemy knightEnemyValue = new ConfigSpawnableEnemy();
public ConfigSpawnableEnemy maidEnemyValue = new ConfigSpawnableEnemy();
public ConfigEnemy revEnemyValue = new ConfigEnemy();
// features
public static ConfigEntryBundle<int> shovelDamage = new ConfigEntryBundle<int>(
dungeonFeaturesPrefix,
"Door Shovel Damage",
45,
"The amount of damage done by swinging your shovel at a SDM door. Door health equals to 100.",
null,
new AcceptableValueRange<int>(0, 100)
);
public static ConfigEntryBundle<int> shotgunDamage = new ConfigEntryBundle<int>(
dungeonFeaturesPrefix,
"Door Shotgun Damage",
100,
"The amount of damage done by shotgun blasting at a SDM door. Door health equals to 100.",
null,
new AcceptableValueRange<int>(0, 100)
);
public static ConfigEntryBundle<float> lockedDoorEnemyDamageMultiplier = new ConfigEntryBundle<float>(
dungeonFeaturesPrefix,
"Locked Door Enemy Damage Multiplier",
0.5f,
"The damage multiplier applied to an enemy's attacks against a locked SDM door.\nSetting the value to 0 means that enemies can't break through a locked SDM door like in vanilla.",
null,
new AcceptableValueRange<float>(0f, 1f)
);
public static ConfigEntryBundle<bool> useSDMFireExits = new ConfigEntryBundle<bool>(
dungeonFeaturesPrefix,
"Use SDM Fire Exits",
true,
"If enabled, the dungeon will spawn its custom fire exits. If disabled, the dungeon will instead spawn the vanilla fire exits."
);
public int shovelDamageValue;
public int shotgunDamageValue;
public float lockedDoorEnemyDamageMultiplierValue;
public bool useSDMFireExitsValue;
// painting
public static ConfigEntryBundle<int> paintingValue = new ConfigEntryBundle<int>(
dungeonPaintingEventPrefix,
"Painting Value",
100,
"The scrap value of demonic paintings.",
null,
new AcceptableValueRange<int>(0, 400)
);
public static ConfigEntryBundle<bool> paintingSpawnEnemy = new ConfigEntryBundle<bool>(
dungeonPaintingEventPrefix,
"Spawn Enemy",
true,
"If enabled, an enemy will spawn when the bedroom's painting event ends."
);
public static ConfigEntryBundle<string> paintingEnemyList = new ConfigEntryBundle<string>(
dungeonPaintingEventPrefix,
"Enemy List",
"default",
$"The enemies that can spawn when the bedroom's painting event ends, in the form of a comma separated list of enemy names along with optional parameters separated or '@'.It can accept either the enemy's internal or display name.\n\nThe available parameters are:\nSpawn Logic: 's#' where '#' is either '0' for normal spawn logic or '1' for special spawn logic. Only the knight and jester have special spawn logic. When their special spawn logic is enabled, the knight will spawn properly in the bedroom and the jester will spawn already cranking.\n\nThe following string \"default\" is a preset that uses the following string \"{ScarletBedroom.ENEMY_SPAWN_LIST_DEFAULT}\"."
);
public static ConfigEntryBundle<bool> paintingEnemyEvilSkin = new ConfigEntryBundle<bool>(
dungeonPaintingEventPrefix,
"Invader Apperance",
true,
"Enemies spawned from the bedroom's painting event has a pitch black and red appearance. Only valid for the default enemy list."
);
public static ConfigEntryBundle<bool> facilityMeltdownActive = new ConfigEntryBundle<bool>(
dungeonPaintingEventPrefix,
"Activate Facility Meltdown",
false,
"If enabled, facility meltdown will activate when the bedroom's painting event ends. Requires FacilityMeltdown v2.4.7 or higher."
);
public int paintingValueValue;
public bool paintingSpawnEnemyValue;
public string paintingEnemyListValue;
public bool paintingEnemyEvilSkinValue;
public bool facilityMeltdownActiveValue;
}
}

View File

@ -0,0 +1,138 @@
using BepInEx.Configuration;
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
namespace ScarletMansion.Configs {
public partial class ConfigManager<T> : SyncedInstance<T> where T: ConfigManager<T> {
public static string GetConfigPrefix => typeof(T).Name;
public static string GetRequestMessage = $"{GetConfigPrefix}_OnRequestConfigSync";
public static string GetReceiveMessage = $"{GetConfigPrefix}_OnReceieveConfigSync";
public const string dungeonWeightPrefix = "Dungeon Weight";
public const string dungeonGenerationPrefix = "Dungeon Generation";
public const string dungeonGenerationBoundingBoxPrefix = "DunGen Bounding Box";
public const string dungeonGenerationMPathsPrefix = "DunGen Main Path";
public const string dungeonGenerationBPathPrefix = "DunGen Branching Path";
public const string dungeonGenerationBPathOnePrefix = "DunGen Branching Path 1";
public const string dungeonGenerationBPathTwoPrefix = "DunGen Branching Path 2";
public const string dungeonGenerationBPathThreePrefix = "DunGen Branching Path 3";
public const string dungeonItemPrefix = "Dungeon Items";
public const string dungeonEnemiesPrefix = "Dungeon Enemies";
public const string dungeonBalancePrefix = "Dungeon Balance";
public const string dungeonFeaturesPrefix = "Dungeon Features";
public const string dungeonPaintingEventPrefix = "Dungeon Painting Event";
public const string dungeonTreasureRoomPrefix = "Dungeon Treasure Rooms";
public const string dungeonLightingPrefix = "Lighting";
public const string presetsPrefix = "_Presets";
public ConfigManager(ConfigFile cfg){
InitInstance(this as T);
}
public void AutoGenerateOnChangeConfig(ConfigFile cfg, params object[] ignoreTargets){
var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
foreach(var p in fields){
var value = p.GetValue(this);
if (ignoreTargets.Contains(value)) continue;
var valueBundle = value as ConfigEntryBundleBase;
if (valueBundle != null){
var targetFieldName = $"{p.Name}Value";
var targetField = typeof(T).GetField(targetFieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
if (targetField == null) {
Plugin.logger.LogError($"Could not find field {targetFieldName}");
continue;
}
valueBundle.Bind(cfg, this as T, new ConfigEntryBundleBase.FieldPropertyInfo(targetField), this);
}
}
}
public static void RequestSync(){
if (!IsClient) return;
using (var stream = new FastBufferWriter(IntSize, Unity.Collections.Allocator.Temp)){
SendMessage(stream, GetRequestMessage);
}
}
public static void OnRequestSync(ulong clientId, FastBufferReader _){
if (!IsHost) return;
logger.LogDebug($"Config sync request received from client: {clientId}");
var array = SerializeToBytes(Instance);
var value = array.Length;
using (var stream = new FastBufferWriter(value + IntSize, Unity.Collections.Allocator.Temp)){
try {
stream.WriteValueSafe(value, default(FastBufferWriter.ForPrimitives));
stream.WriteBytesSafe(array);
SendMessage(stream, GetReceiveMessage, clientId);
} catch (Exception e){
logger.LogError($"Error occurred syncing config with client: {clientId}\n{e}");
}
}
}
public static void OnReceiveSync(ulong _, FastBufferReader reader){
if (!reader.TryBeginRead(IntSize)){
logger.LogError("Config sync error: Could not begin reading buffer.");
return;
}
reader.ReadValueSafe(out int val, default(FastBufferWriter.ForPrimitives));
if (!reader.TryBeginRead(val)){
logger.LogError("Config sync error: Host could not sync.");
return;
}
var data = new byte[val];
reader.ReadBytesSafe(ref data, val);
SyncInstance(data);
logger.LogDebug("Successfully synced config with host.");
}
[HarmonyPatch(typeof(GameNetcodeStuff.PlayerControllerB), "ConnectClientToPlayerObject")]
[HarmonyPostfix]
public static void InitializeLocalPlayer(){
if (IsHost){
MessageManager.RegisterNamedMessageHandler(GetRequestMessage, OnRequestSync);
Synced = true;
return;
}
Synced = false;
MessageManager.RegisterNamedMessageHandler(GetReceiveMessage, OnReceiveSync);
RequestSync();
}
[HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")]
[HarmonyPostfix]
public static void PlayerLeave(){
RevertSync();
}
public override string ToString() {
var variables = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
return string.Join(", ", variables.Select(v => $"[{v.Name}]{v.GetValue(this)}"));
}
public void SerializerTest(){
var bytes = SerializeToBytes(Instance);
var copy = DeserializeFromBytes(bytes);
Plugin.logger.LogDebug($"Inst: {Instance.ToString()}");
Plugin.logger.LogDebug($"Copy: {copy.ToString()}");
}
}
}

View File

@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using HarmonyLib;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using System.Reflection;
namespace ScarletMansion.Configs {
public partial class ConfigManager<T> : SyncedInstance<T> where T: ConfigManager<T> {
public abstract class ConfigEntryBundleBase {
public class FieldPropertyInfo {
public FieldInfo fieldInfo;
public PropertyInfo propertyInfo;
public FieldPropertyInfo(FieldInfo fieldInfo){
this.fieldInfo = fieldInfo;
}
public FieldPropertyInfo(PropertyInfo propertyInfo){
this.propertyInfo = propertyInfo;
}
public void SetValue(object obj, object value){
if (fieldInfo != null) fieldInfo.SetValue(obj, value);
if (propertyInfo != null) propertyInfo.SetValue(obj, value);
}
public object GetValue(object obj){
if (fieldInfo != null) return fieldInfo.GetValue(obj);
if (propertyInfo != null) return propertyInfo.GetValue(obj);
return null;
}
public Type GetFieldPropertyType(){
if (fieldInfo != null) return fieldInfo.FieldType;
if (propertyInfo != null) return propertyInfo.PropertyType;
return null;
}
}
public abstract IEnumerable<ConfigEntryBase> GetConfigs();
public abstract void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget);
}
public class ConfigEntryBundleExtraParameters {
public Action<T> onSettingsChanged;
public bool callOnBind;
public ConfigEntryBundleExtraParameters(Action<T> onSettingsChanged, bool callOnBind){
this.onSettingsChanged = onSettingsChanged;
this.callOnBind = callOnBind;
}
public void CallAction(T instance, bool isBind){
if (isBind && !callOnBind) return;
onSettingsChanged?.Invoke(instance);
}
}
public class ConfigEntryBundle<U> : ConfigEntryBundleBase {
public ConfigEntry<U> config;
public ConfigDefinition definition;
public U defaultValue;
public ConfigDescription description;
public ConfigEntryBundleExtraParameters extra;
public ConfigEntryBundle(string section, string key, U dvalue, string desc, ConfigEntryBundleExtraParameters extra = null, AcceptableValueBase valuebase = null){
definition = new ConfigDefinition(section, key);
defaultValue = dvalue;
description = new ConfigDescription(desc, valuebase);
if (extra == null) extra = new ConfigEntryBundleExtraParameters(null, false);
this.extra = extra;
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
config = cfg.Bind(definition, defaultValue, description);
config.SettingChanged += (obj, args) => {
Plugin.logger.LogDebug($"Config settings {config.Definition.Key} changed");
memberInfo.SetValue(memberTarget, config.Value);
extra.CallAction(instance, false);
};
memberInfo.SetValue(memberTarget, config.Value);
extra.CallAction(instance, true);
}
public void Bind(ConfigFile cfg, T instance){
config = cfg.Bind(definition, defaultValue, description);
config.SettingChanged += (obj, args) => {
Plugin.logger.LogDebug($"Config settings {config.Definition.Key} changed");
extra.CallAction(instance, false);
};
extra.CallAction(instance, true);
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return config;
}
}
public class ConfigEntryBundleMinMax<U> : ConfigEntryBundleBase {
public ConfigEntryBundle<U> min;
public ConfigEntryBundle<U> max;
public ConfigEntryBundleMinMax(string section, string keyMin, string keyMax, U defaultValueMin, U defaultValueMax, string descMin, string descMax, ConfigEntryBundleExtraParameters extra = null, AcceptableValueBase valuebase = null){
min = new ConfigEntryBundle<U>(section, keyMin, defaultValueMin, descMin, extra, valuebase);
max = new ConfigEntryBundle<U>(section, keyMax, defaultValueMax, descMax, extra, valuebase);
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var minProp = type.GetProperty("min", BindingFlags.Public | BindingFlags.Instance);
var maxProp = type.GetProperty("max", BindingFlags.Public | BindingFlags.Instance);
min.Bind(cfg, instance, new FieldPropertyInfo(minProp), memberInfo.GetValue(memberTarget));
max.Bind(cfg, instance, new FieldPropertyInfo(maxProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return min.config;
yield return max.config;
}
}
public class ConfigEntryBundleBranchingPath : ConfigEntryBundleBase {
public ConfigEntryBundleMinMax<int> count;
public ConfigEntryBundleMinMax<int> depth;
// branching path mini sections
private static string _branchPathBranchCountMessage(int section, string minmax, string middleSection) => $"The {minmax} amount of branching paths in Section {section} of the dungeon generation process.\n{middleSection}\n\n{_branchPathPostCountMessage}";
private static string _branchPathBranchDepthMessage(int section, string minmax, string middleSection) => $"The {minmax} length of the branching paths in Section {section} of the dungeon generation process.\n{middleSection}\n\n{_branchPathPostDepthMessage}";
private static readonly string _branchPathPostGenericMessage = "This is not an enforced number and the dungeon generation will proceed if it can't fit any tiles.";
private static readonly string _branchPathPostCountMessage = $"Each tile on the main path will try to generate a number of branching paths equal to this number. {_branchPathPostGenericMessage}";
private static readonly string _branchPathPostDepthMessage = $"Each branching path will try to generate a number of connecting tiles equal to this number. {_branchPathPostGenericMessage}";
public ConfigEntryBundleBranchingPath(string section, int sectionId, string extraMessage, int countMinValue, int countMaxValue, int depthMinValue, int depthMaxValue, ConfigEntryBundleExtraParameters extra = null){
count = new ConfigEntryBundleMinMax<int>(
section,
$"Section {sectionId} Count Min",
$"Section {sectionId} Count Max",
countMinValue,
countMaxValue,
_branchPathBranchCountMessage(sectionId, "minimum", extraMessage),
_branchPathBranchCountMessage(sectionId, "maximum", extraMessage),
extra,
new AcceptableValueRange<int>(0, 20)
);
depth = new ConfigEntryBundleMinMax<int>(
section,
$"Section {sectionId} Depth Min",
$"Section {sectionId} Depth Max",
depthMinValue,
depthMaxValue,
_branchPathBranchDepthMessage(sectionId, "minimum", extraMessage),
_branchPathBranchDepthMessage(sectionId, "maximum", extraMessage),
extra,
new AcceptableValueRange<int>(0, 20)
);
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var countField = type.GetField("count", BindingFlags.Public | BindingFlags.Instance);
var depthField = type.GetField("depth", BindingFlags.Public | BindingFlags.Instance);
count.Bind(cfg, instance, new FieldPropertyInfo(countField), memberInfo.GetValue(memberTarget));
depth.Bind(cfg, instance, new FieldPropertyInfo(depthField), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
foreach(var a in count.GetConfigs()) {
yield return a;
}
foreach(var a in depth.GetConfigs()) {
yield return a;
}
}
}
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.Configs {
public abstract class ConfigNumberRange<T> {
private string name;
[SerializeField] protected T _min;
[SerializeField] protected T _max;
public T min {
get {
return _min;
} set {
_min = value;
Verify();
}
}
public T max {
get {
return _max;
} set {
_max = value;
Verify();
}
}
public ConfigNumberRange(string name){
this.name = name;
_min = default;
_max = default;
}
public virtual void Verify(){
Plugin.logger.LogDebug($"Verifying min/max for {name}");
}
public override string ToString() {
return $"({min}, {max})";
}
}
[System.Serializable]
public class ConfigIntRange : ConfigNumberRange<int> {
public ConfigIntRange(string name) : base(name) { }
public override void Verify() {
base.Verify();
_max = Mathf.Max(_min, _max);
}
public DunGen.IntRange GetDungenIntRange(){
return new DunGen.IntRange(min, max);
}
}
[System.Serializable]
public class ConfigFloatRange : ConfigNumberRange<float> {
public ConfigFloatRange(string name) : base(name) { }
public override void Verify() {
base.Verify();
_max = Mathf.Max(_min, _max);
}
public DunGen.FloatRange GetDungenFloatRange(){
return new DunGen.FloatRange(min, max);
}
}
[System.Serializable]
public class ConfigBranchingPathRange {
private string name;
public ConfigIntRange count;
public ConfigIntRange depth;
public ConfigBranchingPathRange(string name){
this.name = name;
count = new ConfigIntRange($"{name} count");
depth = new ConfigIntRange($"{name} depth");
}
public void UpdateValues(DunGen.DungeonArchetype archetype){
archetype.BranchCount = new DunGen.IntRange(count.min, count.max);
archetype.BranchingDepth = new DunGen.IntRange(depth.min, depth.max);
}
public override string ToString() {
return $"{count.ToString()}+{depth.ToString()}";
}
}
[System.Serializable]
public class ConfigItem {
public bool enabled;
public ConfigIntRange valueRange;
public ConfigItem(string name) {
valueRange = new ConfigIntRange($"{name} value range");
}
}
[System.Serializable]
public class ConfigScrapItem : ConfigItem {
public int spawnWeight;
public bool spawnOnAllMoons;
public ConfigScrapItem(string name) : base(name) { }
}
[System.Serializable]
public class ConfigEnemy {
public bool enabled;
public int health;
public float speed;
}
[System.Serializable]
public class ConfigSpawnableEnemy : ConfigEnemy {
public int spawnWeightBase;
public float spawnWeightStealPercentage;
}
}

View File

@ -0,0 +1,80 @@
using BepInEx.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ScarletMansion.Configs {
public partial class ConfigManager<T> : SyncedInstance<T> where T: ConfigManager<T> {
public class ConfigEntryBundleEnemy : ConfigEntryBundleBase {
public ConfigEntryBundle<bool> enabled;
public ConfigEntryBundle<int> health;
public ConfigEntryBundle<float> speed;
public ConfigEntryBundleEnemy(string section, string enemyName, int baseHealth, float baseSpeed, ConfigEntryBundleExtraParameters extra = null){
var enabledDesc = $"If disabled, {enemyName} is disabled from spawning.";
var healthDesc = $"The health of the {enemyName}. A shovel is 1 damage.";
var speedDesc = $"The speed of the {enemyName} during their killing/aggressive stance. The player's default speed is 4.6.";
enabled = new ConfigEntryBundle<bool>(section, "Enabled", true, enabledDesc, extra);
health = new ConfigEntryBundle<int>(section, "Health", baseHealth, healthDesc, extra, new AcceptableValueRange<int>(1, 20));
speed = new ConfigEntryBundle<float>(section, "Speed", baseSpeed, speedDesc, extra, new AcceptableValueRange<float>(1f, 20f));
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var enabledProp = type.GetField("enabled", BindingFlags.Public | BindingFlags.Instance);
var healthProp = type.GetField("health", BindingFlags.Public | BindingFlags.Instance);
var speedProp = type.GetField("speed", BindingFlags.Public | BindingFlags.Instance);
enabled.Bind(cfg, instance, new FieldPropertyInfo(enabledProp), memberInfo.GetValue(memberTarget));
if (health.defaultValue > 0) health.Bind(cfg, instance, new FieldPropertyInfo(healthProp), memberInfo.GetValue(memberTarget));
speed.Bind(cfg, instance, new FieldPropertyInfo(speedProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return enabled.config;
if (health.defaultValue > 0) yield return health.config;
yield return speed.config;
}
}
public class ConfigEntryBundleSpawnableEnemy : ConfigEntryBundleEnemy {
public ConfigEntryBundle<int> spawnWeightBase;
public ConfigEntryBundle<float> spawnWeightStealPercentage;
public ConfigEntryBundleSpawnableEnemy(string section, string enemyName, string replacementEnemyName, int baseHealth, float baseSpeed, ConfigEntryBundleExtraParameters extra = null) : base(section, enemyName, baseHealth, baseSpeed, extra) {
var weightBaseDesc = $"The base spawn weight of the {enemyName}. This is added onto the spawn weight stolen from the {replacementEnemyName}, or the base weight of 10 if the moon doesn't spawn the {replacementEnemyName}.";
var weightStealPercentageDesc = $"The percentage of spawn weight that the {enemyName} steals from the {replacementEnemyName} for that moon.\nSetting this 0 means that the {replacementEnemyName}'s weight is unaffected and the {enemyName}'s weight is based entirely by Spawn Weight Base.\nSetting this 1 means the {enemyName} effectively replaces the {replacementEnemyName}.";
spawnWeightBase = new ConfigEntryBundle<int>(section, "Spawn Weight Base", 0, weightBaseDesc, extra, new AcceptableValueRange<int>(0, 999));
spawnWeightStealPercentage = new ConfigEntryBundle<float>(section, "Spawn Weight Steal Percentage", 0.75f, weightStealPercentageDesc, extra, new AcceptableValueRange<float>(0f, 1f));
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
base.Bind(cfg, instance, memberInfo, memberTarget);
var type = memberInfo.GetFieldPropertyType();
var weightBaseProp = type.GetField("spawnWeightBase", BindingFlags.Public | BindingFlags.Instance);
var weightStealPercentageProp = type.GetField("spawnWeightStealPercentage", BindingFlags.Public | BindingFlags.Instance);
spawnWeightBase.Bind(cfg, instance, new FieldPropertyInfo(weightBaseProp), memberInfo.GetValue(memberTarget));
spawnWeightStealPercentage.Bind(cfg, instance, new FieldPropertyInfo(weightStealPercentageProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
foreach(var a in base.GetConfigs()) {
yield return a;
}
yield return spawnWeightBase.config;
yield return spawnWeightStealPercentage.config;
}
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ScarletMansion.Configs {
public interface IConfigDungeon {
int GetDunGenWidthBase { get; }
int GetDunGenLengthBase { get; }
float GetDunGenWidthMultiFactor { get; }
float GetDunGenLengthMultiFactor { get; }
int GetMainPathCount { get; }
ConfigIntRange GetMainPathLength { get; }
bool GetBranchMultiSim { get; }
int GetBranchSimCount { get; }
ConfigBranchingPathRange GetBranchPathSectionOne { get; }
ConfigBranchingPathRange GetBranchPathSectionTwo { get; }
ConfigBranchingPathRange GetBranchPathSectionThree { get; }
int GetHallwayLightsWeight { get; }
int GetCeilingLightsWeight { get; }
int GetLightsSpawnZeroWeight { get; }
int GetLightsSpawnOneWeight { get; }
int GetLightsSpawnTwoWeight { get; }
float GetLootMultiplier { get; }
float GetMapHazardsMultiplier { get; }
int GetMinIndoorEnemySpawnCount { get; }
int GetPaintingCount { get; }
ConfigIntRange GetPaintingExtraLoot { get; }
int GetTreasureRoomCount { get; }
ConfigIntRange GetTreasureRoomLoot { get; }
}
}

View File

@ -0,0 +1,89 @@
using BepInEx.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ScarletMansion.Configs {
public partial class ConfigManager<T> : SyncedInstance<T> where T: ConfigManager<T> {
public class ConfigEntryBundleItem : ConfigEntryBundleBase {
public ConfigEntryBundle<bool> enabled;
public ConfigEntryBundleMinMax<int> valueRange;
private static string _valueMessage(string itemName, string minmax) => $"The {minmax} scrap value of {itemName}. Lethal Company multiplies all scrap values by 0.4.";
public ConfigEntryBundleItem(string section, string keyPrefix, string itemName, int baseValueMin, int baseValueMax, ConfigEntryBundleExtraParameters extra = null){
var enabledDesc = $"If disabled, {itemName} is disabled from spawning.";
enabled = new ConfigEntryBundle<bool>(section, $"{keyPrefix}Enabled", true, enabledDesc, extra);
valueRange = new ConfigEntryBundleMinMax<int>(
section,
$"Value Min",
$"Value Max",
baseValueMin,
baseValueMax,
_valueMessage(itemName, "minimum"),
_valueMessage(itemName, "maximum"),
extra,
new AcceptableValueRange<int>(1, 400)
);
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var enabledProp = type.GetField("enabled", BindingFlags.Public | BindingFlags.Instance);
var valueRangeProp = type.GetField("valueRange", BindingFlags.Public | BindingFlags.Instance);
enabled.Bind(cfg, instance, new FieldPropertyInfo(enabledProp), memberInfo.GetValue(memberTarget));
if (valueRange.min.defaultValue > 0) valueRange.Bind(cfg, instance, new FieldPropertyInfo(valueRangeProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return enabled.config;
if (valueRange.min.defaultValue > 0) {
foreach(var a in valueRange.GetConfigs()) {
yield return a;
}
}
}
}
public class ConfigEntryBundleScrapItem : ConfigEntryBundleItem {
public ConfigEntryBundle<int> spawnWeight;
public ConfigEntryBundle<bool> spawnOnAllMoons;
public ConfigEntryBundleScrapItem(string section, string itemName, int baseValueMin, int baseValueMax, int baseSpawnWeight, ConfigEntryBundleExtraParameters extra = null) : base(section, string.Empty, itemName, baseValueMin, baseValueMax, extra) {
var weightDesc = $"The {itemName}'s spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.";
var spawnDesc = $"If enabled, the {itemName} scrap item will spawn on all moons regardless if SDM loaded.";
spawnWeight = new ConfigEntryBundle<int>(section, "Spawn Weight Base", baseSpawnWeight, weightDesc, extra, new AcceptableValueRange<int>(0, 999));
spawnOnAllMoons = new ConfigEntryBundle<bool>(section, "Spawn On All Moons", false, spawnDesc, extra);
}
public override void Bind(ConfigFile cfg, T instance, FieldPropertyInfo memberInfo, object memberTarget) {
base.Bind(cfg, instance, memberInfo, memberTarget);
var type = memberInfo.GetFieldPropertyType();
var weightDescProp = type.GetField("spawnWeight", BindingFlags.Public | BindingFlags.Instance);
var spawnDescProp = type.GetField("spawnOnAllMoons", BindingFlags.Public | BindingFlags.Instance);
spawnWeight.Bind(cfg, instance, new FieldPropertyInfo(weightDescProp), memberInfo.GetValue(memberTarget));
spawnOnAllMoons.Bind(cfg, instance, new FieldPropertyInfo(spawnDescProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
foreach(var a in base.GetConfigs()) {
yield return a;
}
yield return spawnWeight.config;
yield return spawnOnAllMoons.config;
}
}
}
}

View File

@ -0,0 +1,268 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using BepInEx.Configuration;
namespace ScarletMansion.Configs {
public partial class ConfigManager<T> : SyncedInstance<T> where T: ConfigManager<T> {
public enum DungeonGeneration {
Custom,
Default,
Small,
MoreLoot,
BitMoreLoot,
Vanilla
}
public enum DungeonLighting {
Custom,
Default,
Bright,
Dark
}
public class ChangeList {
public string name;
public string description;
public Change[] changes;
public ChangeList(string name, string description, params Change[] changes){
this.name = name;
this.description = description;
this.changes = changes;
}
public ChangeList(string name, string description, ChangeList baseList, params Change[] changes){
this.name = name;
this.description = description;
var newChanges = new List<Change>();
foreach(var c in baseList.changes){
newChanges.Add(c);
}
foreach(var c in changes){
var added = false;
for(var i = 0; i < newChanges.Count; ++i) {
if (newChanges[i].IsEqual(c)){
newChanges[i] = c;
added = true;
break;
}
}
if (!added) newChanges.Add(c);
}
this.changes = newChanges.ToArray();
}
public void UpdateChanges(){
Plugin.logger.LogDebug($"Updating config values with preset {name}");
foreach(var c in changes){
c.Update();
}
Plugin.Instance.Config.Save();
Plugin.Instance.Config.Reload();
}
public override string ToString() {
var str = string.Empty;
foreach(var c in changes){
str += c.ToString() + "\n";
}
if (string.IsNullOrWhiteSpace(str)){
str = "No changes.\n";
}
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<U> : Change {
public ConfigEntryBundle<U> configBundle;
public ConfigEntry<U> configEntry => configBundle.config;
public U value;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return configEntry;
}
}
public ChangeType(ConfigEntryBundle<U> configBundle){
this.configBundle = configBundle;
this.value = configBundle.defaultValue;
}
public ChangeType(ConfigEntryBundle<U> configBundle, U value){
this.configBundle = configBundle;
this.value = value;
}
public override void Update(){
configEntry.BoxedValue = value;
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeType<U>;
if (target != null) {
return configEntry.Definition == target.configEntry.Definition;
}
return false;
}
public override string ToString() {
return $"{configBundle.definition.Key}: {value}";
}
}
public class ChangeBool : ChangeType<bool> {
public ChangeBool(ConfigEntryBundle<bool> configEntry) : base (configEntry){ }
public ChangeBool(ConfigEntryBundle<bool> configEntry, bool value) : base (configEntry, value){ }
}
public class ChangeFloat : ChangeType<float> {
public ChangeFloat(ConfigEntryBundle<float> configEntry) : base (configEntry){ }
public ChangeFloat(ConfigEntryBundle<float> configEntry, float value) : base (configEntry, value){ }
}
public class ChangeInt : ChangeType<int> {
public ChangeInt(ConfigEntryBundle<int> configEntry) : base (configEntry){ }
public ChangeInt(ConfigEntryBundle<int> configEntry, int value) : base (configEntry, value){ }
}
public class ChangeMinMaxInt : Change {
public ChangeInt min;
public ChangeInt max;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return min.configEntry;
yield return max.configEntry;
}
}
public ChangeMinMaxInt(ConfigEntryBundleMinMax<int> configBundle){
min = new ChangeInt(configBundle.min);
max = new ChangeInt(configBundle.max);
}
public ChangeMinMaxInt(ConfigEntryBundleMinMax<int> configBundle, int minValue, int maxValue){
min = new ChangeInt(configBundle.min, minValue == int.MaxValue ? configBundle.min.defaultValue : minValue);
max = new ChangeInt(configBundle.max, maxValue == int.MaxValue ? configBundle.max.defaultValue : maxValue);
}
public override void Update() {
min.Update();
max.Update();
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeMinMaxInt;
if (target != null) return min.IsEqual(target.min) && max.IsEqual(target.max);
return false;
}
public override string ToString() {
return $"{min.ToString()}\n{max.ToString()}";
}
}
public class ChangeMinMaxFloat : Change{
public ChangeFloat min;
public ChangeFloat max;
public override IEnumerable<ConfigEntryBase> configBases {
get {
yield return min.configEntry;
yield return max.configEntry;
}
}
public ChangeMinMaxFloat(ConfigEntryBundleMinMax<float> configBundle){
min = new ChangeFloat(configBundle.min);
max = new ChangeFloat(configBundle.max);
}
public ChangeMinMaxFloat(ConfigEntryBundleMinMax<float> configBundle, float minValue, float maxValue){
min = new ChangeFloat(configBundle.min, minValue == float.MaxValue ? configBundle.min.defaultValue : minValue);
max = new ChangeFloat(configBundle.max, maxValue == float.MaxValue ? configBundle.max.defaultValue : maxValue);
}
public override void Update() {
min.Update();
max.Update();
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeMinMaxFloat;
if (target != null) return min.IsEqual(target.min) && max.IsEqual(target.max);
return false;
}
public override string ToString() {
return $"{min.ToString()}\n{max.ToString()}";
}
}
public class ChangeBranchingPath : Change{
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(ConfigEntryBundleBranchingPath configBundle){
count = new ChangeMinMaxInt(configBundle.count);
depth = new ChangeMinMaxInt(configBundle.depth);
}
public ChangeBranchingPath(ConfigEntryBundleBranchingPath configBundle, int countMin, int countMax, int depthMin, int depthMax){
count = new ChangeMinMaxInt(configBundle.count, countMin, countMax);
depth = new ChangeMinMaxInt(configBundle.depth, depthMin, depthMax);
}
public override void Update() {
count.Update();
depth.Update();
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeBranchingPath;
if (target != null) return count.IsEqual(target.count) && depth.IsEqual(target.depth);
return false;
}
public override string ToString() {
return $"{count.ToString()}\n{depth.ToString()}";
}
}
}
}

View File

@ -13,6 +13,8 @@ using UnityEngine.Rendering.HighDefinition;
using DunGenPlus.Collections; using DunGenPlus.Collections;
using GameNetcodeStuff; using GameNetcodeStuff;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using ScarletMansion.Configs;
using DunGen.Graph;
namespace ScarletMansion.DunGenPatch { namespace ScarletMansion.DunGenPatch {
public static class Patch { public static class Patch {
@ -65,40 +67,58 @@ namespace ScarletMansion.DunGenPatch {
} }
public static void UpdateDunGenExtenderProperties(DunGenExtenderProperties props, EventCallbackScenario callback) { public static void UpdateDunGenExtenderProperties<T>(ConfigDungeon<T> dungeonConfig, DunGenExtenderProperties props, EventCallbackScenario callback) where T: ConfigDungeon<T> {
if (callback.IsDevDebug) return; if (callback.IsDevDebug) return;
props.MainPathProperties.MainPathCount = PluginConfig.Instance.mainPathCountValue; props.MainPathProperties.MainPathCount = dungeonConfig.mainPathCountValue;
props.DungeonBoundsProperties.SizeBase = new Vector3(PluginConfig.Instance.dunGenWidthBaseValue, props.DungeonBoundsProperties.SizeBase.y, PluginConfig.Instance.dunGenLengthBaseValue); props.DungeonBoundsProperties.SizeBase = new Vector3(dungeonConfig.dunGenWidthBaseValue, props.DungeonBoundsProperties.SizeBase.y, dungeonConfig.dunGenLengthBaseValue);
props.DungeonBoundsProperties.SizeFactor = new Vector3(PluginConfig.Instance.dunGenWidthMultiFactorValue, props.DungeonBoundsProperties.SizeFactor.y, PluginConfig.Instance.dunGenLengthMultiFactorValue); props.DungeonBoundsProperties.SizeFactor = new Vector3(dungeonConfig.dunGenWidthMultiFactorValue, props.DungeonBoundsProperties.SizeFactor.y, dungeonConfig.dunGenLengthMultiFactorValue);
props.BranchPathMultiSimulationProperties.UseBranchPathMultiSim = PluginConfig.Instance.branchMultiSimValue; props.BranchPathMultiSimulationProperties.UseBranchPathMultiSim = dungeonConfig.branchMultiSimValue;
props.BranchPathMultiSimulationProperties.SimulationCount = PluginConfig.Instance.branchSimCountValue; props.BranchPathMultiSimulationProperties.SimulationCount = dungeonConfig.branchSimCountValue;
var i = 0; var i = 0;
while(i < 3) { while(i < 3) {
PluginConfig.Instance.branchPathSectionOneValue.UpdateValues(props.LineRandomizerProperties.Archetypes[i]); dungeonConfig.branchPathSectionOneValue.UpdateValues(props.LineRandomizerProperties.Archetypes[i]);
++i; ++i;
} }
while(i < 7) { while(i < 7) {
PluginConfig.Instance.branchPathSectionTwoValue.UpdateValues(props.LineRandomizerProperties.Archetypes[i]); dungeonConfig.branchPathSectionTwoValue.UpdateValues(props.LineRandomizerProperties.Archetypes[i]);
++i; ++i;
} }
while(i < 10) { while(i < 10) {
PluginConfig.Instance.branchPathSectionThreeValue.UpdateValues(props.LineRandomizerProperties.Archetypes[i]); dungeonConfig.branchPathSectionThreeValue.UpdateValues(props.LineRandomizerProperties.Archetypes[i]);
++i; ++i;
} }
var count = PluginConfig.Instance.treasureRoomCountValue; var count = dungeonConfig.treasureRoomCountValue;
props.AdditionalTilesProperties.UseAdditionalTiles = count > 0; props.AdditionalTilesProperties.UseAdditionalTiles = count > 0;
var addTilesProperties = props.AdditionalTilesProperties; var addTilesProperties = props.AdditionalTilesProperties;
var copyTarget = addTilesProperties.AdditionalTileSets[0]; var copyTarget = addTilesProperties.AdditionalTileSets[0];
addTilesProperties.AdditionalTileSets = new List<AdditionalTileSetList>(); addTilesProperties.AdditionalTileSets = new List<AdditionalTileSetList>();
for(var j = 0; j < count; ++j) addTilesProperties.AdditionalTileSets.Add(copyTarget); for(var j = 0; j < count; ++j) addTilesProperties.AdditionalTileSets.Add(copyTarget);
Lights.ScarletLightCleanup.weights = new float[] {
dungeonConfig.lightsSpawnZeroWeightValue,
dungeonConfig.lightsSpawnOneWeightValue,
dungeonConfig.lightsSpawnTwoWeightValue
};
DungeonFlow.GlobalPropSettings GetGlobalPropSetting(int id) {
foreach(var p in dungeonConfig.dungeon.GlobalProps){
if (p.ID == id ) return p;
}
return null;
}
GetGlobalPropSetting(254).Count = new DunGen.IntRange(
dungeonConfig.paintingCountValue,
dungeonConfig.paintingCountValue
);
} }
} }

View File

@ -10,6 +10,7 @@ using DunGen;
using ScarletMansion.Lights; using ScarletMansion.Lights;
using ScarletMansion.GamePatch.Managers; using ScarletMansion.GamePatch.Managers;
using GameNetcodeStuff; using GameNetcodeStuff;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch.Components { namespace ScarletMansion.GamePatch.Components {
public class ScarletBedroom : MonoBehaviour, IDungeonCompleteReceiver { public class ScarletBedroom : MonoBehaviour, IDungeonCompleteReceiver {
@ -126,7 +127,7 @@ namespace ScarletMansion.GamePatch.Components {
private static List<EnemyReferenceSpawnLogic> spawnableEnemiesTrueList; private static List<EnemyReferenceSpawnLogic> spawnableEnemiesTrueList;
public static void CreateRandomEnemyList(List<SpawnableEnemyWithRarity> enemies){ public static void CreateRandomEnemyList(List<SpawnableEnemyWithRarity> enemies){
var lower = PluginConfig.Instance.paintingEnemyListValue.ToLowerInvariant(); var lower = ConfigMain.Instance.paintingEnemyListValue.ToLowerInvariant();
if (lower == "default"){ if (lower == "default"){
lower = ENEMY_SPAWN_LIST_DEFAULT; lower = ENEMY_SPAWN_LIST_DEFAULT;
} }
@ -155,7 +156,7 @@ namespace ScarletMansion.GamePatch.Components {
public EnemyReferenceSpawnLogic GetRandomEnemy(System.Random sysRandom){ public EnemyReferenceSpawnLogic GetRandomEnemy(System.Random sysRandom){
var roundManager = RoundManager.Instance; var roundManager = RoundManager.Instance;
if (!roundManager.IsServer) return null; if (!roundManager.IsServer) return null;
if (!PluginConfig.Instance.paintingSpawnEnemyValue) return null; if (!ConfigMain.Instance.paintingSpawnEnemyValue) return null;
if (spawnableEnemiesTrueList == null || spawnableEnemiesTrueList.Count == 0){ if (spawnableEnemiesTrueList == null || spawnableEnemiesTrueList.Count == 0){
Plugin.logger.LogError($"Could not select enemy to spawn in bedroom. Empty enemy list?"); Plugin.logger.LogError($"Could not select enemy to spawn in bedroom. Empty enemy list?");
@ -222,7 +223,7 @@ namespace ScarletMansion.GamePatch.Components {
var sysRandom = DunGenPatch.Patch.CreateSystemRandom(); var sysRandom = DunGenPatch.Patch.CreateSystemRandom();
Utility.Shuffle(sysRandom, itemSpawns); Utility.Shuffle(sysRandom, itemSpawns);
var count = sysRandom.Next(PluginConfig.Instance.paintingExtraLootValue.min, PluginConfig.Instance.paintingExtraLootValue.max + 1); var count = sysRandom.Next(Plugin.GetConfigDungeon.GetPaintingExtraLoot.min, Plugin.GetConfigDungeon.GetPaintingExtraLoot.max + 1);
bonusItems = AngerManager.CreateAngerLoot(count, sysRandom); bonusItems = AngerManager.CreateAngerLoot(count, sysRandom);
bonusEnemy = GetRandomEnemy(sysRandom); bonusEnemy = GetRandomEnemy(sysRandom);

View File

@ -7,6 +7,7 @@ using UnityEngine;
using Unity.Netcode; using Unity.Netcode;
using ScarletMansion.GamePatch.Enemies; using ScarletMansion.GamePatch.Enemies;
using System.Security.Permissions; using System.Security.Permissions;
using ScarletMansion.Configs;
[assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )] [assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )]
namespace ScarletMansion.GamePatch.Components { namespace ScarletMansion.GamePatch.Components {
@ -68,7 +69,7 @@ namespace ScarletMansion.GamePatch.Components {
direction = direction.normalized; direction = direction.normalized;
var timeDelta = Time.deltaTime * GetDoorDamagePerSecond(enemyScript); var timeDelta = Time.deltaTime * GetDoorDamagePerSecond(enemyScript);
if (isLocked) timeDelta *= PluginConfig.Instance.lockedDoorEnemyDamageMultiplierValue; if (isLocked) timeDelta *= ConfigMain.Instance.lockedDoorEnemyDamageMultiplierValue;
destroyMeter += timeDelta; destroyMeter += timeDelta;
ApplyDoorPieceDamage(direction); ApplyDoorPieceDamage(direction);

View File

@ -8,6 +8,7 @@ using Unity.Netcode;
using UnityEngine.AI; using UnityEngine.AI;
using GameNetcodeStuff; using GameNetcodeStuff;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using ScarletMansion.Configs;
namespace ScarletMansion { namespace ScarletMansion {
@ -26,7 +27,7 @@ namespace ScarletMansion {
public override void Start(){ public override void Start(){
base.Start(); base.Start();
maxChaseSpeed = PluginConfig.Instance.revEnemyValue.speed; maxChaseSpeed = ConfigMain.Instance.revEnemyValue.speed;
if (IsOwner && KnightSpawnManager.Instance) { if (IsOwner && KnightSpawnManager.Instance) {
var index = KnightSpawnManager.Instance.GetSpawnPointIndex(); var index = KnightSpawnManager.Instance.GetSpawnPointIndex();

View File

@ -1,4 +1,5 @@
using GameNetcodeStuff; using GameNetcodeStuff;
using ScarletMansion.Configs;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -51,7 +52,7 @@ namespace ScarletMansion.GamePatch.Enemies {
public override void Start(){ public override void Start(){
base.Start(); base.Start();
currentChaseSpeed = PluginConfig.Instance.knightEnemyValue.speed; currentChaseSpeed = ConfigMain.Instance.knightEnemyValue.speed;
if (IsOwner && KnightSpawnManager.Instance) { if (IsOwner && KnightSpawnManager.Instance) {
var index = KnightSpawnManager.Instance.GetSpawnPointIndex(); var index = KnightSpawnManager.Instance.GetSpawnPointIndex();

View File

@ -1,6 +1,7 @@
//using System; //using System;
using System.Collections; using System.Collections;
using GameNetcodeStuff; using GameNetcodeStuff;
using ScarletMansion.Configs;
using ScarletMansion.GamePatch.Items; using ScarletMansion.GamePatch.Items;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
@ -246,8 +247,8 @@ namespace ScarletMansion.GamePatch.Enemies {
MaidVariant.murderMusicAudio = chaseMusic; MaidVariant.murderMusicAudio = chaseMusic;
} }
enemyHP = PluginConfig.Instance.maidEnemyValue.health; enemyHP = ConfigMain.Instance.maidEnemyValue.health;
chaseMovementSpeedBase = PluginConfig.Instance.maidEnemyValue.speed; chaseMovementSpeedBase = ConfigMain.Instance.maidEnemyValue.speed;
if (StartOfRound.Instance.connectedPlayersAmount == 0){ if (StartOfRound.Instance.connectedPlayersAmount == 0){
enemyHP = 2; enemyHP = 2;
@ -272,7 +273,7 @@ namespace ScarletMansion.GamePatch.Enemies {
knifeRender.SetActive(false); knifeRender.SetActive(false);
if (IsServer) { if (IsServer) {
if (PluginConfig.Instance.revEnemyValue.enabled){ if (ConfigMain.Instance.revEnemyValue.enabled){
var enemy = ScarletNetworkManagerUtility.CreateEnemyWithType<KnightGhostVariant>(knightGhostEnemy, transform.position, 0f); var enemy = ScarletNetworkManagerUtility.CreateEnemyWithType<KnightGhostVariant>(knightGhostEnemy, transform.position, 0f);
enemy.FindAndTunnelPlayer(transform.position); enemy.FindAndTunnelPlayer(transform.position);
} }

View File

@ -11,7 +11,7 @@ namespace ScarletMansion.GamePatch.FixValues {
public void Awake(){ public void Awake(){
var weight = target.Props.Weights[1]; var weight = target.Props.Weights[1];
var value = PluginConfig.Instance.ceilingLightsWeightValue; var value = Plugin.GetConfigDungeon.GetCeilingLightsWeight;
weight.MainPathWeight = value; weight.MainPathWeight = value;
weight.BranchPathWeight = value; weight.BranchPathWeight = value;
} }

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch.FixValues { namespace ScarletMansion.GamePatch.FixValues {
public class FixFireExit : FixBaseClass<SpawnSyncedObject> { public class FixFireExit : FixBaseClass<SpawnSyncedObject> {
@ -26,7 +27,7 @@ namespace ScarletMansion.GamePatch.FixValues {
public GameObject vanillaCube001GameObject; public GameObject vanillaCube001GameObject;
public GameObject vanillaCube002GameObject; public GameObject vanillaCube002GameObject;
public bool EnableVanillaFireExit => !PluginConfig.Instance.useSDMFireExitsValue && vanillaSetup; public bool EnableVanillaFireExit => !ConfigMain.Instance.useSDMFireExitsValue && vanillaSetup;
public void Awake(){ public void Awake(){
if (EnableVanillaFireExit){ if (EnableVanillaFireExit){

View File

@ -11,7 +11,7 @@ namespace ScarletMansion.GamePatch.FixValues {
public void Awake(){ public void Awake(){
var weight = target.Props.Weights[1]; var weight = target.Props.Weights[1];
var value = PluginConfig.Instance.hallwayLightsWeightValue; var value = Plugin.GetConfigDungeon.GetHallwayLightsWeight;
weight.MainPathWeight = value; weight.MainPathWeight = value;
weight.BranchPathWeight = value; weight.BranchPathWeight = value;
} }

View File

@ -10,7 +10,7 @@ namespace ScarletMansion.GamePatch.FixValues
{ {
public class FixTreasureRoom : FixBaseClass<LocalPropSetBasic> { public class FixTreasureRoom : FixBaseClass<LocalPropSetBasic> {
public void Awake(){ public void Awake(){
var loot = PluginConfig.Instance.treasureRoomLootValue; var loot = Plugin.GetConfigDungeon.GetTreasureRoomLoot;
target.propCount = new IntRange(loot.min, loot.max); target.propCount = new IntRange(loot.min, loot.max);
} }

View File

@ -18,6 +18,7 @@ using ScarletMansion.GamePatch.Enemies;
using UnityEngine.UI; using UnityEngine.UI;
using ScarletMansion.GamePatch.Items; using ScarletMansion.GamePatch.Items;
using DunGenPlus.Managers; using DunGenPlus.Managers;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch { namespace ScarletMansion.GamePatch {
@ -170,10 +171,10 @@ namespace ScarletMansion.GamePatch {
var itemsList = round.allItemsList.itemsList; var itemsList = round.allItemsList.itemsList;
void QuickItemFix(Item itemToFix, Item itemReference){ void QuickItemFix(Item itemToFix, Item itemReference){
itemToFix.itemIcon = itemReference.itemIcon; if (itemToFix.itemIcon == null) itemToFix.itemIcon = itemReference.itemIcon;
itemToFix.grabSFX = itemReference.grabSFX; if (itemToFix.grabSFX == null) itemToFix.grabSFX = itemReference.grabSFX;
itemToFix.dropSFX = itemReference.dropSFX; if (itemToFix.dropSFX == null) itemToFix.dropSFX = itemReference.dropSFX;
itemToFix.pocketSFX = itemReference.pocketSFX; if (itemToFix.pocketSFX == null) itemToFix.pocketSFX = itemReference.pocketSFX;
} }
var magClass = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "magnifyingglass"); var magClass = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "magnifyingglass");
@ -181,6 +182,7 @@ namespace ScarletMansion.GamePatch {
QuickItemFix(Assets.scrapItems[0].item, magClass); QuickItemFix(Assets.scrapItems[0].item, magClass);
QuickItemFix(Assets.scrapItems[1].item, magClass); QuickItemFix(Assets.scrapItems[1].item, magClass);
QuickItemFix(Assets.scrapItems[4].item, magClass); QuickItemFix(Assets.scrapItems[4].item, magClass);
Assets.scrapItems[5].item.itemIcon = magClass.itemIcon;
} }
var paintClass = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "fancypainting"); var paintClass = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "fancypainting");
@ -265,6 +267,16 @@ namespace ScarletMansion.GamePatch {
Utility.FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle); Utility.FixParticleSystemMaterial(scarletScript.buffedParticleSystem, knifeScript.bloodParticle);
} }
var shovelItem = itemsList.FirstOrDefault(i => i.name.ToLowerInvariant() == "shovel");
if (GameReadNullCheck(shovelItem, "shovel", "Item will have missing image and sound assets")){
QuickItemFix(Assets.scrapItems[5].item, shovelItem);
var shovelScript = shovelItem.spawnPrefab.GetComponent<Shovel>();
var goheiScript = Assets.scrapItems[5].item.spawnPrefab.GetComponent<ScarletGohei>();
goheiScript.reelUp = shovelScript.reelUp;
goheiScript.swing = shovelScript.swing;
}
if (Assets.flashlight.item == null) { if (Assets.flashlight.item == null) {
UpdateFlashlight(Assets.flashlight, "proflashlight", 2); UpdateFlashlight(Assets.flashlight, "proflashlight", 2);
} }
@ -357,35 +369,19 @@ namespace ScarletMansion.GamePatch {
} }
private static IEnumerator UpdateNetworkConfig(RoundManager roundManager){ private static IEnumerator UpdateNetworkConfig(RoundManager roundManager){
while(PluginConfig.Synced == false){ while(ConfigMain.Synced == false || ConfigDungeonFoyer.Synced == false){
yield return null; yield return null;
} }
Lights.ScarletLightCleanup.weights = new float[] {
PluginConfig.Instance.lightsSpawnZeroWeightValue,
PluginConfig.Instance.lightsSpawnOneWeightValue,
PluginConfig.Instance.lightsSpawnTwoWeightValue
};
Assets.dungeon.Length = PluginConfig.Instance.mainPathLengthValue.GetDungenIntRange();
DungeonFlow.GlobalPropSettings GetGlobalPropSetting(int id) {
foreach(var p in Assets.dungeon.GlobalProps){
if (p.ID == id ) return p;
}
return null;
}
GetGlobalPropSetting(254).Count = new DunGen.IntRange(
PluginConfig.Instance.paintingCountValue,
PluginConfig.Instance.paintingCountValue
);
foreach(var scrap in Assets.scrapItems){ foreach(var scrap in Assets.scrapItems){
scrap.UpdateItemValue(); scrap.UpdateItemValue();
} }
Assets.dungeonExtended.OverrideKeyPrefab = Assets.key.GetConfigItemEntry().enabled ? Assets.key.item.spawnPrefab : typeof(DungeonLoader).GetField("defaultKeyPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null) as GameObject; ConfigDungeonFoyer.Instance.dungeon.Length = ConfigDungeonFoyer.Instance.mainPathLengthValue.GetDungenIntRange();
ConfigDungeonFoyer.Instance.dungeonExtended.OverrideKeyPrefab = Assets.key.GetConfigItemEntry().enabled ?
Assets.key.item.spawnPrefab :
typeof(DungeonLoader).GetField("defaultKeyPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null) as GameObject;
Plugin.logger.LogDebug("Set networked config values"); Plugin.logger.LogDebug("Set networked config values");
} }

View File

@ -3,8 +3,6 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using GameNetcodeStuff; using GameNetcodeStuff;
using OdinSerializer;
using ScarletMansion.GamePatch.Components;
using ScarletMansion.GamePatch.Enemies; using ScarletMansion.GamePatch.Enemies;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
@ -165,7 +163,7 @@ namespace ScarletMansion.GamePatch.Items
if (flag) { if (flag) {
RoundManager.PlayRandomClip(knifeAudio, hitSFX); RoundManager.PlayRandomClip(knifeAudio, hitSFX);
UnityEngine.Object.FindObjectOfType<RoundManager>().PlayAudibleNoise(base.transform.position, 17f, 0.8f); RoundManager.Instance.PlayAudibleNoise(base.transform.position, 17f, 0.8f);
if (!flag2 && num != -1) { if (!flag2 && num != -1) {
knifeAudio.PlayOneShot(StartOfRound.Instance.footstepSurfaces[num].hitSurfaceSFX); knifeAudio.PlayOneShot(StartOfRound.Instance.footstepSurfaces[num].hitSurfaceSFX);
WalkieTalkie.TransmitOneShotAudio(knifeAudio, StartOfRound.Instance.footstepSurfaces[num].hitSurfaceSFX); WalkieTalkie.TransmitOneShotAudio(knifeAudio, StartOfRound.Instance.footstepSurfaces[num].hitSurfaceSFX);

View File

@ -8,6 +8,7 @@ using UnityEngine;
using Unity.Netcode; using Unity.Netcode;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using ScarletMansion.GamePatch.Managers; using ScarletMansion.GamePatch.Managers;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch.Items { namespace ScarletMansion.GamePatch.Items {
public class ScarletPainting : GrabbableObject { public class ScarletPainting : GrabbableObject {
@ -51,7 +52,7 @@ namespace ScarletMansion.GamePatch.Items {
manager.AddPainting(this); manager.AddPainting(this);
bedroom = manager.GetBedroomWithPainting(transform.position); bedroom = manager.GetBedroomWithPainting(transform.position);
scrapValue = PluginConfig.Instance.paintingValueValue; scrapValue = ConfigMain.Instance.paintingValueValue;
grabbedByEvent = false; grabbedByEvent = false;
} else { } else {
transform.rotation = Quaternion.Euler(itemProperties.restingRotation); // why zeekers transform.rotation = Quaternion.Euler(itemProperties.restingRotation); // why zeekers

View File

@ -6,12 +6,15 @@ using System.Reflection.Emit;
using System.Reflection; using System.Reflection;
using DunGenPlus.Managers; using DunGenPlus.Managers;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch { namespace ScarletMansion.GamePatch {
public class LoadAssetsIntoLevelPatch { public class LoadAssetsIntoLevelPatch {
public static void AddItemsLocal(RoundManager roundManager){ public static void AddItemsLocal(RoundManager roundManager){
if (DunGenPlus.API.IsDevDebugModeActive()) return;
foreach(var item in Assets.scrapItems){ foreach(var item in Assets.scrapItems){
if (item.SpawnsOnMap) { if (item.SpawnsOnMap) {
var config = item.GetConfigScrapItemEntry(); var config = item.GetConfigScrapItemEntry();
@ -24,6 +27,8 @@ namespace ScarletMansion.GamePatch {
} }
public static void AddItemsGlobal(RoundManager roundManager){ public static void AddItemsGlobal(RoundManager roundManager){
if (DunGenPlus.API.IsDevDebugModeActive()) return;
foreach(var item in Assets.scrapItems){ foreach(var item in Assets.scrapItems){
if (item.SpawnsOnMap) { if (item.SpawnsOnMap) {
var config = item.GetConfigScrapItemEntry(); var config = item.GetConfigScrapItemEntry();
@ -36,14 +41,15 @@ namespace ScarletMansion.GamePatch {
} }
public static void AddEnemiesLocal(RoundManager roundManager) { public static void AddEnemiesLocal(RoundManager roundManager) {
if (DunGenPlus.API.IsDevDebugModeActive()) return;
var currentEnemies = roundManager.currentLevel.Enemies; var currentEnemies = roundManager.currentLevel.Enemies;
void AddEnemy(Assets.Enemy enemy, string sourceEnemyName, string targetEnemyName, PluginConfig.SpawnableEnemy enemyConfig) { void AddEnemy(Assets.Enemy enemy, string sourceEnemyName, string targetEnemyName, ConfigSpawnableEnemy enemyConfig) {
const int minBaseWeight = 10; const int minBaseWeight = 10;
if (!enemyConfig.enabled) { if (!enemyConfig.enabled) {
Plugin.logger.LogDebug($"{sourceEnemyName} is disblaed. Not loading onto moon."); Plugin.logger.LogDebug($"{sourceEnemyName} is disabled. Not loading onto moon.");
return; return;
} }
@ -76,8 +82,8 @@ namespace ScarletMansion.GamePatch {
} }
} }
AddEnemy(Assets.knight, "knight", "springman", PluginConfig.Instance.knightEnemyValue); AddEnemy(Assets.knight, "knight", "springman", ConfigMain.Instance.knightEnemyValue);
AddEnemy(Assets.maid, "maid", "butler", PluginConfig.Instance.maidEnemyValue); AddEnemy(Assets.maid, "maid", "butler", ConfigMain.Instance.maidEnemyValue);
ScarletBedroom.CreateRandomEnemyList(currentEnemies); ScarletBedroom.CreateRandomEnemyList(currentEnemies);
} }

View File

@ -73,7 +73,7 @@ namespace ScarletMansion.GamePatch.Managers {
} }
public void OnDungeonComplete(Dungeon dungeon) { public void OnDungeonComplete(Dungeon dungeon) {
Anger(PluginConfig.Instance.minIndoorEnemySpawnCountValue); Anger(Plugin.GetConfigDungeon.GetMinIndoorEnemySpawnCount);
} }
public void TriggerAngerLightBrief(float duration){ public void TriggerAngerLightBrief(float duration){

View File

@ -12,6 +12,7 @@ using ScarletMansion.GamePatch;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using static LethalLevelLoader.ExtendedEvent; using static LethalLevelLoader.ExtendedEvent;
using ScarletMansion.GamePatch.Managers; using ScarletMansion.GamePatch.Managers;
using ScarletMansion.Configs;
namespace ScarletMansion { namespace ScarletMansion {
@ -266,7 +267,7 @@ namespace ScarletMansion {
public void RequestEvilSkinApply(NetworkObjectReference reference, string enemyName) { public void RequestEvilSkinApply(NetworkObjectReference reference, string enemyName) {
if (PluginConfig.Instance.paintingEnemyEvilSkinValue && ScarletBedroom.ENEMY_EVIL_LIST.Contains(enemyName)) { if (ConfigMain.Instance.paintingEnemyEvilSkinValue && ScarletBedroom.ENEMY_EVIL_LIST.Contains(enemyName)) {
RequestEvilSkinApplyClientRpc(reference); RequestEvilSkinApplyClientRpc(reference);
} }
} }

View File

@ -40,14 +40,14 @@ namespace ScarletMansion.GamePatch {
public static float ModifyScrapCount(float count){ public static float ModifyScrapCount(float count){
if (DunGenPatch.Patch.active == false) return count; if (DunGenPatch.Patch.active == false) return count;
Plugin.logger.LogDebug($"Scrap: {count} -> {count * PluginConfig.Instance.lootMultiplierValue}"); Plugin.logger.LogDebug($"Scrap: {count} -> {count * Plugin.GetConfigDungeon.GetLootMultiplier}");
return count * PluginConfig.Instance.lootMultiplierValue; return count * Plugin.GetConfigDungeon.GetLootMultiplier;
} }
public static float ModifyMapCount(float count){ public static float ModifyMapCount(float count){
if (DunGenPatch.Patch.active == false) return count; if (DunGenPatch.Patch.active == false) return count;
Plugin.logger.LogDebug($"Map Hazards: {count} -> {count * PluginConfig.Instance.mapHazardsMultiplierValue}"); Plugin.logger.LogDebug($"Map Hazards: {count} -> {count * Plugin.GetConfigDungeon.GetMapHazardsMultiplier}");
return count * PluginConfig.Instance.mapHazardsMultiplierValue; return count * Plugin.GetConfigDungeon.GetMapHazardsMultiplier;
} }
} }

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using HarmonyLib; using HarmonyLib;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch { namespace ScarletMansion.GamePatch {
public class ShotgunItemPatch { public class ShotgunItemPatch {
@ -13,7 +14,7 @@ namespace ScarletMansion.GamePatch {
[HarmonyPostfix] [HarmonyPostfix]
public static void ShootGunPatch(ref ShotgunItem __instance, Vector3 shotgunPosition, Vector3 shotgunForward){ public static void ShootGunPatch(ref ShotgunItem __instance, Vector3 shotgunPosition, Vector3 shotgunForward){
if (Components.ScarletDoorLock.ScarletDoorRaycast(__instance, shotgunPosition, shotgunForward, 30f, out var door)){ if (Components.ScarletDoorLock.ScarletDoorRaycast(__instance, shotgunPosition, shotgunForward, 30f, out var door)){
var damage = PluginConfig.Instance.shotgunDamageValue; var damage = ConfigMain.Instance.shotgunDamageValue;
door.ApplyDamageServerRpc(shotgunForward, damage); door.ApplyDamageServerRpc(shotgunForward, damage);
} }
} }

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using HarmonyLib; using HarmonyLib;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch { namespace ScarletMansion.GamePatch {
public class ShovelPatch { public class ShovelPatch {
@ -19,7 +20,7 @@ namespace ScarletMansion.GamePatch {
var position = prevTransform.position + prevTransform.right * -0.35f; var position = prevTransform.position + prevTransform.right * -0.35f;
var forward = prevTransform.forward; var forward = prevTransform.forward;
if (Components.ScarletDoorLock.ScarletDoorRaycast(__instance, position, forward, 1.5f + 0.8f, out var door)){ if (Components.ScarletDoorLock.ScarletDoorRaycast(__instance, position, forward, 1.5f + 0.8f, out var door)){
var damage = PluginConfig.Instance.shovelDamageValue; var damage = ConfigMain.Instance.shovelDamageValue;
door.ApplyDamageServerRpc(forward, damage); door.ApplyDamageServerRpc(forward, damage);
} }
} }

View File

@ -1,4 +1,5 @@
using System; using ScarletMansion.Configs;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -17,7 +18,7 @@ namespace ScarletMansion.ModPatch {
} }
public static void Call(){ public static void Call(){
if (StartOfRound.Instance.IsHost && PluginConfig.Instance.facilityMeltdownActiveValue){ if (StartOfRound.Instance.IsHost && ConfigMain.Instance.facilityMeltdownActiveValue){
FacilityMeltdown.API.MeltdownAPI.StartMeltdown(Plugin.modGUID); FacilityMeltdown.API.MeltdownAPI.StartMeltdown(Plugin.modGUID);
GamePatch.Managers.AngerManager.Instance.TriggerAngerLightForever(); GamePatch.Managers.AngerManager.Instance.TriggerAngerLightForever();
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -9,18 +10,16 @@ using LethalConfig.ConfigItems;
using BepInEx.Configuration; using BepInEx.Configuration;
using System.Reflection; using System.Reflection;
using ChangeList = ScarletMansion.PresetConfig.ChangeList;
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 HarmonyLib;
using LethalConfig.MonoBehaviours.Components; using LethalConfig.MonoBehaviours.Components;
using UnityEngine.UI; using UnityEngine.UI;
using LethalConfig.MonoBehaviours; using LethalConfig.MonoBehaviours;
using ScarletMansion.Configs;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
namespace ScarletMansion.ModPatch { namespace ScarletMansion.ModPatch {
public class LethalConfigPatch : ModPatch { public class LethalConfigPatch<T> : ModPatch where T: ConfigDungeon<T> {
public const string section = "_Presets"; public const string section = "_Presets";
public const string descriptionPrefix = "These are a set of preset config values for your convience. Your config values will automatically update to these preset values every time SDM is loaded. These config values are a brighter shade of red. 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.";
@ -32,7 +31,8 @@ namespace ScarletMansion.ModPatch {
public static void ForceUIUpdate(){ public static void ForceUIUpdate(){
try { try {
var configMenuObject = GameObject.FindObjectOfType<ConfigMenu>(); // zaggy said it's better
var configMenuObject = GameObject.FindAnyObjectByType<ConfigMenu>();
configMenuObject.Close(false); configMenuObject.Close(false);
configMenuObject.Open(); configMenuObject.Open();
} catch (Exception e) { } catch (Exception e) {
@ -50,14 +50,14 @@ namespace ScarletMansion.ModPatch {
return LethalConfig.ConfigItems.Options.CanModifyResult.True(); return LethalConfig.ConfigItems.Options.CanModifyResult.True();
} }
public static void CreateConfig(ConfigEntryBase configEntry) { public static void CreateConfig(Assembly assembly, ConfigEntryBase configEntry) {
if (configEntry is ConfigEntry<int>) CreateIntConfig(configEntry as ConfigEntry<int>); if (configEntry is ConfigEntry<int>) CreateIntConfig(assembly, configEntry as ConfigEntry<int>);
else if (configEntry is ConfigEntry<float>) CreateFloatConfig(configEntry as ConfigEntry<float>); else if (configEntry is ConfigEntry<float>) CreateFloatConfig(assembly, configEntry as ConfigEntry<float>);
else if (configEntry is ConfigEntry<string>) CreateStringConfig(configEntry as ConfigEntry<string>); else if (configEntry is ConfigEntry<string>) CreateStringConfig(assembly, configEntry as ConfigEntry<string>);
else if (configEntry is ConfigEntry<bool>) CreateBoolConfig(configEntry as ConfigEntry<bool>); else if (configEntry is ConfigEntry<bool>) CreateBoolConfig(assembly, configEntry as ConfigEntry<bool>);
} }
public static void CreateIntConfig(ConfigEntry<int> configEntry){ public static void CreateIntConfig(Assembly assembly, ConfigEntry<int> configEntry){
var options = new LethalConfig.ConfigItems.Options.IntSliderOptions{ var options = new LethalConfig.ConfigItems.Options.IntSliderOptions{
Section = configEntry.Definition.Section, Section = configEntry.Definition.Section,
Name = configEntry.Definition.Key, Name = configEntry.Definition.Key,
@ -67,10 +67,10 @@ namespace ScarletMansion.ModPatch {
}; };
var entry = new IntSliderConfigItem(configEntry, options); var entry = new IntSliderConfigItem(configEntry, options);
LethalConfigManager.AddConfigItem(entry); LethalConfigManager.AddConfigItem(entry, assembly);
} }
public static void CreateFloatConfig(ConfigEntry<float> configEntry){ public static void CreateFloatConfig(Assembly assembly, ConfigEntry<float> configEntry){
var options = new LethalConfig.ConfigItems.Options.FloatSliderOptions{ var options = new LethalConfig.ConfigItems.Options.FloatSliderOptions{
Section = configEntry.Definition.Section, Section = configEntry.Definition.Section,
Name = configEntry.Definition.Key, Name = configEntry.Definition.Key,
@ -80,10 +80,10 @@ namespace ScarletMansion.ModPatch {
}; };
var entry = new FloatSliderConfigItem(configEntry, options); var entry = new FloatSliderConfigItem(configEntry, options);
LethalConfigManager.AddConfigItem(entry); LethalConfigManager.AddConfigItem(entry, assembly);
} }
public static void CreateStringConfig(ConfigEntry<string> configEntry){ public static void CreateStringConfig(Assembly assembly, ConfigEntry<string> configEntry){
var options = new LethalConfig.ConfigItems.Options.TextInputFieldOptions{ var options = new LethalConfig.ConfigItems.Options.TextInputFieldOptions{
Section = configEntry.Definition.Section, Section = configEntry.Definition.Section,
Name = configEntry.Definition.Key, Name = configEntry.Definition.Key,
@ -93,10 +93,10 @@ namespace ScarletMansion.ModPatch {
}; };
var entry = new TextInputFieldConfigItem(configEntry, options); var entry = new TextInputFieldConfigItem(configEntry, options);
LethalConfigManager.AddConfigItem(entry); LethalConfigManager.AddConfigItem(entry, assembly);
} }
public static void CreateBoolConfig(ConfigEntry<bool> configEntry){ public static void CreateBoolConfig(Assembly assembly, ConfigEntry<bool> configEntry){
var options = new LethalConfig.ConfigItems.Options.BoolCheckBoxOptions{ var options = new LethalConfig.ConfigItems.Options.BoolCheckBoxOptions{
Section = configEntry.Definition.Section, Section = configEntry.Definition.Section,
Name = configEntry.Definition.Key, Name = configEntry.Definition.Key,
@ -106,10 +106,10 @@ namespace ScarletMansion.ModPatch {
}; };
var entry = new BoolCheckBoxConfigItem(configEntry, options); var entry = new BoolCheckBoxConfigItem(configEntry, options);
LethalConfigManager.AddConfigItem(entry); LethalConfigManager.AddConfigItem(entry, assembly);
} }
public static void CreatePresetConfig<T>(ConfigEntry<T> configEntry, List<ChangeList> changeList) where T: Enum { public static void CreatePresetConfig<U>(Assembly assembly, ConfigEntry<U> configEntry, List<ConfigManager<T>.ChangeList> changeList) where U: Enum {
configEntry.SettingChanged += (obj, args) => ForceUIUpdate(); configEntry.SettingChanged += (obj, args) => ForceUIUpdate();
var description = $"{requiresNewLobby}\n\n{descriptionPrefix}\n\n"; var description = $"{requiresNewLobby}\n\n{descriptionPrefix}\n\n";
@ -125,20 +125,20 @@ namespace ScarletMansion.ModPatch {
CanModifyCallback = CanModifyCallback, CanModifyCallback = CanModifyCallback,
}; };
var entry = new EnumDropDownConfigItem<T>(configEntry, options); var entry = new EnumDropDownConfigItem<U>(configEntry, options);
LethalConfigManager.AddConfigItem(entry); LethalConfigManager.AddConfigItem(entry, assembly);
} }
public static void AutoGenerateConfigs(params object[] ignoreTargets){ public static void AutoGenerateConfigs<TTarget>(Assembly assembly, params object[] ignoreTargets) where TTarget: ConfigManager<TTarget> {
var fields = typeof(PluginConfig).GetFields(BindingFlags.Public | BindingFlags.Static); var fields = typeof(TTarget).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
foreach(var p in fields){ foreach(var p in fields){
var value = p.GetValue(null); var value = p.GetValue(null);
if (ignoreTargets.Contains(value)) continue; if (ignoreTargets.Contains(value)) continue;
var valueBundle = value as PluginConfig.ConfigEntryBundleBase; if (value is ConfigManager<TTarget>.ConfigEntryBundleBase){
if (valueBundle != null){ var valueBundle = (ConfigManager<TTarget>.ConfigEntryBundleBase)value;
foreach(var c in valueBundle.GetConfigs()){ foreach(var c in valueBundle.GetConfigs()){
CreateConfig(c); CreateConfig(assembly, c);
} }
} }
} }
@ -147,27 +147,34 @@ namespace ScarletMansion.ModPatch {
public override void AddPatch() { public override void AddPatch() {
LethalConfigManager.SkipAutoGen(); LethalConfigManager.SkipAutoGen();
CreatePresetConfig(PluginConfig.lcDungeonGenerationPreset.config, PresetConfig.dungeonGenerationChangeList); LethalConfigManager.SetModDescription("Adds the Scarlet Devil Mansion from Touhou as a possible dungeon to the snow maps.\n\nThis is the master config for the entire mod.");
CreatePresetConfig(PluginConfig.lcDungeonLightingPreset.config, PresetConfig.dungeonLightingChangeList);
var mainAssembly = typeof(Plugin).Assembly;
var dungeonAssembly = typeof(DungeonFoyer.Plugin).Assembly;
CreatePresetConfig(dungeonAssembly, ConfigDungeon<T>.dungeonGenerationPreset.config, ConfigDungeon<T>.dungeonGenerationChangeList);
CreatePresetConfig(dungeonAssembly, ConfigDungeon<T>.dungeonLightingPreset.config, ConfigDungeon<T>.dungeonLightingChangeList);
AutoGenerateConfigs(PluginConfig.lcDungeonGenerationPreset, PluginConfig.lcDungeonLightingPreset); AutoGenerateConfigs<ConfigMain>(mainAssembly);
AutoGenerateConfigs<T>(dungeonAssembly, ConfigDungeon<T>.dungeonGenerationPreset, ConfigDungeon<T>.dungeonLightingPreset);
try { try {
Plugin.Instance.harmony.PatchAll(typeof(LethalConfigPatch)); //Plugin.Instance.harmony.PatchAll(typeof(LethalConfigPatch));
} catch (Exception e) { } catch (Exception e) {
Plugin.logger.LogWarning("Tried to HarmonyPatch LethalConfig but failed. Nothing bad will happen but report this bug to dev."); Plugin.logger.LogWarning("Tried to HarmonyPatch LethalConfig but failed. Nothing bad will happen but report this bug to dev.");
Plugin.logger.LogError(e.ToString()); Plugin.logger.LogError(e.ToString());
} }
} }
/*
[HarmonyPatch(typeof(LethalConfig.MonoBehaviours.ConfigList), "LoadConfigsForMod")] [HarmonyPatch(typeof(LethalConfig.MonoBehaviours.ConfigList), "LoadConfigsForMod")]
[HarmonyPostfix] [HarmonyPostfix]
public static void LoadConfigsForModPatch(ref LethalConfig.MonoBehaviours.ConfigList __instance, ref LethalConfig.Mods.Mod mod) { public static void LoadConfigsForModPatch(ref LethalConfig.MonoBehaviours.ConfigList __instance, ref LethalConfig.Mods.Mod mod) {
try { try {
if (mod.ModInfo.Guid == Plugin.modGUID) { if (mod.ModInfo.Guid == Plugin.modGUID) {
var genChanges = PresetConfig.defaultGeneration.GetAllConfigEntries(); var genChanges = Presets.defaultGeneration.GetAllConfigEntries();
var lightChanges = PresetConfig.defaultLighting.GetAllConfigEntries(); var lightChanges = Presets.defaultLighting.GetAllConfigEntries();
foreach(Transform child in __instance.listContainerObject.transform){ foreach(Transform child in __instance.listContainerObject.transform){
var controller = child.GetComponentInChildren<ModConfigController>(); var controller = child.GetComponentInChildren<ModConfigController>();
@ -187,6 +194,7 @@ namespace ScarletMansion.ModPatch {
} }
} }
*/
} }
} }

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BepInEx; using BepInEx;
using BepInEx.Bootstrap; using BepInEx.Bootstrap;
using ScarletMansion.Configs;
namespace ScarletMansion.ModPatch { namespace ScarletMansion.ModPatch {
public class ModCompability { public class ModCompability {
@ -17,7 +18,7 @@ namespace ScarletMansion.ModPatch {
public static readonly ModPatch[] modPatches = new ModPatch[] { public static readonly ModPatch[] modPatches = new ModPatch[] {
//new AdvancedCompanyPatch(advancedCompanyGuid), //new AdvancedCompanyPatch(advancedCompanyGuid),
new LethalConfigPatch(lethalConfigGuid), new LethalConfigPatch<ConfigDungeonFoyer>(lethalConfigGuid),
new FacilityMeltdownPatch(facilityMeldownGuid), new FacilityMeltdownPatch(facilityMeldownGuid),
new ReservedFlashlightPatch(reserveFlashlightGuid), new ReservedFlashlightPatch(reserveFlashlightGuid),
new ReservedKeyPatch(reserveKeyGuid) new ReservedKeyPatch(reserveKeyGuid)

View File

@ -16,14 +16,16 @@ using ScarletMansion.GamePatch;
using ScarletMansion.ModPatch; using ScarletMansion.ModPatch;
using ScarletMansion.DunGenPatch; using ScarletMansion.DunGenPatch;
using ScarletMansion.GamePatch.Managers; using ScarletMansion.GamePatch.Managers;
using ScarletMansion.Configs;
namespace ScarletMansion { namespace ScarletMansion {
[BepInPlugin(modGUID, modName, modVersion)] [BepInPlugin(modGUID, modName, modVersion)]
[BepInDependency("imabatby.lethallevelloader", "1.2.0.3")] [BepInDependency("imabatby.lethallevelloader", "1.3.13")]
[BepInDependency("evaisa.lethallib", "0.13.2")] [BepInDependency("evaisa.lethallib", "0.13.2")]
[BepInDependency("dev.ladyalice.dungenplus", "1.3.0")] [BepInDependency("dev.ladyalice.dungenplus", "1.3.3")]
[BepInDependency("dev.ladyalice.scarletmansion.foyer")]
//[BepInDependency(ModCompability.advancedCompanyGuid, BepInDependency.DependencyFlags.SoftDependency)] //[BepInDependency(ModCompability.advancedCompanyGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.lethalConfigGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.lethalConfigGuid, BepInDependency.DependencyFlags.SoftDependency)]
@ -33,13 +35,21 @@ namespace ScarletMansion {
[BepInProcess("Lethal Company.exe")] [BepInProcess("Lethal Company.exe")]
public class Plugin : BaseUnityPlugin { public class Plugin : BaseUnityPlugin {
public const string modGUID = "dev.ladyalice.scarletmansion"; public const string modGUID = "dev.ladyalice.scarletmansion";
private const string modName = "Scarlet Mansion"; private const string modName = "Scarlet Devil Mansion";
private const string modVersion = "2.1.0"; private const string modVersion = "2.1.1";
public readonly Harmony harmony = new Harmony(modGUID); public readonly Harmony harmony = new Harmony(modGUID);
public static Plugin Instance {get; private set;} public static Plugin Instance {get; private set;}
public static PluginConfig MyConfig { get; internal set; }
public static ConfigMain ConfigMain { get; internal set; }
public static ConfigDungeonFoyer ConfigFoyer { get; internal set; }
public static IConfigDungeon GetConfigDungeon {
get {
return ConfigFoyer;
}
}
public static ManualLogSource logger { get; internal set; } public static ManualLogSource logger { get; internal set; }
@ -47,9 +57,12 @@ namespace ScarletMansion {
if (Instance == null) Instance = this; if (Instance == null) Instance = this;
logger = BepInEx.Logging.Logger.CreateLogSource(modGUID); logger = BepInEx.Logging.Logger.CreateLogSource(modGUID);
logger.LogInfo($"Plugin {modName} has been added! Death to Bepinex!"); logger.LogInfo($"Plugin {modName} has been added!");
MyConfig = new PluginConfig(Config); ConfigMain = new ConfigMain(Config);
var foyerFile = new ConfigFile(Path.Combine(Paths.ConfigPath, $"{modGUID}.foyer.cfg"), true);
ConfigFoyer = new ConfigDungeonFoyer(foyerFile);
//MyConfig.SerializerTest(); //MyConfig.SerializerTest();
@ -70,7 +83,8 @@ namespace ScarletMansion {
harmony.PatchAll(typeof(GeneratePathPatch)); harmony.PatchAll(typeof(GeneratePathPatch));
harmony.PatchAll(typeof(PluginConfig)); harmony.PatchAll(typeof(ConfigMain));
harmony.PatchAll(typeof(ConfigDungeonFoyer));
SetupForNetcodePatcher(); SetupForNetcodePatcher();
@ -99,7 +113,7 @@ namespace ScarletMansion {
var extendedDungeon = ScriptableObject.CreateInstance<ExtendedDungeonFlow>(); var extendedDungeon = ScriptableObject.CreateInstance<ExtendedDungeonFlow>();
extendedDungeon.name = "Scarlet Devil Mansion"; extendedDungeon.name = "Scarlet Devil Mansion";
extendedDungeon.DungeonName = "Scarlet Devil Mansion"; extendedDungeon.DungeonName = "Scarlet Devil Mansion";
extendedDungeon.DungeonFlow = Assets.dungeon; extendedDungeon.DungeonFlow = ConfigFoyer.dungeon;
extendedDungeon.FirstTimeDungeonAudio = Assets.entranceAudioClip; extendedDungeon.FirstTimeDungeonAudio = Assets.entranceAudioClip;
extendedDungeon.LevelMatchingProperties = sdmLevelMatchProperties; extendedDungeon.LevelMatchingProperties = sdmLevelMatchProperties;
@ -125,9 +139,9 @@ namespace ScarletMansion {
var extendedMod = ExtendedMod.Create("Scarlet Devil Mansion", "Alice", extendedContent.ToArray()); var extendedMod = ExtendedMod.Create("Scarlet Devil Mansion", "Alice", extendedContent.ToArray());
PatchedContent.RegisterExtendedMod(extendedMod); PatchedContent.RegisterExtendedMod(extendedMod);
Assets.extendedMod = extendedMod; Assets.extendedMod = extendedMod;
Assets.dungeonExtended = extendedDungeon; ConfigFoyer.dungeonExtended = extendedDungeon;
extendedDungeon.DungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch); extendedDungeon.DungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch);
@ -138,8 +152,8 @@ namespace ScarletMansion {
//DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction); //DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction);
DunGenPlus.API.AddDunGenExtender(Assets.dungeon, Assets.dunGenExtender); DunGenPlus.API.AddDunGenExtender(ConfigFoyer.dungeon, ConfigFoyer.dunGenExtender);
Assets.dunGenExtender.Events.OnModifyDunGenExtenderProperties.AddListener(DunGenPatch.Patch.UpdateDunGenExtenderProperties); ConfigFoyer.dunGenExtender.Events.OnModifyDunGenExtenderProperties.AddListener((props, callback) => DunGenPatch.Patch.UpdateDunGenExtenderProperties(ConfigFoyer, props, callback));
} }
void SetupForNetcodePatcher(){ void SetupForNetcodePatcher(){

View File

@ -1,557 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using HarmonyLib;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using System.Reflection;
using ScarletMansion.GamePatch.Components;
namespace ScarletMansion {
[Serializable]
public partial class PluginConfig : SyncedInstance<PluginConfig>{
public const string modPrefix = "SDMansion";
public static readonly string requestMessage = $"{modPrefix}_OnRequestConfigSync";
public static readonly string receiveMessage = $"{modPrefix}_OnReceieveConfigSync";
public const string dungeonWeightPrefix = "Dungeon Weight";
public const string dungeonGenerationPrefix = "Dungeon Generation";
public const string dungeonGenerationBoundingBoxPrefix = "DunGen Bounding Box";
public const string dungeonGenerationMPathsPrefix = "DunGen Main Path";
public const string dungeonGenerationBPathPrefix = "DunGen Branching Path";
public const string dungeonGenerationBPathOnePrefix = "DunGen Branching Path 1";
public const string dungeonGenerationBPathTwoPrefix = "DunGen Branching Path 2";
public const string dungeonGenerationBPathThreePrefix = "DunGen Branching Path 3";
public const string dungeonItemPrefix = "Dungeon Items";
public const string dungeonEnemiesPrefix = "Dungeon Enemies";
public const string dungeonFeaturesPrefix = "Dungeon Features";
public const string dungeonPaintingEventPrefix = "Dungeon Painting Event";
public const string dungeonTreasureRoomPrefix = "Dungeon Treasure Rooms";
public const string dungeonLightingPrefix = "Lighting";
public const string presetsPrefix = "_Presets";
/*
// basic moons
public static ConfigEntryBundle<int> dungeonSnowWeight = new ConfigEntryBundle<int>(
dungeonWeightPrefix,
"SDM Rend/Dine Weight",
300,
"The weight of the dungeon appearing for dine/rend. A higher weight means a higher chance.\n100 = 25% chance, 300 = 50% chance, 900 = 75%, 99999 = 99.99% chance",
null,
new AcceptableValueRange<int>(0, 99999)
//new ConfigDefinition("Network General", "SDM Rend/Dine Weight")
);
public static ConfigEntryBundle<int> dungeonTitanWeight = new ConfigEntryBundle<int>(
dungeonWeightPrefix,
"SDM Titan Weight",
69,
"The weight of the dungeon appearing for titan. A higher weight means a higher chance.\n69 = 16% chance, 150 = 29%, 400 = 52% chance, 99999 = 99.99% chance",
null,
new AcceptableValueRange<int>(0, 99999)
//new ConfigDefinition("Network General", "SDM Titan Weight")
);
public int dungeonSnowWeightValue;
public int dungeonTitanWeightValue;
// custom moons
public static ConfigEntryBundle<string> customMaps = new ConfigEntryBundle<string>(
dungeonWeightPrefix,
"Custom Moons",
string.Empty,
"The moon(s) that SDM can spawn on, in the form of a comma separated list of selectable level names and optionally a weight value by using an '@' and weight value after it (e.g. \"Titan@300,Dine,Rend@10,CUSTOM_MOON_NAME@9999\")\nUsing this config WILL override the default weights used above.\nThe name matching is lenient and should pick it up if you use the terminal name or internal mod name. If no rarity is specified, the default weight below is used.\nThe following strings: \"all\", \"vanilla\", \"modded\", \"paid\", \"free\" are dynamic presets which add the dungeon to that specified group (string must only contain one of these, or a manual moon name list).\n",
null
//new ConfigDefinition("Network Customizability", "Custom Moons")
);
public static ConfigEntryBundle<int> customMapDefaultWeight = new ConfigEntryBundle<int>(
dungeonWeightPrefix,
"Custom Moons Weight",
300,
"The default weight of the dungeon appearing for custom moons. A higher weight means a higher chance.\n300 = 50% chance, 99999 = 99.99% chance",
null,
new AcceptableValueRange<int>(0, 99999)
//new ConfigDefinition("Network Customizability", "Custom Moons Weight")
);
public string customMapsValue;
public int customMapDefaultWeightValue;
*/
/*
// dungeon generation
public static ConfigEntryBundleMinMax<float> dunGenMultiplier = new ConfigEntryBundleMinMax<float>(
dungeonGenerationPrefix,
"Size Multiplier Min",
"Size Multiplier Max",
1f,
2f,
"The minimum allowed size of the dungeon.\nEach moon modifies the size of their dungeon starting from 1 (Experimentation) to 2.35 (Titan).\nPlease check the wiki for the list of map size multipliers for every moon.",
"The maximum allowed size of the dungeon.\nEach moon modifies the size of their dungeon starting from 1 (Experimentation) to 2.35 (Titan).\nPlease check the wiki for the list of map size multipliers for every moon.",
null,
new AcceptableValueRange<float>(1f, 10f)
);
public FloatRange dunGenMultiplierValue = new FloatRange("size multiplier");
*/
// bounding box
public static ConfigEntryBundle<int> dunGenWidthBase = new ConfigEntryBundle<int>(
dungeonGenerationBoundingBoxPrefix,
"Width Base",
120,
"The width (left-to-right) of the dungeon's bounding box.\nThe dungeon is generated within this bounding box and cannot generate tiles outside of it.\nIncreasing this value will make the dungeon less compact but less likely to fail generation and vice versa.\nDecreasing this value too much can make it impossible for the dungeon to generate.",
null,
new AcceptableValueRange<int>(40, 200)
);
public static ConfigEntryBundle<int> dunGenLengthBase = new ConfigEntryBundle<int>(
dungeonGenerationBoundingBoxPrefix,
"Length Base",
80,
"The length (forward-to-back) of the dungeon's bounding box.\nThe dungeon is generated within this bounding box and cannot generate tiles outside of it.\nIncreasing this value will make the dungeon less compact but less likely to fail generation and vice versa.\nDecreasing this value too much can make it impossible for the dungeon to generate.",
null,
new AcceptableValueRange<int>(40, 200)
);
public static ConfigEntryBundle<float> dunGenWidthMultiFactor = new ConfigEntryBundle<float>(
dungeonGenerationBoundingBoxPrefix,
"Width Multiplier Factor",
0.5f,
"The width (left-to-right) of the dungeon's bounding box increases based on the dungeon's size. That additional width is multiplied by this value.\nThe exact formula is width = base + (base * (size - 1) * factor).\nIncreasing this value will make the dungeon less compact but less likely to fail generation on bigger moons.\nSetting this value to 0 means that the base width will be a constant value no matter how small or big the moon may be.",
null,
new AcceptableValueRange<float>(0f, 4f)
);
public static ConfigEntryBundle<float> dunGenLengthMultiFactor = new ConfigEntryBundle<float>(
dungeonGenerationBoundingBoxPrefix,
"Length Multiplier Factor",
0.3333333f,
"The length (forward-to-back) of the dungeon's bounding box increases based on the dungeon's size. That additional length is multiplied by this value.\nThe exact formula is length = base + (base * (size - 1) * factor).\nIncreasing this value will make the dungeon less compact but less likely to fail generation on bigger moons.\nSetting this value to 0 means that the base length will be a constant value no matter how small or big the moon may be.",
null,
new AcceptableValueRange<float>(0f, 4f)
);
public int dunGenWidthBaseValue;
public int dunGenLengthBaseValue;
public float dunGenWidthMultiFactorValue;
public float dunGenLengthMultiFactorValue;
// main paths
public static ConfigEntryBundle<int> mainPathCount = new ConfigEntryBundle<int>(
dungeonGenerationMPathsPrefix,
"Main Path Count",
3,
"The amount of main paths that the modified dungeon generation code must generate.\nDecreasing this value will make it much less likely to fail generation but will lower the length consistency of branching paths.\nSetting the value to 1 will revert back to the vanilla dungeon generation.",
null,
new AcceptableValueRange<int>(1, 3)
);
private static readonly string _mainPathLengthPostMessage = "Decreasing this value will make the dungeon less likely to fail generation but will lower the length consistency of branching paths and vice versa.\nIncreasing this value too much can make it impossible for the dungeon to generate.";
public static ConfigEntryBundleMinMax<int> mainPathLength = new ConfigEntryBundleMinMax<int>(
dungeonGenerationMPathsPrefix,
"Main Path Length Min",
"Main Path Length Max",
3,
4,
$"The minimum allowed length of the main path. This value is multiplied by the dungeon's size.\n\n{_mainPathLengthPostMessage}",
$"The maximum allowed length of the main path. This value is multiplied by the dungeon's size.\n\n{_mainPathLengthPostMessage}",
null,
new AcceptableValueRange<int>(3, 20)
);
public int mainPathCountValue;
public IntRange mainPathLengthValue = new IntRange("main path length");
// branching path
public static ConfigEntryBundle<bool> branchMultiSim = new ConfigEntryBundle<bool>(
dungeonGenerationBPathPrefix,
"Use Branch Path Multi Sim",
true,
"If enabled, the dungeon generation will prioritize branch paths that connect to already generated tiles.\nThis increases the chance of circular/looping paths. Slows dungeon generation times a bit at the end.\n\nOnly works when MainPathCount is more than 1.",
null
);
public static ConfigEntryBundle<int> branchSimCount = new ConfigEntryBundle<int>(
dungeonGenerationBPathPrefix,
"Branch Path Simlation Count",
5,
"The maximum number of valid branch paths that the dungeon generation will simulate before choosing the best path.\nIncreasing this value gives the dungeon generation a higher chance of finding a good path but impacts dungeon generation times and vice versa.",
null,
new AcceptableValueRange<int>(1, 10)
);
public bool branchMultiSimValue;
public int branchSimCountValue;
// branching path mini sections
private static string _branchPathBranchCountMessage(int section, string minmax, string middleSection) => $"The {minmax} amount of branching paths in Section {section} of the dungeon generation process.\n{middleSection}\n\n{_branchPathPostCountMessage}";
private static string _branchPathBranchDepthMessage(int section, string minmax, string middleSection) => $"The {minmax} length of the branching paths in Section {section} of the dungeon generation process.\n{middleSection}\n\n{_branchPathPostDepthMessage}";
private static readonly string _branchPathSectionOneMessage = "Section 1 consists of the first 30% tiles of the main path (including the mayor entrance tile).";
private static readonly string _branchPathSectionTwoMessage = "Section 2 consists of the middle tiles (30% - 70%) of the main path.";
private static readonly string _branchPathSectionThreeMessage = "Section 3 consists of the last 30% tiles of the main path.";
private static readonly string _branchPathPostGenericMessage = "This is not an enforced number and the dungeon generation will proceed if it can't fit any tiles.";
private static readonly string _branchPathPostCountMessage = $"Each tile on the main path will try to generate a number of branching paths equal to this number. {_branchPathPostGenericMessage}";
private static readonly string _branchPathPostDepthMessage = $"Each branching path will try to generate a number of connecting tiles equal to this number. {_branchPathPostGenericMessage}";
public static ConfigEntryBundleBranchingPath branchPathSectionOne = new ConfigEntryBundleBranchingPath(
dungeonGenerationBPathOnePrefix, 1, _branchPathSectionOneMessage, 4, 6, 1, 2
);
public static ConfigEntryBundleBranchingPath branchPathSectionTwo = new ConfigEntryBundleBranchingPath(
dungeonGenerationBPathTwoPrefix, 2, _branchPathSectionTwoMessage, 2, 3, 1, 2
);
public static ConfigEntryBundleBranchingPath branchPathSectionThree = new ConfigEntryBundleBranchingPath(
dungeonGenerationBPathThreePrefix, 3, _branchPathSectionThreeMessage, 1, 2, 0, 1
);
public BranchingPathRange branchPathSectionOneValue = new BranchingPathRange("section one");
public BranchingPathRange branchPathSectionTwoValue = new BranchingPathRange("section two");
public BranchingPathRange branchPathSectionThreeValue = new BranchingPathRange("section three");
// lighting
public static ConfigEntryBundle<int> hallwayLightsWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"Hallway Lights Weight",
150,
"The weight for a hallway wall lamp to appear on its respective walls. With the default weight of 150 against the weight of the empty wall of 50, there is a 75% chance that a wall lamp will spawn. Setting the weight to 100 will give you a 67% chance, 450=90% chance, and so on.",
null,
new AcceptableValueRange<int>(0, 999)
);
public static ConfigEntryBundle<int> ceilingLightsWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"Chandelier Lights Weight",
150,
"The weight for a chandelier to appear on its respective ceilings. With the default weight of 150 against the weight of the empty ceiling of 50, there is a 75% chance that a chandelier will spawn. Setting the weight to 100 will give you a 67% chance, 450=90% chance, and so on.",
null,
new AcceptableValueRange<int>(0, 999)
);
public static ConfigEntryBundle<int> lightsSpawnZeroWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"0 Lights Weight",
1,
"The weight that none of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [1, 7, 2], there is a 10% chance that even in a room filled with lamps, none of them will emit light.",
new ConfigEntryBundleExtraParameters(VerifyLightingValues, false),
new AcceptableValueRange<int>(0, 99)
);
public static ConfigEntryBundle<int> lightsSpawnOneWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"1 Light Weight",
7,
"The weight that only 1 of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [1, 7, 2], there is a 70% chance that even in a room filled with lamps, only one of them will emit light.",
new ConfigEntryBundleExtraParameters(VerifyLightingValues, false),
new AcceptableValueRange<int>(0, 99)
);
public static ConfigEntryBundle<int> lightsSpawnTwoWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"2 Lights Weight",
2,
"The weight that only 2 of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [1, 7, 2], there is a 20% chance that even in a room filled with lamps, only two of them will emit light.",
new ConfigEntryBundleExtraParameters(VerifyLightingValues, true),
new AcceptableValueRange<int>(0, 99)
);
/*
public static ConfigEntryBundle<int> lightsSpawnThreeWeight = new ConfigEntryBundle<int>(
dungeonLightingPrefix,
"3 Lights Weight",
1,
"The weight that only 3 of the spawned light sources (desk lamps, wall lamps, chandeliers) in a given tile will emit light. With the default weight spread of [2, 3, 4, 1], there is a 10% chance that even in a room filled with lamps, only two of them will emit light.",
new AcceptableValueRange<int>(0, 99)
);
*/
public int hallwayLightsWeightValue;
public int ceilingLightsWeightValue;
public int lightsSpawnZeroWeightValue;
public int lightsSpawnOneWeightValue;
public int lightsSpawnTwoWeightValue;
//public int lightsSpawnThreeWeightValue;
// loot
public static ConfigEntryBundle<float> lootMultiplier = new ConfigEntryBundle<float>(
dungeonItemPrefix,
"Loot Multiplier",
1.4f,
"Multiplies the total amount of loot for the dungeon.",
null,
new AcceptableValueRange<float>(0.25f, 4f)
);
public static ConfigEntryBundleItem decoFlashlight = new ConfigEntryBundleItem(dungeonItemPrefix, "Deco Pro Flashlight ", "Deco Pro Flashlight", 0, 0);
public static ConfigEntryBundleItem decoProFlashlight = new ConfigEntryBundleItem(dungeonItemPrefix, "Deco Flashlight ", "Deco Flashlight", 0, 0);
public static ConfigEntryBundleItem scarletKey = new ConfigEntryBundleItem(dungeonItemPrefix, "Scarlet Key ", "Scarlet Key", 0, 0);
public float lootMultiplierValue;
public Item decoFlashlightValue = new Item("Deco Flashlight");
public Item decoProFlashlightValue = new Item("Deco Pro Flashlight");
public Item scarletKeyValue = new Item("Scarlet Key");
public static ConfigEntryBundleScrapItem crystal = new ConfigEntryBundleScrapItem("Dungeon Item Deco Crystal", "Decorative Crystal", 110, 150, 50);
public static ConfigEntryBundleScrapItem crystalBroken = new ConfigEntryBundleScrapItem("Dungeon Item Shattered Deco Crystal", "Shattered Decorative Crystal", 60, 80, 5);
public static ConfigEntryBundleScrapItem snowGlobe = new ConfigEntryBundleScrapItem("Dungeon Item Snow Globe", "Snow Globe", 150, 320, 40);
public ScrapItem crystalValue = new ScrapItem("Crystal");
public ScrapItem crystalBrokenValue = new ScrapItem("Shattered Crystal");
public ScrapItem snowGlobeValue = new ScrapItem("Snow Globe");
// enemies
public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>(
dungeonEnemiesPrefix,
"Map Hazards Multiplier",
1.6f,
"Multiplies the total amount of map hazards (landmines, turrets) for the dungeon.",
null,
new AcceptableValueRange<float>(0.25f, 4f)
);
public static ConfigEntryBundle<int> minIndoorEnemySpawnCount = new ConfigEntryBundle<int>(
dungeonEnemiesPrefix,
"Minimum Indoor Enemy Spawn Count",
1,
"Increases the minimum amount of indoor enemies that spawn with each spawn wave. For reference, Eclipse is +3.",
null,
new AcceptableValueRange<int>(0, 3)
);
public float mapHazardsMultiplierValue;
public int minIndoorEnemySpawnCountValue;
public static ConfigEntryBundleSpawnableEnemy knightEnemy = new ConfigEntryBundleSpawnableEnemy("Dungeon Enemy Knight", "Knight", "Coil-head", 0, 13f);
public static ConfigEntryBundleSpawnableEnemy maidEnemy = new ConfigEntryBundleSpawnableEnemy("Dungeon Enemy Maid", "Maid", "Butler", 6, 5.5f);
public static ConfigEntryBundleEnemy revEnemy = new ConfigEntryBundleEnemy("Dungeon Enemy Revenant", "Revenant", 0, 14.5f);
public SpawnableEnemy knightEnemyValue = new SpawnableEnemy();
public SpawnableEnemy maidEnemyValue = new SpawnableEnemy();
public Enemy revEnemyValue = new Enemy();
// features
public static ConfigEntryBundle<int> shovelDamage = new ConfigEntryBundle<int>(
dungeonFeaturesPrefix,
"Door Shovel Damage",
45,
"The amount of damage done by swinging your shovel at a SDM door. Door health equals to 100.",
null,
new AcceptableValueRange<int>(0, 100)
);
public static ConfigEntryBundle<int> shotgunDamage = new ConfigEntryBundle<int>(
dungeonFeaturesPrefix,
"Door Shotgun Damage",
100,
"The amount of damage done by shotgun blasting at a SDM door. Door health equals to 100.",
null,
new AcceptableValueRange<int>(0, 100)
);
public static ConfigEntryBundle<float> lockedDoorEnemyDamageMultiplier = new ConfigEntryBundle<float>(
dungeonFeaturesPrefix,
"Locked Door Enemy Damage Multiplier",
0.5f,
"The damage multiplier applied to an enemy's attacks against a locked SDM door.\nSetting the value to 0 means that enemies can't break through a locked SDM door like in vanilla.",
null,
new AcceptableValueRange<float>(0f, 1f)
);
public static ConfigEntryBundle<bool> useSDMFireExits = new ConfigEntryBundle<bool>(
dungeonFeaturesPrefix,
"Use SDM Fire Exits",
true,
"If enabled, the dungeon will spawn its custom fire exits. If disabled, the dungeon will instead spawn the vanilla fire exits."
);
public int shovelDamageValue;
public int shotgunDamageValue;
public float lockedDoorEnemyDamageMultiplierValue;
public bool useSDMFireExitsValue;
// painting
public static ConfigEntryBundle<int> paintingCount = new ConfigEntryBundle<int>(
dungeonPaintingEventPrefix,
"Painting Count",
2,
"The maximum amount of demonic paintings that spawn in the dungeon.",
null,
new AcceptableValueRange<int>(0, 10)
);
public static ConfigEntryBundle<int> paintingValue = new ConfigEntryBundle<int>(
dungeonPaintingEventPrefix,
"Painting Value",
100,
"The scrap value of demonic paintings.",
null,
new AcceptableValueRange<int>(0, 400)
);
public static ConfigEntryBundleMinMax<int> paintingExtraLoot = new ConfigEntryBundleMinMax<int>(
dungeonPaintingEventPrefix,
"Painting Extra Loot Min",
"Painting Extra Loot Max",
0,
2,
"The minimum allowed amount of extra loot that spawns after grabbing the demonic painting.",
"The maximum allowed amount of extra loot that spawns after grabbing the demonic painting.",
null,
new AcceptableValueRange<int>(0, 3)
);
public static ConfigEntryBundle<bool> paintingSpawnEnemy = new ConfigEntryBundle<bool>(
dungeonPaintingEventPrefix,
"Spawn Enemy",
true,
"If enabled, an enemy will spawn when the bedroom's painting event ends."
);
public static ConfigEntryBundle<string> paintingEnemyList = new ConfigEntryBundle<string>(
dungeonPaintingEventPrefix,
"Enemy List",
"default",
$"The enemies that can spawn when the bedroom's painting event ends, in the form of a comma separated list of enemy names along with optional parameters separated or '@'.It can accept either the enemy's internal or display name.\n\nThe available parameters are:\nSpawn Logic: 's#' where '#' is either '0' for normal spawn logic or '1' for special spawn logic. Only the knight and jester have special spawn logic. When their special spawn logic is enabled, the knight will spawn properly in the bedroom and the jester will spawn already cranking.\n\nThe following string \"default\" is a preset that uses the following string \"{ScarletBedroom.ENEMY_SPAWN_LIST_DEFAULT}\"."
);
public static ConfigEntryBundle<bool> paintingEnemyEvilSkin = new ConfigEntryBundle<bool>(
dungeonPaintingEventPrefix,
"Invader Apperance",
true,
"Enemies spawned from the bedroom's painting event has a pitch black and red appearance. Only valid for the default enemy list."
);
public static ConfigEntryBundle<bool> facilityMeltdownActive = new ConfigEntryBundle<bool>(
dungeonPaintingEventPrefix,
"Activate Facility Meltdown",
false,
"If enabled, facility meltdown will activate when the bedroom's painting event ends. Requires FacilityMeltdown v2.4.7 or higher."
);
public int paintingCountValue;
public int paintingValueValue;
public IntRange paintingExtraLootValue = new IntRange("painting extra loot");
public bool paintingSpawnEnemyValue;
public string paintingEnemyListValue;
public bool paintingEnemyEvilSkinValue;
public bool facilityMeltdownActiveValue;
// treasure room
public static ConfigEntryBundle<int> treasureRoomCount = new ConfigEntryBundle<int>(
dungeonTreasureRoomPrefix,
"Treasure Room Count",
2,
"The maximum amount of treasure rooms that spawn in the dungeon.",
null,
new AcceptableValueRange<int>(0, 10)
);
public static ConfigEntryBundleMinMax<int> treasureRoomLoot = new ConfigEntryBundleMinMax<int>(
dungeonTreasureRoomPrefix,
"Treasure Room Loot Min",
"Treasure Room Loot Max",
2,
3,
"The minimum allowed amount of loot that spawns in the treasure room.",
"The maximum allowed amount of loot that spawns in the treasure room.",
null,
new AcceptableValueRange<int>(0, 6)
);
public int treasureRoomCountValue;
public IntRange treasureRoomLootValue = new IntRange("treasure room loot");
public static string GetEnumNames<T>() where T: Enum {
var str = string.Empty;
var enums = Enum.GetNames(typeof(T)).Select(e => $"\"{e}\"");
return string.Join(", ", enums);
}
public static ConfigEntryBundle<PresetConfig.DungeonGeneration> lcDungeonGenerationPreset = new ConfigEntryBundle<PresetConfig.DungeonGeneration>(
presetsPrefix,
"Preset Dungeon Generation",
PresetConfig.DungeonGeneration.Default,
$"Automatically updates config values based on this preset string. If you want to disable this, change this value to \"Custom\".\nPossible values are ({GetEnumNames<PresetConfig.DungeonGeneration>()}).\nPlease use the mod LethalConfig for more information on these presets.",
new ConfigEntryBundleExtraParameters((c) => PresetConfig.UpdateFromPresetValue((int)lcDungeonGenerationPreset.config.Value, PresetConfig.dungeonGenerationChangeList), true)
);
public static ConfigEntryBundle<PresetConfig.DungeonLighting> lcDungeonLightingPreset = new ConfigEntryBundle<PresetConfig.DungeonLighting>(
presetsPrefix,
"Preset Dungeon Lighting",
PresetConfig.DungeonLighting.Default,
$"Automatically updates config values based on this preset string. If you want to disable this, change this value to \"Custom\".\nPossible values are ({GetEnumNames<PresetConfig.DungeonLighting>()}).\nPlease use the mod LethalConfig for more information on these presets.",
new ConfigEntryBundleExtraParameters((c) => PresetConfig.UpdateFromPresetValue((int)lcDungeonLightingPreset.config.Value, PresetConfig.dungeonLightingChangeList), true)
);
public void AutoGenerateOnChangeConfig(ConfigFile cfg, params object[] ignoreTargets){
var fields = typeof(PluginConfig).GetFields(BindingFlags.Public | BindingFlags.Static);
foreach(var p in fields){
var value = p.GetValue(this);
if (ignoreTargets.Contains(value)) continue;
var valueBundle = value as ConfigEntryBundleBase;
if (valueBundle != null){
var targetFieldName = $"{p.Name}Value";
var targetField = typeof(PluginConfig).GetField(targetFieldName, BindingFlags.Public | BindingFlags.Instance);
if (targetField == null) {
Plugin.logger.LogError($"Could not find field {targetFieldName}");
continue;
}
valueBundle.Bind(cfg, this, new ConfigEntryBundleBase.FieldPropertyInfo(targetField), this);
}
}
}
public PluginConfig(ConfigFile cfg){
InitInstance(this);
AutoGenerateOnChangeConfig(cfg, lcDungeonGenerationPreset, lcDungeonLightingPreset);
lcDungeonGenerationPreset.Bind(cfg, this);
lcDungeonLightingPreset.Bind(cfg, this);
}
public static void VerifyLightingValues(PluginConfig instance){
Plugin.logger.LogDebug("Verifying lightsSpawn 0/1/2 values");
if (instance.lightsSpawnZeroWeightValue == 0f && instance.lightsSpawnOneWeightValue == 0f && instance.lightsSpawnTwoWeightValue == 0f){
instance.lightsSpawnZeroWeightValue = 1;
}
}
public override string ToString() {
var variables = typeof(PluginConfig).GetFields(BindingFlags.Public | BindingFlags.Instance);
return string.Join(", ", variables.Select(v => $"[{v.Name}]{v.GetValue(this)}"));
}
public void SerializerTest(){
var bytes = SerializeToBytes(Instance);
var copy = DeserializeFromBytes(bytes);
Plugin.logger.LogDebug($"Inst: {Instance.ToString()}");
Plugin.logger.LogDebug($"Copy: {copy.ToString()}");
}
}
}

View File

@ -1,455 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using HarmonyLib;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using System.Reflection;
using static ScarletMansion.PluginConfig;
namespace ScarletMansion {
public partial class PluginConfig : SyncedInstance<PluginConfig> {
public abstract class NumberRange<T> {
private string name;
[SerializeField] protected T _min;
[SerializeField] protected T _max;
public T min {
get {
return _min;
} set {
_min = value;
Verify();
}
}
public T max {
get {
return _max;
} set {
_max = value;
Verify();
}
}
public NumberRange(string name){
this.name = name;
_min = default;
_max = default;
}
public virtual void Verify(){
Plugin.logger.LogDebug($"Verifying min/max for {name}");
}
public override string ToString() {
return $"({min}, {max})";
}
}
[System.Serializable]
public class IntRange : NumberRange<int> {
public IntRange(string name) : base(name) { }
public override void Verify() {
base.Verify();
_max = Mathf.Max(_min, _max);
}
public DunGen.IntRange GetDungenIntRange(){
return new DunGen.IntRange(min, max);
}
}
[System.Serializable]
public class FloatRange : NumberRange<float> {
public FloatRange(string name) : base(name) { }
public override void Verify() {
base.Verify();
_max = Mathf.Max(_min, _max);
}
public DunGen.FloatRange GetDungenFloatRange(){
return new DunGen.FloatRange(min, max);
}
}
[System.Serializable]
public class BranchingPathRange {
private string name;
public IntRange count;
public IntRange depth;
public BranchingPathRange(string name){
this.name = name;
count = new IntRange($"{name} count");
depth = new IntRange($"{name} depth");
}
public void UpdateValues(DunGen.DungeonArchetype archetype){
archetype.BranchCount = new DunGen.IntRange(count.min, count.max);
archetype.BranchingDepth = new DunGen.IntRange(depth.min, depth.max);
}
public override string ToString() {
return $"{count.ToString()}+{depth.ToString()}";
}
}
[System.Serializable]
public class Enemy {
public bool enabled;
public int health;
public float speed;
}
[System.Serializable]
public class SpawnableEnemy : Enemy {
public int spawnWeightBase;
public float spawnWeightStealPercentage;
}
[System.Serializable]
public class Item {
public bool enabled;
public IntRange valueRange;
public Item(string name) {
valueRange = new IntRange($"{name} value range");
}
}
[System.Serializable]
public class ScrapItem : Item {
public int spawnWeight;
public bool spawnOnAllMoons;
public ScrapItem(string name) : base(name) { }
}
public abstract class ConfigEntryBundleBase {
public class FieldPropertyInfo {
public FieldInfo fieldInfo;
public PropertyInfo propertyInfo;
public FieldPropertyInfo(FieldInfo fieldInfo){
this.fieldInfo = fieldInfo;
}
public FieldPropertyInfo(PropertyInfo propertyInfo){
this.propertyInfo = propertyInfo;
}
public void SetValue(object obj, object value){
if (fieldInfo != null) fieldInfo.SetValue(obj, value);
if (propertyInfo != null) propertyInfo.SetValue(obj, value);
}
public object GetValue(object obj){
if (fieldInfo != null) return fieldInfo.GetValue(obj);
if (propertyInfo != null) return propertyInfo.GetValue(obj);
return null;
}
public Type GetFieldPropertyType(){
if (fieldInfo != null) return fieldInfo.FieldType;
if (propertyInfo != null) return propertyInfo.PropertyType;
return null;
}
}
public abstract IEnumerable<ConfigEntryBase> GetConfigs();
public abstract void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget);
}
public class ConfigEntryBundleExtraParameters {
public Action<PluginConfig> onSettingsChanged;
public bool callOnBind;
public ConfigEntryBundleExtraParameters(Action<PluginConfig> onSettingsChanged, bool callOnBind){
this.onSettingsChanged = onSettingsChanged;
this.callOnBind = callOnBind;
}
public void CallAction(PluginConfig instance, bool isBind){
if (isBind && !callOnBind) return;
onSettingsChanged?.Invoke(instance);
}
}
public class ConfigEntryBundle<T> : ConfigEntryBundleBase {
public ConfigEntry<T> config;
public ConfigDefinition definition;
public T defaultValue;
public ConfigDescription description;
public ConfigEntryBundleExtraParameters extra;
public ConfigEntryBundle(string section, string key, T dvalue, string desc, ConfigEntryBundleExtraParameters extra = null, AcceptableValueBase valuebase = null){
definition = new ConfigDefinition(section, key);
defaultValue = dvalue;
description = new ConfigDescription(desc, valuebase);
if (extra == null) extra = new ConfigEntryBundleExtraParameters(null, false);
this.extra = extra;
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
config = cfg.Bind(definition, defaultValue, description);
config.SettingChanged += (obj, args) => {
Plugin.logger.LogDebug($"Config settings {config.Definition.Key} changed");
memberInfo.SetValue(memberTarget, config.Value);
extra.CallAction(instance, false);
};
memberInfo.SetValue(memberTarget, config.Value);
extra.CallAction(instance, true);
}
public void Bind(ConfigFile cfg, PluginConfig instance){
config = cfg.Bind(definition, defaultValue, description);
config.SettingChanged += (obj, args) => {
Plugin.logger.LogDebug($"Config settings {config.Definition.Key} changed");
extra.CallAction(instance, false);
};
extra.CallAction(instance, true);
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return config;
}
}
public class ConfigEntryBundleMinMax<T> : ConfigEntryBundleBase {
public ConfigEntryBundle<T> min;
public ConfigEntryBundle<T> max;
public ConfigEntryBundleMinMax(string section, string keyMin, string keyMax, T defaultValueMin, T defaultValueMax, string descMin, string descMax, ConfigEntryBundleExtraParameters extra = null, AcceptableValueBase valuebase = null){
min = new ConfigEntryBundle<T>(section, keyMin, defaultValueMin, descMin, extra, valuebase);
max = new ConfigEntryBundle<T>(section, keyMax, defaultValueMax, descMax, extra, valuebase);
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var minProp = type.GetProperty("min", BindingFlags.Public | BindingFlags.Instance);
var maxProp = type.GetProperty("max", BindingFlags.Public | BindingFlags.Instance);
min.Bind(cfg, instance, new FieldPropertyInfo(minProp), memberInfo.GetValue(memberTarget));
max.Bind(cfg, instance, new FieldPropertyInfo(maxProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return min.config;
yield return max.config;
}
}
public class ConfigEntryBundleBranchingPath : ConfigEntryBundleBase {
public ConfigEntryBundleMinMax<int> count;
public ConfigEntryBundleMinMax<int> depth;
public ConfigEntryBundleBranchingPath(string section, int sectionId, string extraMessage, int countMinValue, int countMaxValue, int depthMinValue, int depthMaxValue, ConfigEntryBundleExtraParameters extra = null){
count = new ConfigEntryBundleMinMax<int>(
section,
$"Section {sectionId} Count Min",
$"Section {sectionId} Count Max",
countMinValue,
countMaxValue,
_branchPathBranchCountMessage(sectionId, "minimum", extraMessage),
_branchPathBranchCountMessage(sectionId, "maximum", extraMessage),
extra,
new AcceptableValueRange<int>(0, 20)
);
depth = new ConfigEntryBundleMinMax<int>(
section,
$"Section {sectionId} Depth Min",
$"Section {sectionId} Depth Max",
depthMinValue,
depthMaxValue,
_branchPathBranchDepthMessage(sectionId, "minimum", extraMessage),
_branchPathBranchDepthMessage(sectionId, "maximum", extraMessage),
extra,
new AcceptableValueRange<int>(0, 20)
);
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var countField = type.GetField("count", BindingFlags.Public | BindingFlags.Instance);
var depthField = type.GetField("depth", BindingFlags.Public | BindingFlags.Instance);
count.Bind(cfg, instance, new FieldPropertyInfo(countField), memberInfo.GetValue(memberTarget));
depth.Bind(cfg, instance, new FieldPropertyInfo(depthField), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
foreach(var a in count.GetConfigs()) {
yield return a;
}
foreach(var a in depth.GetConfigs()) {
yield return a;
}
}
}
public class ConfigEntryBundleEnemy : ConfigEntryBundleBase {
public ConfigEntryBundle<bool> enabled;
public ConfigEntryBundle<int> health;
public ConfigEntryBundle<float> speed;
public ConfigEntryBundleEnemy(string section, string enemyName, int baseHealth, float baseSpeed, ConfigEntryBundleExtraParameters extra = null){
var enabledDesc = $"If disabled, {enemyName} is disabled from spawning.";
var healthDesc = $"The health of the {enemyName}. A shovel is 1 damage.";
var speedDesc = $"The speed of the {enemyName} during their killing/aggressive stance. The player's default speed is 4.6.";
enabled = new ConfigEntryBundle<bool>(section, "Enabled", true, enabledDesc, extra);
health = new ConfigEntryBundle<int>(section, "Health", baseHealth, healthDesc, extra, new AcceptableValueRange<int>(1, 20));
speed = new ConfigEntryBundle<float>(section, "Speed", baseSpeed, speedDesc, extra, new AcceptableValueRange<float>(1f, 20f));
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var enabledProp = type.GetField("enabled", BindingFlags.Public | BindingFlags.Instance);
var healthProp = type.GetField("health", BindingFlags.Public | BindingFlags.Instance);
var speedProp = type.GetField("speed", BindingFlags.Public | BindingFlags.Instance);
enabled.Bind(cfg, instance, new FieldPropertyInfo(enabledProp), memberInfo.GetValue(memberTarget));
if (health.defaultValue > 0) health.Bind(cfg, instance, new FieldPropertyInfo(healthProp), memberInfo.GetValue(memberTarget));
speed.Bind(cfg, instance, new FieldPropertyInfo(speedProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return enabled.config;
if (health.defaultValue > 0) yield return health.config;
yield return speed.config;
}
}
public class ConfigEntryBundleSpawnableEnemy : ConfigEntryBundleEnemy {
public ConfigEntryBundle<int> spawnWeightBase;
public ConfigEntryBundle<float> spawnWeightStealPercentage;
public ConfigEntryBundleSpawnableEnemy(string section, string enemyName, string replacementEnemyName, int baseHealth, float baseSpeed, ConfigEntryBundleExtraParameters extra = null) : base(section, enemyName, baseHealth, baseSpeed, extra) {
var weightBaseDesc = $"The base spawn weight of the {enemyName}. This is added onto the spawn weight stolen from the {replacementEnemyName}, or the base weight of 10 if the moon doesn't spawn the {replacementEnemyName}.";
var weightStealPercentageDesc = $"The percentage of spawn weight that the {enemyName} steals from the {replacementEnemyName} for that moon.\nSetting this 0 means that the {replacementEnemyName}'s weight is unaffected and the {enemyName}'s weight is based entirely by Spawn Weight Base.\nSetting this 1 means the {enemyName} effectively replaces the {replacementEnemyName}.";
spawnWeightBase = new ConfigEntryBundle<int>(section, "Spawn Weight Base", 0, weightBaseDesc, extra, new AcceptableValueRange<int>(0, 999));
spawnWeightStealPercentage = new ConfigEntryBundle<float>(section, "Spawn Weight Steal Percentage", 0.75f, weightStealPercentageDesc, extra, new AcceptableValueRange<float>(0f, 1f));
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
base.Bind(cfg, instance, memberInfo, memberTarget);
var type = memberInfo.GetFieldPropertyType();
var weightBaseProp = type.GetField("spawnWeightBase", BindingFlags.Public | BindingFlags.Instance);
var weightStealPercentageProp = type.GetField("spawnWeightStealPercentage", BindingFlags.Public | BindingFlags.Instance);
spawnWeightBase.Bind(cfg, instance, new FieldPropertyInfo(weightBaseProp), memberInfo.GetValue(memberTarget));
spawnWeightStealPercentage.Bind(cfg, instance, new FieldPropertyInfo(weightStealPercentageProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
foreach(var a in base.GetConfigs()) {
yield return a;
}
yield return spawnWeightBase.config;
yield return spawnWeightStealPercentage.config;
}
}
public class ConfigEntryBundleItem : ConfigEntryBundleBase {
public ConfigEntryBundle<bool> enabled;
public ConfigEntryBundleMinMax<int> valueRange;
private static string _valueMessage(string itemName, string minmax) => $"The {minmax} scrap value of {itemName}. Lethal Company multiplies all scrap values by 0.4.";
public ConfigEntryBundleItem(string section, string keyPrefix, string itemName, int baseValueMin, int baseValueMax, ConfigEntryBundleExtraParameters extra = null){
var enabledDesc = $"If disabled, {itemName} is disabled from spawning.";
enabled = new ConfigEntryBundle<bool>(section, $"{keyPrefix}Enabled", true, enabledDesc, extra);
valueRange = new ConfigEntryBundleMinMax<int>(
section,
$"Value Min",
$"Value Max",
baseValueMin,
baseValueMax,
_valueMessage(itemName, "minimum"),
_valueMessage(itemName, "maximum"),
extra,
new AcceptableValueRange<int>(1, 400)
);
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
var type = memberInfo.GetFieldPropertyType();
var enabledProp = type.GetField("enabled", BindingFlags.Public | BindingFlags.Instance);
var valueRangeProp = type.GetField("valueRange", BindingFlags.Public | BindingFlags.Instance);
enabled.Bind(cfg, instance, new FieldPropertyInfo(enabledProp), memberInfo.GetValue(memberTarget));
if (valueRange.min.defaultValue > 0) valueRange.Bind(cfg, instance, new FieldPropertyInfo(valueRangeProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
yield return enabled.config;
if (valueRange.min.defaultValue > 0) {
foreach(var a in valueRange.GetConfigs()) {
yield return a;
}
}
}
}
public class ConfigEntryBundleScrapItem : ConfigEntryBundleItem {
public ConfigEntryBundle<int> spawnWeight;
public ConfigEntryBundle<bool> spawnOnAllMoons;
public ConfigEntryBundleScrapItem(string section, string itemName, int baseValueMin, int baseValueMax, int baseSpawnWeight, ConfigEntryBundleExtraParameters extra = null) : base(section, string.Empty, itemName, baseValueMin, baseValueMax, extra) {
var weightDesc = $"The {itemName}'s spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.";
var spawnDesc = $"If enabled, the {itemName} scrap item will spawn on all moons regardless if SDM loaded.";
spawnWeight = new ConfigEntryBundle<int>(section, "Spawn Weight Base", baseSpawnWeight, weightDesc, extra, new AcceptableValueRange<int>(0, 999));
spawnOnAllMoons = new ConfigEntryBundle<bool>(section, "Spawn On All Moons", false, spawnDesc, extra);
}
public override void Bind(ConfigFile cfg, PluginConfig instance, FieldPropertyInfo memberInfo, object memberTarget) {
base.Bind(cfg, instance, memberInfo, memberTarget);
var type = memberInfo.GetFieldPropertyType();
var weightDescProp = type.GetField("spawnWeight", BindingFlags.Public | BindingFlags.Instance);
var spawnDescProp = type.GetField("spawnOnAllMoons", BindingFlags.Public | BindingFlags.Instance);
spawnWeight.Bind(cfg, instance, new FieldPropertyInfo(weightDescProp), memberInfo.GetValue(memberTarget));
spawnOnAllMoons.Bind(cfg, instance, new FieldPropertyInfo(spawnDescProp), memberInfo.GetValue(memberTarget));
}
public override IEnumerable<ConfigEntryBase> GetConfigs() {
foreach(var a in base.GetConfigs()) {
yield return a;
}
yield return spawnWeight.config;
yield return spawnOnAllMoons.config;
}
}
}
}

View File

@ -1,83 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using HarmonyLib;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using System.Reflection;
namespace ScarletMansion {
public partial class PluginConfig : SyncedInstance<PluginConfig> {
public static void RequestSync(){
if (!IsClient) return;
using (var stream = new FastBufferWriter(IntSize, Unity.Collections.Allocator.Temp)){
SendMessage(stream, requestMessage);
}
}
public static void OnRequestSync(ulong clientId, FastBufferReader _){
if (!IsHost) return;
logger.LogDebug($"Config sync request received from client: {clientId}");
var array = SerializeToBytes(Instance);
var value = array.Length;
using (var stream = new FastBufferWriter(value + IntSize, Unity.Collections.Allocator.Temp)){
try {
stream.WriteValueSafe(value, default(FastBufferWriter.ForPrimitives));
stream.WriteBytesSafe(array);
SendMessage(stream, receiveMessage, clientId);
} catch (Exception e){
logger.LogError($"Error occurred syncing config with client: {clientId}\n{e}");
}
}
}
public static void OnReceiveSync(ulong _, FastBufferReader reader){
if (!reader.TryBeginRead(IntSize)){
logger.LogError("Config sync error: Could not begin reading buffer.");
return;
}
reader.ReadValueSafe(out int val, default(FastBufferWriter.ForPrimitives));
if (!reader.TryBeginRead(val)){
logger.LogError("Config sync error: Host could not sync.");
return;
}
var data = new byte[val];
reader.ReadBytesSafe(ref data, val);
SyncInstance(data);
logger.LogDebug("Successfully synced config with host.");
}
[HarmonyPatch(typeof(GameNetcodeStuff.PlayerControllerB), "ConnectClientToPlayerObject")]
[HarmonyPostfix]
public static void InitializeLocalPlayer(){
if (IsHost){
MessageManager.RegisterNamedMessageHandler(requestMessage, OnRequestSync);
Synced = true;
return;
}
Synced = false;
MessageManager.RegisterNamedMessageHandler(receiveMessage, OnReceiveSync);
RequestSync();
}
[HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")]
[HarmonyPostfix]
public static void PlayerLeave(){
RevertSync();
}
}
}

View File

@ -1,427 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using BepInEx.Configuration;
namespace ScarletMansion {
public class PresetConfig {
public enum DungeonGeneration {
Custom,
Default,
Small,
MoreLoot,
BitMoreLoot,
Vanilla
}
public static ChangeList customChangeList = new ChangeList("Custom", "Disables auto loading of preset config values.");
public static ChangeList defaultGeneration = new ChangeList(
"Default",
"The default generation values. Intended for lobbies with 3+ players.",
//new ChangeMinMaxFloat ( PluginConfig.dunGenMultiplier ),
new ChangeInt ( PluginConfig.dunGenWidthBase ),
new ChangeInt ( PluginConfig.dunGenLengthBase ),
new ChangeFloat ( PluginConfig.dunGenWidthMultiFactor ),
new ChangeFloat ( PluginConfig.dunGenLengthMultiFactor ),
new ChangeInt ( PluginConfig.mainPathCount ),
new ChangeMinMaxInt ( PluginConfig.mainPathLength),
new ChangeBranchingPath( PluginConfig.branchPathSectionOne ),
new ChangeBranchingPath( PluginConfig.branchPathSectionTwo ),
new ChangeBranchingPath( PluginConfig.branchPathSectionThree ),
new ChangeFloat ( PluginConfig.lootMultiplier ),
new ChangeInt ( PluginConfig.paintingCount ),
new ChangeMinMaxInt( PluginConfig.paintingExtraLoot ),
new ChangeInt( PluginConfig.treasureRoomCount ),
new ChangeMinMaxInt( PluginConfig.treasureRoomLoot ),
new ChangeFloat ( PluginConfig.mapHazardsMultiplier ),
new ChangeInt ( PluginConfig.minIndoorEnemySpawnCount )
);
public static ChangeList smallGeneration = new ChangeList(
"Small",
"A smaller variation of the default generation values. Intended for lobbies with 1-3 players.",
defaultGeneration,
new ChangeInt ( PluginConfig.mainPathCount, 2 ),
/*
new ChangeBranchingPath( PluginConfig.branchPathSectionOne, int.MaxValue, int.MaxValue, 3, 4 ),
new ChangeBranchingPath( PluginConfig.branchPathSectionTwo, int.MaxValue, int.MaxValue, 1, 2 ),
new ChangeBranchingPath( PluginConfig.branchPathSectionThree, int.MaxValue, int.MaxValue, 0, 1 ),
*/
new ChangeFloat ( PluginConfig.lootMultiplier, 1.2f ),
new ChangeFloat ( PluginConfig.mapHazardsMultiplier, 1.3f )
);
public static ChangeList moreLootGeneration = new ChangeList(
"More Loot, More Danger",
"Increases the amount of loot, map hazards, and starting enemies in the mansion. Intended for lobbies with 3+ players.",
defaultGeneration,
new ChangeFloat ( PluginConfig.lootMultiplier, 1.8f ),
new ChangeInt ( PluginConfig.paintingCount, 3 ),
new ChangeMinMaxInt( PluginConfig.paintingExtraLoot, 1 , 3 ),
new ChangeInt( PluginConfig.treasureRoomCount, 3 ),
new ChangeMinMaxInt( PluginConfig.treasureRoomLoot, 3, 4 ),
new ChangeFloat ( PluginConfig.mapHazardsMultiplier, 2.2f ),
new ChangeInt ( PluginConfig.minIndoorEnemySpawnCount, 2 )
);
public static ChangeList bitMoreLootGeneration = new ChangeList(
"Bit More Loot, Bit More Danger",
"Increases the amount of loot, map hazards, and starting enemies in the mansion a bit. Intended for lobbies with 1-3 players.",
smallGeneration,
new ChangeFloat ( PluginConfig.lootMultiplier, 1.5f ),
new ChangeInt ( PluginConfig.paintingCount, 3 ),
new ChangeInt( PluginConfig.treasureRoomCount, 3 ),
new ChangeFloat ( PluginConfig.mapHazardsMultiplier, 1.75f ),
new ChangeInt ( PluginConfig.minIndoorEnemySpawnCount, 2 )
);
public static ChangeList vanillaGeneration = new ChangeList(
"Vanilla",
"Changes the dungeon generation to match closer to a vanilla dungeon.",
defaultGeneration,
//new ChangeMinMaxFloat ( PluginConfig.dunGenMultiplier, float.MaxValue, 10f ),
new ChangeInt ( PluginConfig.dunGenWidthBase, 200 ),
new ChangeInt ( PluginConfig.dunGenLengthBase, 200 ),
new ChangeFloat ( PluginConfig.dunGenWidthMultiFactor, 4f ),
new ChangeFloat ( PluginConfig.dunGenLengthMultiFactor, 4f ),
new ChangeInt ( PluginConfig.mainPathCount, 1 ),
new ChangeMinMaxInt ( PluginConfig.mainPathLength, 6, 8 ),
new ChangeBranchingPath( PluginConfig.branchPathSectionOne, 3, 5, 2, 3 ),
new ChangeBranchingPath( PluginConfig.branchPathSectionTwo, 2, 3, 1, 2 ),
new ChangeBranchingPath( PluginConfig.branchPathSectionThree, 1, 2, 0, 1 ),
new ChangeFloat ( PluginConfig.lootMultiplier, 1f ),
new ChangeFloat ( PluginConfig.mapHazardsMultiplier, 1f ),
new ChangeInt ( PluginConfig.minIndoorEnemySpawnCount, 0 )
);
public enum DungeonLighting {
Custom,
Default,
Bright,
Dark
}
// 1, 8, 2
public static ChangeList defaultLighting = new ChangeList(
"Default",
"The default lighting values.",
new ChangeInt ( PluginConfig.hallwayLightsWeight ),
new ChangeInt ( PluginConfig.ceilingLightsWeight ),
new ChangeInt ( PluginConfig.lightsSpawnZeroWeight ),
new ChangeInt ( PluginConfig.lightsSpawnOneWeight ),
new ChangeInt ( PluginConfig.lightsSpawnTwoWeight )
//new ChangeInt ( PluginConfig.lightsSpawnThreeWeight )
);
public static ChangeList brightLighting = new ChangeList(
"Bright",
"Makes light sources much more common.",
new ChangeInt ( PluginConfig.hallwayLightsWeight, 450 ),
new ChangeInt ( PluginConfig.ceilingLightsWeight, 450 ),
new ChangeInt ( PluginConfig.lightsSpawnZeroWeight, 0 ),
new ChangeInt ( PluginConfig.lightsSpawnOneWeight, 7 ),
new ChangeInt ( PluginConfig.lightsSpawnTwoWeight, 3 )
//new ChangeInt ( PluginConfig.lightsSpawnThreeWeight, 2 )
);
public static ChangeList darkLighting = new ChangeList(
"Dark",
"Makes light sources much less common.",
new ChangeInt ( PluginConfig.hallwayLightsWeight, 50 ),
new ChangeInt ( PluginConfig.ceilingLightsWeight, 50 ),
new ChangeInt ( PluginConfig.lightsSpawnZeroWeight, 2 ),
new ChangeInt ( PluginConfig.lightsSpawnOneWeight, 8 ),
new ChangeInt ( PluginConfig.lightsSpawnTwoWeight, 0 )
//new ChangeInt ( PluginConfig.lightsSpawnThreeWeight, 0 )
);
public static List<ChangeList> dungeonGenerationChangeList = new List<ChangeList>() {
customChangeList,
defaultGeneration,
smallGeneration,
moreLootGeneration,
bitMoreLootGeneration,
vanillaGeneration
};
public static List<ChangeList> dungeonLightingChangeList = new List<ChangeList>() {
customChangeList,
defaultLighting,
brightLighting,
darkLighting
};
public static void UpdateFromPresetValue(int value, List<ChangeList> changeList){
if (value >= 0 && value < changeList.Count) {
var target = changeList[value];
target.UpdateChanges();
}
}
public class ChangeList {
public string name;
public string description;
public Change[] changes;
public ChangeList(string name, string description, params Change[] changes){
this.name = name;
this.description = description;
this.changes = changes;
}
public ChangeList(string name, string description, ChangeList baseList, params Change[] changes){
this.name = name;
this.description = description;
var newChanges = new List<Change>();
foreach(var c in baseList.changes){
newChanges.Add(c);
}
foreach(var c in changes){
var added = false;
for(var i = 0; i < newChanges.Count; ++i) {
if (newChanges[i].IsEqual(c)){
newChanges[i] = c;
added = true;
break;
}
}
if (!added) newChanges.Add(c);
}
this.changes = newChanges.ToArray();
}
public void UpdateChanges(){
Plugin.logger.LogDebug($"Updating config values with preset {name}");
foreach(var c in changes){
c.Update();
}
Plugin.Instance.Config.Save();
Plugin.Instance.Config.Reload();
}
public override string ToString() {
var str = string.Empty;
foreach(var c in changes){
str += c.ToString() + "\n";
}
if (string.IsNullOrWhiteSpace(str)){
str = "No changes.\n";
}
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 {
public PluginConfig.ConfigEntryBundle<T> configBundle;
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;
}
public ChangeType(PluginConfig.ConfigEntryBundle<T> configBundle, T value){
this.configBundle = configBundle;
this.value = value;
}
public override void Update(){
configEntry.BoxedValue = value;
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeType<T>;
if (target != null) return configEntry.Definition == target.configEntry.Definition;
return false;
}
public override string ToString() {
return $"{configBundle.definition.Key}: {value}";
}
}
public class ChangeBool : ChangeType<bool> {
public ChangeBool(PluginConfig.ConfigEntryBundle<bool> configEntry) : base (configEntry){ }
public ChangeBool(PluginConfig.ConfigEntryBundle<bool> configEntry, bool value) : base (configEntry, value){ }
}
public class ChangeFloat : ChangeType<float> {
public ChangeFloat(PluginConfig.ConfigEntryBundle<float> configEntry) : base (configEntry){ }
public ChangeFloat(PluginConfig.ConfigEntryBundle<float> configEntry, float value) : base (configEntry, value){ }
}
public class ChangeInt : ChangeType<int> {
public ChangeInt(PluginConfig.ConfigEntryBundle<int> configEntry) : base (configEntry){ }
public ChangeInt(PluginConfig.ConfigEntryBundle<int> configEntry, int value) : base (configEntry, value){ }
}
public class ChangeMinMaxInt : Change {
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);
}
public ChangeMinMaxInt(PluginConfig.ConfigEntryBundleMinMax<int> configBundle, int minValue, int maxValue){
min = new ChangeInt(configBundle.min, minValue == int.MaxValue ? configBundle.min.defaultValue : minValue);
max = new ChangeInt(configBundle.max, maxValue == int.MaxValue ? configBundle.max.defaultValue : maxValue);
}
public override void Update() {
min.Update();
max.Update();
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeMinMaxInt;
if (target != null) return min.IsEqual(target.min) && max.IsEqual(target.max);
return false;
}
public override string ToString() {
return $"{min.ToString()}\n{max.ToString()}";
}
}
public class ChangeMinMaxFloat : Change{
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);
}
public ChangeMinMaxFloat(PluginConfig.ConfigEntryBundleMinMax<float> configBundle, float minValue, float maxValue){
min = new ChangeFloat(configBundle.min, minValue == float.MaxValue ? configBundle.min.defaultValue : minValue);
max = new ChangeFloat(configBundle.max, maxValue == float.MaxValue ? configBundle.max.defaultValue : maxValue);
}
public override void Update() {
min.Update();
max.Update();
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeMinMaxFloat;
if (target != null) return min.IsEqual(target.min) && max.IsEqual(target.max);
return false;
}
public override string ToString() {
return $"{min.ToString()}\n{max.ToString()}";
}
}
public class ChangeBranchingPath : Change{
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);
}
public ChangeBranchingPath(PluginConfig.ConfigEntryBundleBranchingPath configBundle, int countMin, int countMax, int depthMin, int depthMax){
count = new ChangeMinMaxInt(configBundle.count, countMin, countMax);
depth = new ChangeMinMaxInt(configBundle.depth, depthMin, depthMax);
}
public override void Update() {
count.Update();
depth.Update();
}
public override bool IsEqual(Change obj) {
var target = obj as ChangeBranchingPath;
if (target != null) return count.IsEqual(target.count) && depth.IsEqual(target.depth);
return false;
}
public override string ToString() {
return $"{count.ToString()}\n{depth.ToString()}";
}
}
}
}

View File

@ -49,6 +49,9 @@
<Reference Include="DunGenPlus"> <Reference Include="DunGenPlus">
<HintPath>..\..\..\Libraries\DunGenPlus.dll</HintPath> <HintPath>..\..\..\Libraries\DunGenPlus.dll</HintPath>
</Reference> </Reference>
<Reference Include="DungeonFoyer">
<HintPath>..\..\..\Libraries\DungeonFoyer.dll</HintPath>
</Reference>
<Reference Include="FacilityMeltdown"> <Reference Include="FacilityMeltdown">
<HintPath>..\..\..\Libraries\FacilityMeltdown.dll</HintPath> <HintPath>..\..\..\Libraries\FacilityMeltdown.dll</HintPath>
</Reference> </Reference>
@ -126,6 +129,10 @@
<Reference Include="UnityEngine.CoreModule"> <Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Libraries\UnityEngine.CoreModule.dll</HintPath> <HintPath>..\..\..\Libraries\UnityEngine.CoreModule.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine.ImageConversionModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Libraries\UnityEngine.ImageConversionModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputModule"> <Reference Include="UnityEngine.InputModule">
<HintPath>..\..\..\Libraries\UnityEngine.InputModule.dll</HintPath> <HintPath>..\..\..\Libraries\UnityEngine.InputModule.dll</HintPath>
</Reference> </Reference>
@ -147,6 +154,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Assets.cs" /> <Compile Include="Assets.cs" />
<Compile Include="Configs\ConfigMain.cs" />
<Compile Include="Configs\ConfigManager.cs" />
<Compile Include="Configs\ConfigDungeonFoyer.cs" />
<Compile Include="Configs\ConfigDungeon.cs" />
<Compile Include="Configs\ConfigManagerData.cs" />
<Compile Include="Configs\Enemies.cs" />
<Compile Include="Configs\IConfigDungeon.cs" />
<Compile Include="Configs\Items.cs" />
<Compile Include="DunGenPatch\GeneratePathPatch.cs" /> <Compile Include="DunGenPatch\GeneratePathPatch.cs" />
<Compile Include="DunGenPatch\Patch.cs" /> <Compile Include="DunGenPatch\Patch.cs" />
<Compile Include="GamePatch\Components\FloorCleanup.cs" /> <Compile Include="GamePatch\Components\FloorCleanup.cs" />
@ -178,6 +193,10 @@
<Compile Include="GamePatch\Components\TreasureRoom\TreasureRoomTimeEvent.cs" /> <Compile Include="GamePatch\Components\TreasureRoom\TreasureRoomTimeEvent.cs" />
<Compile Include="GamePatch\Components\TreasureRoom\TreasureRoomBookPullEvent.cs" /> <Compile Include="GamePatch\Components\TreasureRoom\TreasureRoomBookPullEvent.cs" />
<Compile Include="GamePatch\DoorLockPatch.cs" /> <Compile Include="GamePatch\DoorLockPatch.cs" />
<Compile Include="GamePatch\DynamicBone\DynamicBone.cs" />
<Compile Include="GamePatch\DynamicBone\DynamicBoneCollider.cs" />
<Compile Include="GamePatch\DynamicBone\DynamicBoneColliderBase.cs" />
<Compile Include="GamePatch\DynamicBone\DynamicBonePlaneCollider.cs" />
<Compile Include="GamePatch\Enemies\KnightGhostVariant.cs" /> <Compile Include="GamePatch\Enemies\KnightGhostVariant.cs" />
<Compile Include="GamePatch\Enemies\KnightV2Variant.cs" /> <Compile Include="GamePatch\Enemies\KnightV2Variant.cs" />
<Compile Include="GamePatch\Enemies\KnightVariant.cs" /> <Compile Include="GamePatch\Enemies\KnightVariant.cs" />
@ -196,6 +215,7 @@
<Compile Include="GamePatch\Items\FlandreCrystal.cs" /> <Compile Include="GamePatch\Items\FlandreCrystal.cs" />
<Compile Include="GamePatch\Items\IScarletItem.cs" /> <Compile Include="GamePatch\Items\IScarletItem.cs" />
<Compile Include="GamePatch\Items\ScarletFlashlight.cs" /> <Compile Include="GamePatch\Items\ScarletFlashlight.cs" />
<Compile Include="GamePatch\Items\ScarletGohei.cs" />
<Compile Include="GamePatch\Items\ScarletKnife.cs" /> <Compile Include="GamePatch\Items\ScarletKnife.cs" />
<Compile Include="GamePatch\Items\ScarletPainting.cs" /> <Compile Include="GamePatch\Items\ScarletPainting.cs" />
<Compile Include="GamePatch\Items\ScarletSnowGlobe.cs" /> <Compile Include="GamePatch\Items\ScarletSnowGlobe.cs" />
@ -233,10 +253,8 @@
<Compile Include="ModPatch\ReservedFlashlightPatch.cs" /> <Compile Include="ModPatch\ReservedFlashlightPatch.cs" />
<Compile Include="ModPatch\ReservedKeyPatch.cs" /> <Compile Include="ModPatch\ReservedKeyPatch.cs" />
<Compile Include="Plugin.cs" /> <Compile Include="Plugin.cs" />
<Compile Include="PluginConfig.cs" /> <Compile Include="Configs\ConfigManagerClasses.cs" />
<Compile Include="PluginConfigClasses.cs" /> <Compile Include="Configs\Presets.cs" />
<Compile Include="PluginConfigNetwork.cs" />
<Compile Include="PresetConfig.cs" />
<Compile Include="PrintProperties.cs" /> <Compile Include="PrintProperties.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SyncedInstance.cs" /> <Compile Include="SyncedInstance.cs" />