From 9d4f02a83ea2eec15bd64802902d924a8846edcc Mon Sep 17 00:00:00 2001 From: mfoltz Date: Fri, 6 Dec 2024 19:12:34 -0600 Subject: [PATCH] - filtering for units spawned in via command from being quest targets - commands and config entries generated and updated automatically, yay --- Bloodcraft.csproj | 49 +- Commands/BloodCommands.cs | 2 +- Commands/ClassCommands.cs | 2 +- Commands/FamiliarCommands.cs | 207 -------- Commands/QuestCommands.cs | 2 - Commands/WeaponCommands.cs | 3 - GenerateREADME.cs | 398 ++++++++++++---- Patches/ServerBootstrapSystemPatches.cs | 2 +- README.md | 4 +- READMETEST.md | 607 ++++++++++++++++++++++++ Services/ConfigService.cs | 467 +++++++++++++++++- Services/EclipseInterface.cs | 2 +- Utilities/EntityUtilities.cs | 5 +- 13 files changed, 1407 insertions(+), 343 deletions(-) create mode 100644 READMETEST.md diff --git a/Bloodcraft.csproj b/Bloodcraft.csproj index 6f35963..5acb000 100644 --- a/Bloodcraft.csproj +++ b/Bloodcraft.csproj @@ -1,19 +1,22 @@ ο»Ώ - - net6.0 - enable - Bloodcraft - 1.5.3 - True - - https://api.nuget.org/v3/index.json; - https://nuget.bepinex.dev/v3/index.json; - - True - io.zfolmt.Bloodcraft - preview - true + + net6.0 + enable + Bloodcraft + 1.5.3 + True + + https://api.nuget.org/v3/index.json; + https://nuget.bepinex.dev/v3/index.json; + + True + io.zfolmt.Bloodcraft + preview + true + Exe + false + False @@ -39,13 +42,10 @@ - - - - - - - + + + + @@ -53,10 +53,9 @@ - + diff --git a/Commands/BloodCommands.cs b/Commands/BloodCommands.cs index dbe39c0..16e7b66 100644 --- a/Commands/BloodCommands.cs +++ b/Commands/BloodCommands.cs @@ -13,7 +13,7 @@ namespace Bloodcraft.Commands; -[CommandGroup("bloodlegacy", "bl")] +[CommandGroup(name: "bloodlegacy", "bl")] internal static class BloodCommands { static EntityManager EntityManager => Core.EntityManager; diff --git a/Commands/ClassCommands.cs b/Commands/ClassCommands.cs index 56efb1b..84fcf4f 100644 --- a/Commands/ClassCommands.cs +++ b/Commands/ClassCommands.cs @@ -11,7 +11,7 @@ namespace Bloodcraft.Commands; -[CommandGroup("class")] +[CommandGroup(name: "class")] internal static class ClassCommands { static EntityManager EntityManager => Core.EntityManager; diff --git a/Commands/FamiliarCommands.cs b/Commands/FamiliarCommands.cs index adb37c4..2e6c876 100644 --- a/Commands/FamiliarCommands.cs +++ b/Commands/FamiliarCommands.cs @@ -2,7 +2,6 @@ using Bloodcraft.Patches; using Bloodcraft.Services; using Bloodcraft.Utilities; -using Il2CppInterop.Runtime; using ProjectM; using ProjectM.Network; using ProjectM.Scripting; @@ -30,8 +29,6 @@ internal static class FamiliarCommands static SystemService SystemService => Core.SystemService; static PrefabCollectionSystem PrefabCollectionSystem => SystemService.PrefabCollectionSystem; - static readonly PrefabGUID CombatBuff = new(581443919); - static readonly PrefabGUID PvPCombatBuff = new(697095869); static readonly PrefabGUID DominateBuff = new(-1447419822); static readonly PrefabGUID TakeFlightBuff = new(1205505492); @@ -41,21 +38,6 @@ internal static class FamiliarCommands {"Shiny", FamiliarUtilities.ToggleShinies} }; - static readonly ComponentType[] NetworkEventComponents = - [ - ComponentType.ReadOnly(Il2CppType.Of()), - ComponentType.ReadOnly(Il2CppType.Of()), - ComponentType.ReadOnly(Il2CppType.Of()), - ComponentType.ReadOnly(Il2CppType.Of()) - ]; - - static readonly NetworkEventType EventType = new() - { - IsAdminEvent = false, - EventId = NetworkEvents.EventId_RenameInteractable, - IsDebugEvent = false - }; - [Command(name: "bind", shortHand: "b", adminOnly: false, usage: ".fam b [#]", description: "Activates specified familiar from current list.")] public static void BindFamiliar(ChatCommandContext ctx, int boxIndex) { @@ -72,151 +54,6 @@ public static void BindFamiliar(ChatCommandContext ctx, int boxIndex) FamiliarUtilities.BindFamiliar(character, userEntity, steamId, boxIndex); } - //[Command(name: "forcebind", shortHand: "fb", adminOnly: true, usage: ".fam fb [Name] [Box] [#]", description: "Activates specified familiar from entered player box.")] - public static void ForceBindFamiliar(ChatCommandContext ctx, string name, string box, int choice) - { - if (!ConfigService.FamiliarSystem) - { - LocalizationService.HandleReply(ctx, "Familiars are not enabled."); - return; - } - - PlayerInfo playerInfo = PlayerCache.FirstOrDefault(kvp => kvp.Key.ToLower() == name.ToLower()).Value; - if (!playerInfo.UserEntity.Exists()) - { - ctx.Reply($"Couldn't find player."); - return; - } - - Entity character = playerInfo.CharEntity; - Entity userEntity = playerInfo.UserEntity; - ulong steamId = playerInfo.User.PlatformId; - - Entity familiar = FamiliarUtilities.FindPlayerFamiliar(character); - - /* skip this for forcebind - if (ServerGameManager.HasBuff(character, combatBuff.ToIdentifier()) || ServerGameManager.HasBuff(character, pvpCombatBuff.ToIdentifier()) || ServerGameManager.HasBuff(character, dominateBuff.ToIdentifier())) - { - LocalizationService.HandleReply(ctx, "You can't bind a familiar while in combat or dominating presence is active."); - return; - } - */ - - // this is still a good check though - if (familiar.Exists()) - { - LocalizationService.HandleReply(ctx, $"{playerInfo.User.CharacterName.Value} already has an active familiar."); - return; - } - - UnlockedFamiliarData unlocksData = LoadUnlockedFamiliars(steamId); - - string set = unlocksData.UnlockedFamiliars.ContainsKey(box) ? box : ""; - if (string.IsNullOrEmpty(set)) - { - LocalizationService.HandleReply(ctx, $"Couldn't find box for {playerInfo.User.CharacterName.Value}. List player boxes by entering '.fam lpf [Name]' without a following specific box."); - return; - } - - if (steamId.TryGetFamiliarActives(out var data) && data.Familiar.Equals(Entity.Null) && data.FamKey.Equals(0) && unlocksData.UnlockedFamiliars.TryGetValue(set, out var famKeys)) - { - if (choice < 1 || choice > famKeys.Count) - { - LocalizationService.HandleReply(ctx, $"Invalid choice, please use 1 to {famKeys.Count} (Current List: {set})"); - return; - } - - PlayerUtilities.SetPlayerBool(steamId, "Binding", true); - steamId.SetFamiliarDefault(choice); - - data = new(Entity.Null, famKeys[choice - 1]); - steamId.SetFamiliarActives(data); - - SummonFamiliar(character, userEntity, famKeys[choice - 1]); - } - else - { - LocalizationService.HandleReply(ctx, "Couldn't find familiar or familiar already active."); - } - } - - //[Command(name: "listplayerfams", shortHand: "lpf", adminOnly: true, usage: ".fam lpf [Name] [Box]", description: "Lists unlocked familiars from players active box if entered and found or list all player boxes if left blank.")] - public static void ListPlayerFamiliars(ChatCommandContext ctx, string name, string box = "") - { - if (!ConfigService.FamiliarSystem) - { - LocalizationService.HandleReply(ctx, "Familiars are not enabled."); - return; - } - - PlayerInfo playerInfo = PlayerCache.FirstOrDefault(kvp => kvp.Key.ToLower() == name.ToLower()).Value; - if (!playerInfo.UserEntity.Exists()) - { - ctx.Reply($"Couldn't find player."); - return; - } - - ulong steamId = playerInfo.User.PlatformId; - - if (string.IsNullOrEmpty(box)) - { - UnlockedFamiliarData data = LoadUnlockedFamiliars(steamId); - - if (data.UnlockedFamiliars.Keys.Count > 0) - { - List sets = []; - foreach (var key in data.UnlockedFamiliars.Keys) - { - sets.Add(key); - } - - string fams = string.Join(", ", sets.Select(set => $"{set}")); - LocalizationService.HandleReply(ctx, $"Familiar Boxes for {playerInfo.User.CharacterName.Value}: {fams}"); - } - else - { - LocalizationService.HandleReply(ctx, $"{playerInfo.User.CharacterName.Value} doesn't have any unlocked familiars yet."); - } - - return; - } - else - { - UnlockedFamiliarData unlocksData = LoadUnlockedFamiliars(steamId); - FamiliarBuffsData buffsData = LoadFamiliarBuffs(steamId); - - string set = unlocksData.UnlockedFamiliars.ContainsKey(box) ? box : ""; - if (unlocksData.UnlockedFamiliars.TryGetValue(set, out var famKeys)) - { - int count = 1; - - foreach (var famKey in famKeys) - { - PrefabGUID famPrefab = new(famKey); - string famName = famPrefab.GetPrefabName(); - string colorCode = ""; // Default color for the asterisk - - // Check if the familiar has buffs and update the color based on RandomVisuals - if (buffsData.FamiliarBuffs.ContainsKey(famKey)) - { - // Look up the color from the RandomVisuals dictionary if it exists - if (ShinyBuffColorHexMap.TryGetValue(new(buffsData.FamiliarBuffs[famKey][0]), out var hexColor)) - { - colorCode = $""; - } - } - - LocalizationService.HandleReply(ctx, $"{count}: {famName}{(buffsData.FamiliarBuffs.ContainsKey(famKey) ? $"{colorCode}*" : "")}"); - count++; - } - } - else - { - LocalizationService.HandleReply(ctx, "Couldn't locate player box."); - } - } - } - [Command(name: "unbind", shortHand: "ub", adminOnly: false, usage: ".fam ub", description: "Destroys active familiar.")] public static void UnbindFamiliar(ChatCommandContext ctx) { @@ -1109,49 +946,5 @@ public static void ToggleFamiliarSetting(ChatCommandContext ctx, string option) LocalizationService.HandleReply(ctx, $"Invalid option. Please choose from the following: {validOptions}"); } } - - //[Command(name: "name", shortHand: "n", adminOnly: false, usage: ".fam n [Name]", description: "testing")] does not work at all D: - public static void NameFamiliar(ChatCommandContext ctx, string name) - { - if (!ConfigService.FamiliarSystem) - { - LocalizationService.HandleReply(ctx, "Familiars are not enabled."); - return; - } - - Entity familiar = FamiliarUtilities.FindPlayerFamiliar(ctx.Event.SenderCharacterEntity); - familiar.Add(); - - if (!familiar.Exists() || !familiar.Has()) - { - LocalizationService.HandleReply(ctx, "Make sure familiar is active and present before naming it."); - return; - } - - /* - SendEventToUser sendEventToUser = new() - { - UserIndex = ctx.Event.User.Index - }; - networkEntity.Write(sendEventToUser); - */ - - FromCharacter fromCharacter = new() - { - Character = ctx.Event.SenderCharacterEntity, - User = ctx.Event.SenderUserEntity - }; - - InteractEvents_Client.RenameInteractable renameInteractable = new() // named means they show their nameplate? - { - InteractableId = familiar.Read(), - NewName = new Unity.Collections.FixedString64Bytes(name) - }; - - Entity networkEntity = EntityManager.CreateEntity(NetworkEventComponents); - networkEntity.Write(fromCharacter); - networkEntity.Write(EventType); - networkEntity.Write(renameInteractable); - } } diff --git a/Commands/QuestCommands.cs b/Commands/QuestCommands.cs index 9cf67f6..306a284 100644 --- a/Commands/QuestCommands.cs +++ b/Commands/QuestCommands.cs @@ -17,8 +17,6 @@ internal static class QuestCommands static EntityManager EntityManager => Core.EntityManager; static ServerGameManager ServerGameManager => Core.ServerGameManager; - static readonly PrefabGUID ImprisonedBuff = new(1603329680); - [Command(name: "log", adminOnly: false, usage: ".quest log", description: "Toggles quest progress logging.")] public static void LogQuestCommand(ChatCommandContext ctx) { diff --git a/Commands/WeaponCommands.cs b/Commands/WeaponCommands.cs index 792f0fa..a745919 100644 --- a/Commands/WeaponCommands.cs +++ b/Commands/WeaponCommands.cs @@ -4,13 +4,10 @@ using ProjectM; using ProjectM.Scripting; using ProjectM.Shared; -using Steamworks; using Stunlock.Core; -using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; -using UnityEngine.TextCore.Text; using VampireCommandFramework; using static Bloodcraft.Services.PlayerService; using static Bloodcraft.Systems.Expertise.WeaponManager; diff --git a/GenerateREADME.cs b/GenerateREADME.cs index e29b5d8..599f4cb 100644 --- a/GenerateREADME.cs +++ b/GenerateREADME.cs @@ -1,131 +1,341 @@ ο»Ώusing System.Text; using System.Text.RegularExpressions; +using static Bloodcraft.Services.ConfigService; -namespace Bloodcraft; - -internal static class GenerateREADME +namespace Bloodcraft { - // Paths set by the user or build script - static string CommandsPath { get; set; } - static string ReadMePath { get; set; } + internal static class GenerateREADME + { + // Paths set by the user or build script + static string CommandsPath { get; set; } + static string ReadMePath { get; set; } - // Static regex patterns for parsing commands - static readonly Regex CommandGroupRegex = new(@"\[CommandGroup\((?:name:\s*""(?[^""]+)""\s*,\s*)?""(?[^""]+)""(?:\s*,\s*""(?[^""]+)"")?\)\]"); - static readonly Regex CommandAttributeRegex = new(@"\[Command\((?:name:\s*""(?[^""]+)"")?(?:,\s*shortHand:\s*""(?[^""]+)"")?(?:,\s*adminOnly:\s*(?\w+))?(?:,\s*usage:\s*""(?[^""]+)"")?(?:,\s*description:\s*""(?[^""]+)"")?\)\]"); - static readonly Regex CommandSectionPattern = new(@"^(?!.*using\s+static).*?\b[A-Z][a-zA-Z]*Commands\b"); + // Regex patterns for parsing commands + static readonly Regex CommandGroupRegex1 = new(@"\[CommandGroup\(name:\s*""(?[^""]+)"",\s*""(?[^""]+)""\)\]"); // the first and second one here should really just be one but this works and tired so leaving >_> + static readonly Regex CommandGroupRegex2 = new(@"\[CommandGroup\(name:\s*""(?[^""]+)""(?:\s*,\s*short:\s*""(?[^""]+)"")?\)\]"); + static readonly Regex CommandAttributeRegex = new(@"\[Command\(name:\s*""(?[^""]+)""(?:,\s*shortHand:\s*""(?[^""]+)"")?(?:,\s*adminOnly:\s*(?\w+))?(?:,\s*usage:\s*""(?[^""]+)"")?(?:,\s*description:\s*""(?[^""]+)"")?\)\]"); - // Entry point for post-build invocation - public static void Main(string[] args) - { - if (args.Length < 2) + // Constants for README sections + const string COMMANDS_HEADER = "## Commands"; + const string CONFIG_HEADER = "## Configuration"; + + // We'll store all commands in this structure before outputting them + static readonly Dictionary<(string groupName, string groupShort), List<(string name, string shortHand, bool adminOnly, string usage, string description)>> commandsByGroup + = []; + + // Entry point for post-build invocation + public static void Main(string[] args) { - Console.WriteLine("Usage: GenerateREADME "); - return; - } + // Check if we're running in a GitHub Actions environment and skip + if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") == "true") + { + Console.WriteLine("GenerateREADME skipped during GitHub Actions build."); + return; + } - // Set the paths from the command-line arguments - CommandsPath = args[0]; - ReadMePath = args[1]; + if (args.Length < 2) + { + Console.WriteLine("Usage: GenerateREADME "); + return; + } - Generate(); - } + CommandsPath = args[0]; + ReadMePath = args[1]; - // Main method to generate the README - static void Generate() - { - try - { - // Call the command generation logic - GenerateCommandsSection(); - Console.WriteLine("README generated successfully."); + try + { + Generate(); + Console.WriteLine("README generated successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error generating README: {ex.Message}"); + } } - catch (Exception ex) + + // Main method to generate the README + static void Generate() { - Console.WriteLine($"Error generating README: {ex.Message}"); + CollectCommands(); + var commandsSection = BuildCommandsSection(); + var configSection = BuildConfigSection(); + UpdateReadme(commandsSection, configSection); } - } - // Method to generate the commands section of the README - static void GenerateCommandsSection() - { - // Get all C# files from the CommandsPath - string[] files = Directory.GetFiles(CommandsPath, "*.cs"); + // Collect commands from all files into the dictionary + static void CollectCommands() + { + var files = Directory.GetFiles(CommandsPath, "*.cs"); - // StringBuilder to construct new Commands section - StringBuilder commandsSection = new(); - commandsSection.AppendLine("## Commands"); + foreach (var file in files) + { + var fileContent = File.ReadAllText(file); + var commandGroupMatch = CommandGroupRegex1.Match(fileContent); - foreach (string file in files) - { - // Load the file content - string[] fileLines = File.ReadAllLines(file); + if (!commandGroupMatch.Success) commandGroupMatch = CommandGroupRegex2.Match(fileContent); - // Extract command section - string commandSection = Regex.Replace(fileLines.First(line => CommandSectionPattern.IsMatch(line)), "(? CommandGroupRegex.IsMatch(line)); - if (commandGroupLine != null) + // If optional attributes are missing, they won't have .Success = true + string shortHand = commandMatch.Groups["shortHand"].Success ? commandMatch.Groups["shortHand"].Value : string.Empty; + bool adminOnly = false; + + if (commandMatch.Groups["adminOnly"].Success) + { + bool.TryParse(commandMatch.Groups["adminOnly"].Value, out adminOnly); + } + + string usage = commandMatch.Groups["usage"].Success ? commandMatch.Groups["usage"].Value : string.Empty; + string description = commandMatch.Groups["description"].Success ? commandMatch.Groups["description"].Value : string.Empty; + + cmdList.Add((name, shortHand, adminOnly, usage, description)); + } + } + } + + static string BuildCommandsSection() + { + StringBuilder sb = new(); + sb.AppendLine("## Commands"); + sb.AppendLine(); + + var orderedGroups = commandsByGroup.Keys.OrderBy(g => g.groupName).ToList(); + + foreach (var group in orderedGroups) { - var match = CommandGroupRegex.Match(commandGroupLine); - commandGroup = match.Groups[1].Success ? match.Groups[1].Value : match.Groups[3].Value; - commandGroupShort = match.Groups[2].Success ? match.Groups[2].Value : ""; + var (groupName, groupShort) = group; + sb.AppendLine($"### {Capitalize(groupName)} Commands"); + + var cmdList = commandsByGroup[group]; + foreach (var (name, shortHand, adminOnly, usage, description) in cmdList) + { + bool hasShorthand = !string.IsNullOrEmpty(shortHand); + bool hasGroupShort = !string.IsNullOrEmpty(groupShort); + + // If has parameters and no shorthand replace + string commandUsage = string.IsNullOrEmpty(usage) ? name : usage; + string nameReplacement = commandUsage.EndsWith(name) || !hasShorthand ? name : string.Empty; + + // Prebuild command line strings + string adminLock = adminOnly ? " πŸ”’" : string.Empty; + string commandParameters = string.Empty; + + if (hasGroupShort) + { + commandParameters = hasShorthand ? commandUsage.Replace($".{groupShort} {shortHand}", "") : commandUsage.Replace($".{groupShort} {nameReplacement}", ""); + } + else + { + commandParameters = hasShorthand ? commandUsage.Replace($".{groupName} {shortHand}", "") : commandUsage.Replace($".{groupName} {nameReplacement}", ""); + } + + // Build main command line string + var commandLine = $"- `.{groupName} {name}{commandParameters}`{adminLock}"; + + // Handle misc formatting + if (groupName == "misc") + { + commandLine = commandLine.Replace("misc ", ""); + int adjustmentLength = adminOnly ? usage.Length + 3 : usage.Length + 1; // +3 for " πŸ”’" + commandLine = commandLine[..^adjustmentLength] + (adminOnly ? " πŸ”’`" : "`"); + } + + sb.AppendLine(commandLine); + + // Description line if available + if (!string.IsNullOrEmpty(description)) + { + sb.AppendLine($" - {description}"); + } + + sb.AppendLine($" - Shortcut: *{commandUsage}*"); + } + + // Add spacing after each group, except the last one + if (orderedGroups.IndexOf(group) < orderedGroups.Count - 1) + { + sb.AppendLine(); + } } - // Append section title - commandsSection.AppendLine($"\n### {commandSection}"); + return sb.ToString(); + } + static string BuildConfigSection() + { + StringBuilder sb = new(); + sb.AppendLine("## Configuration"); + sb.AppendLine(); - // Find methods with command attribute - foreach (string line in fileLines) + // Group config entries by their section + var groupedConfigEntries = ConfigInitialization.ConfigEntries + .GroupBy(entry => entry.Section) + .OrderBy(group => ConfigInitialization.SectionOrder.IndexOf(group.Key)).ToList(); + + foreach (var group in groupedConfigEntries) { - var match = CommandAttributeRegex.Match(line); - if (match.Success) + sb.AppendLine($"### {group.Key}"); + + foreach (var entry in group) { - string name = match.Groups["name"].Value; - string shortHand = match.Groups["shortHand"].Success ? match.Groups["shortHand"].Value : ""; - string adminOnly = match.Groups["adminOnly"].Value; - string usage = match.Groups["usage"].Value; - string description = match.Groups["description"].Value; - - // Formulate command prefix - string commandPrefix = $"- `.{commandGroup} {usage}`"; - - // Append information to the section - commandsSection.AppendLine(commandPrefix); - commandsSection.AppendLine($" - {description}"); - if (bool.Parse(adminOnly)) + string defaultValue = entry.DefaultValue is string strValue ? $"\"{strValue}\"" : entry.DefaultValue.ToString(); + string typeName = entry.DefaultValue.GetType().Name.ToLower(); + + // Adjust type naming for readability + if (typeName == "boolean") typeName = "bool"; + else if (typeName == "single") typeName = "float"; + else if (typeName == "int32") typeName = "int"; + + sb.AppendLine($"- **{AddSpacesToCamelCase(entry.Key)}**: `{entry.Key}` ({typeName}, default: {defaultValue})"); + if (!string.IsNullOrEmpty(entry.Description)) { - commandsSection.AppendLine($" - Admin-only"); + sb.AppendLine($" {entry.Description}"); } } + + // Add spacing after each group, except the last one + if (groupedConfigEntries.IndexOf(group) < groupedConfigEntries.Count - 1) + { + sb.AppendLine(); + } } + + return sb.ToString(); } + static string AddSpacesToCamelCase(string input) + { + if (string.IsNullOrEmpty(input)) + return input; - // Write the new Commands section to the README - UpdateReadme(commandsSection.ToString()); - } + StringBuilder sb = new(); + for (int i = 0; i < input.Length; i++) + { + char current = input[i]; - // Method to update the README with the new Commands section - static void UpdateReadme(string commandsSection) - { - // Load the existing README file - string[] readmeLines = File.ReadAllLines(ReadMePath); + // Check for capital letters but ignore consecutive ones (e.g., XP) + bool isUpperCase = char.IsUpper(current); + bool isNotFirstChar = i > 0; + bool isPreviousCharLowerCase = isNotFirstChar && char.IsLower(input[i - 1]); + bool isNextCharLowerCase = (i < input.Length - 1) && char.IsLower(input[i + 1]); + + if (isNotFirstChar && isUpperCase && (isPreviousCharLowerCase || isNextCharLowerCase)) + { + sb.Append(' '); + } + + sb.Append(current); + } + + return sb.ToString(); + } + static void UpdateReadme(string commandsSection, string configSection) + { + bool inCommandsSection = false; + bool inConfigSection = false; + bool commandsReplaced = false; + bool configReplaced = false; + + List newContent = []; + + try + { + foreach (string line in File.ReadLines(ReadMePath)) + { + if (line.Trim().Equals(COMMANDS_HEADER, StringComparison.OrdinalIgnoreCase)) + { + // Start of "## Commands" + inCommandsSection = true; + commandsReplaced = true; - // Find start and end of the Commands section - int commandsStartIndex = Array.FindIndex(readmeLines, line => line.StartsWith("## Commands")); - int commandsEndIndex = Array.FindIndex(readmeLines, commandsStartIndex + 1, line => line.StartsWith("## ")); + newContent.Add(commandsSection); // Add new commands - // Replace the old Commands section with the new one - StringBuilder updatedReadme = new(); - updatedReadme.Append(string.Join(Environment.NewLine, readmeLines.Take(commandsStartIndex))); - updatedReadme.AppendLine(commandsSection); - updatedReadme.Append(string.Join(Environment.NewLine, readmeLines.Skip(commandsEndIndex))); + continue; + } + + if (line.Trim().Equals(CONFIG_HEADER, StringComparison.OrdinalIgnoreCase)) + { + // Start of "## Configuration" + inConfigSection = true; + configReplaced = true; + + newContent.Add(configSection); // Add new configuration + + continue; + } + + if (inCommandsSection && line.Trim().StartsWith("## ", StringComparison.OrdinalIgnoreCase) && + !line.Trim().Equals(COMMANDS_HEADER, StringComparison.OrdinalIgnoreCase)) + { + // Reached the next section or a new header + inCommandsSection = false; + } + + if (inConfigSection && line.Trim().StartsWith("## ", StringComparison.OrdinalIgnoreCase) && + !line.Trim().Equals(CONFIG_HEADER, StringComparison.OrdinalIgnoreCase)) + { + // Reached the next section or a new header + inConfigSection = false; + } + + if (!inCommandsSection && !inConfigSection) + { + newContent.Add(line); + } + } + + if (inConfigSection) + { + newContent.Add(configSection); + inConfigSection = false; + } + + if (!commandsReplaced) + { + // Append new section if "## Commands" not found + newContent.Add(COMMANDS_HEADER); + newContent.Add(commandsSection); + } + + if (!configReplaced) + { + // Append new config section if "## Configuration" not found + newContent.Add(CONFIG_HEADER); + newContent.Add(configSection); + } + + File.WriteAllLines(ReadMePath, newContent); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error updating the readme: {ex.Message}"); + throw; + } + } - // Write the updated content back to the README file - File.WriteAllText(ReadMePath, updatedReadme.ToString()); + // Helper method to capitalize strings + static string Capitalize(string input) => + string.IsNullOrEmpty(input) ? input : char.ToUpper(input[0]) + input[1..]; } } \ No newline at end of file diff --git a/Patches/ServerBootstrapSystemPatches.cs b/Patches/ServerBootstrapSystemPatches.cs index b8b7813..d12493a 100644 --- a/Patches/ServerBootstrapSystemPatches.cs +++ b/Patches/ServerBootstrapSystemPatches.cs @@ -422,7 +422,7 @@ static IEnumerator UpdatePlayerData(ulong steamId, Entity playerCharacter, Entit //if (exists) FamiliarUtilities.ClearBuffers(playerCharacter, steamId); Entity familiar = FamiliarUtilities.FindPlayerFamiliar(playerCharacter); - + if (!familiar.Exists()) FamiliarUtilities.ClearFamiliarActives(steamId); else { diff --git a/README.md b/README.md index a0fc588..c53c94e 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ Jairon Orellana; Odjit; Jera; Eve winters; Kokuren TCG and Gaming Shop; - `.prepareforthehunt` - Completes GettingReadyForTheHunt if not already completed. This shouldn't be needed at this point but leaving just incase. - Shortcut: *.prepare* -- `.lockspell` +- `.lockspells` - Enables registering spells to use in unarmed slots if extra slots for unarmed are enabled. Toggle, move spells to slots, then toggle again and switch to unarmed. - Shortcut: *.locksp* - `.lockshift` @@ -315,7 +315,7 @@ Jairon Orellana; Odjit; Jera; Eve winters; Kokuren TCG and Gaming Shop; Enable or disable allowing clients to register with server for UI updates. - **Elite Shard Bearers**: `EliteShardBearers` (bool, default: false) Enable or disable elite shard bearers (significant increases to health, damage, movement speed and attack speed but will no longer scale to number of players). -- **Shard Bearer Level**: `EliteShardBearers` (bool, default: false) +- **Shard Bearer Level**: `ShardBearerLevel` (int, default: 0) Sets level of shard bearers if elite shard bearers is enabled (will override game vblood settings for shard bearers as this is set when they spawn). Leave at 0 for no change. ### Leveling System diff --git a/READMETEST.md b/READMETEST.md new file mode 100644 index 0000000..6a40792 --- /dev/null +++ b/READMETEST.md @@ -0,0 +1,607 @@ +## Table of Contents + +Commands/config up to date, better feature summaries next on the list. + +- [Sponsors](#sponsors) +- [Features](#features) +- [Commands](#commands) +- [Configuration](#configuration) +- [Recommended Mods](#recommended) + +## Sponsor this project + +[![patreon](https://i.imgur.com/u6aAqeL.png)](https://www.patreon.com/join/4865914) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/zfolmt) + +## Sponsors + +Jairon Orellana; Odjit; Jera; Eve winters; Kokuren TCG and Gaming Shop; + +## Commands + +### Bloodlegacy Commands +- `.bloodlegacy get [BloodType]` + - Display your current blood legacy progress. + - Shortcut: *.bl get [BloodType]* +- `.bloodlegacy log` + - Toggles Legacy progress logging. + - Shortcut: *.bl log* +- `.bloodlegacy choosestat [Blood] [BloodStat]` + - Choose a blood stat to enhance based on your legacy. + - Shortcut: *.bl cst [Blood] [BloodStat]* +- `.bloodlegacy resetstats` + - Reset stats for current blood. + - Shortcut: *.bl rst* +- `.bloodlegacy liststats` + - Lists blood stats available. + - Shortcut: *.bl lst* +- `.bloodlegacy set [Player] [Blood] [Level]` πŸ”’ + - Sets player Blood Legacy level. + - Shortcut: *.bl set [Player] [Blood] [Level]* +- `.bloodlegacy list` + - Lists blood legacies available. + - Shortcut: *.bl l* + +### Class Commands +- `.class choose [Class]` + - Choose class. + - Shortcut: *.class c [Class]* +- `.class choosespell [#]` + - Sets shift spell for class if prestige level is high enough. + - Shortcut: *.class csp [#]* +- `.class change [Class]` + - Change classes. + - Shortcut: *.class change [Class]* +- `.class syncbuffs` + - Applies class buffs appropriately if not present. + - Shortcut: *.class sb* +- `.class list` + - Lists classes. + - Shortcut: *.class l* +- `.class listbuffs [ClassType]` + - Shows perks that can be gained from class. + - Shortcut: *.class lb [ClassType]* +- `.class listspells [ClassType]` + - Shows spells that can be gained from class. + - Shortcut: *.class lsp [ClassType]* +- `.class liststats [Class]` + - Shows weapon and blood stat synergies for a class. + - Shortcut: *.class lst [Class]* + +### Familiar Commands +- `.familiar bind [#]` + - Activates specified familiar from current list. + - Shortcut: *.fam b [#]* +- `.familiar unbind` + - Destroys active familiar. + - Shortcut: *.fam ub* +- `.familiar list` + - Lists unlocked familiars from current box. + - Shortcut: *.fam l* +- `.familiar boxes` + - Shows the available familiar boxes. + - Shortcut: *.fam box* +- `.familiar choosebox [Name]` + - Choose active box of familiars. + - Shortcut: *.fam cb [Name]* +- `.familiar renamebox [CurrentName] [NewName]` + - Renames a box. + - Shortcut: *.fam rb [CurrentName] [NewName]* +- `.familiar movebox [BoxName]` + - Moves active familiar to specified box. + - Shortcut: *.fam mb [BoxName]* +- `.familiar deletebox [BoxName]` + - Deletes specified box if empty. + - Shortcut: *.fam db [BoxName]* +- `.familiar addbox [BoxName]` + - Adds empty box with name. + - Shortcut: *.fam ab [BoxName]* +- `.familiar add [Name] [PrefabGUID/CHAR_Unit_Name]` πŸ”’ + - Unit testing. + - Shortcut: *.fam a [Name] [PrefabGUID/CHAR_Unit_Name]* +- `.familiar remove [#]` + - Removes familiar from current set permanently. + - Shortcut: *.fam r [#]* +- `.familiar getlevel` + - Display current familiar leveling progress. + - Shortcut: *.fam gl* +- `.familiar setlevel [Player] [Level]` πŸ”’ + - Set current familiar level. + - Shortcut: *.fam sl [Player] [Level]* +- `.familiar prestige [BonusStat]` + - Prestiges familiar if at max, raising base stats by configured multiplier and adding an extra chosen stat. + - Shortcut: *.fam pr [BonusStat]* +- `.familiar reset` + - Resets (destroys) entities found in followerbuffer and clears familiar actives data. + - Shortcut: *.fam reset* +- `.familiar search [Name]` + - Searches boxes for unit with entered name. + - Shortcut: *.fam s [Name]* +- `.familiar shinybuff [SpellSchool]` + - Chooses shiny for current active familiar, one freebie then costs configured amount to change if already unlocked. + - Shortcut: *.fam shiny [SpellSchool]* +- `.familiar resetshiny [Name]` πŸ”’ + - Allows player to choose another free visual, however, does not erase any visuals they have chosen previously. Mainly for testing. + - Shortcut: *.fam rs [Name]* +- `.familiar toggleoption [Setting]` + - Toggles various familiar settings. + - Shortcut: *.fam option [Setting]* + +### Level Commands +- `.level log` + - Toggles leveling progress logging. + - Shortcut: *.lvl log* +- `.level get` + - Display current leveling progress. + - Shortcut: *.lvl get* +- `.level set [Player] [Level]` πŸ”’ + - Sets player level. + - Shortcut: *.lvl set [Player] [Level]* + +### Misc Commands +- `.reminders` + - Toggles general reminders for various mod features. + - Shortcut: *.remindme* +- `.sct` + - Toggles scrolling text. + - Shortcut: *.sct* +- `.starterkit` + - Provides starting kit. + - Shortcut: *.kitme* +- `.prepareforthehunt` + - Completes GettingReadyForTheHunt if not already completed. + - Shortcut: *.prepare* +- `.lockspells` + - Locks in the next spells equipped to use in your unarmed slots. + - Shortcut: *.locksp* +- `.lockshift` + - Toggle shift spell. + - Shortcut: *.shift* +- `.userstats` + - Shows neat information about the player. + - Shortcut: *.userstats* +- `.silence` + - Resets music for player. + - Shortcut: *.silence* +- `.exoform` + - Toggles taunting to enter exo form. + - Shortcut: *.exoform* +- `.cleanupfams. πŸ”’` + - Removes disabled, invisible familiars on the map preventing building. + - Shortcut: *.cleanupfams* + +### Party Commands +- `.party toggleinvites` + - Toggles being able to be invited to parties, prevents damage and share exp. + - Shortcut: *.party inv* +- `.party add [Player]` + - Adds player to party. + - Shortcut: *.party a [Player]* +- `.party remove [Player]` + - Removes player from party. + - Shortcut: *.party r [Player]* +- `.party listmembers` + - Lists party members of your active party. + - Shortcut: *.party lm* +- `.party disband` + - Disbands party. + - Shortcut: *.party end* +- `.party leave` + - Leaves party if in one. + - Shortcut: *.party drop* + +### Prestige Commands +- `.prestige self [PrestigeType]` + - Handles player prestiging. + - Shortcut: *.prestige me [PrestigeType]* +- `.prestige set [Name] [PrestigeType] [Level]` πŸ”’ + - Sets the specified player to a certain level of prestige in a certain type of prestige. + - Shortcut: *.prestige set [Name] [PrestigeType] [Level]* +- `.prestige listbuffs` + - Lists prestige buff names. + - Shortcut: *.prestige lb* +- `.prestige reset [Name] [PrestigeType]` πŸ”’ + - Handles resetting prestiging. + - Shortcut: *.prestige r [Name] [PrestigeType]* +- `.prestige syncbuffs` + - Applies prestige buffs appropriately if not present. + - Shortcut: *.prestige sb* +- `.prestige get [PrestigeType]` + - Shows information about player's prestige status. + - Shortcut: *.prestige get [PrestigeType]* +- `.prestige list` + - Lists prestiges available. + - Shortcut: *.prestige l* +- `.prestige leaderboard [PrestigeType]` + - Lists prestige leaderboard for type. + - Shortcut: *.prestige lb [PrestigeType]* +- `.prestige shroud` + - Toggles permashroud if applicable. + - Shortcut: *.prestige shroud* + +### Profession Commands +- `.profession log` + - Toggles profession progress logging. + - Shortcut: *.prof log* +- `.profession get [Profession]` + - Display your current profession progress. + - Shortcut: *.prof get [Profession]* +- `.profession set [Name] [Profession] [Level]` πŸ”’ + - Sets player profession level. + - Shortcut: *.prof set [Name] [Profession] [Level]* +- `.profession list` + - Lists professions available. + - Shortcut: *.prof l* + +### Quest Commands +- `.quest log` + - Toggles quest progress logging. + - Shortcut: *.quest log* +- `.quest progress [QuestType]` + - Display your current quest progress. + - Shortcut: *.quest p [QuestType]* +- `.quest track [QuestType]` + - Locate and track quest target. + - Shortcut: *.quest t [QuestType]* +- `.quest refresh [Name]` πŸ”’ + - Refreshes daily and weekly quests for player. + - Shortcut: *.quest rf [Name]* +- `.quest reroll [QuestType]` + - Reroll quest for cost (daily only currently). + - Shortcut: *.quest r [QuestType]* + +### Weapon Commands +- `.weapon getexpertise` + - Displays your current expertise. + - Shortcut: *.wep get* +- `.weapon logexpertise` + - Toggles expertise logging. + - Shortcut: *.wep log* +- `.weapon choosestat [Weapon] [WeaponStat]` + - Choose a weapon stat to enhance based on your expertise. + - Shortcut: *.wep cst [Weapon] [WeaponStat]* +- `.weapon resetwepstats` + - Reset the stats for current weapon. + - Shortcut: *.wep rst* +- `.weapon setexpertise [Name] [Weapon] [Level]` πŸ”’ + - Sets player weapon expertise level. + - Shortcut: *.wep set [Name] [Weapon] [Level]* +- `.weapon liststats` + - Lists weapon stats available. + - Shortcut: *.wep lst* +- `.weapon list` + - Lists weapon expertises available. + - Shortcut: *.wep l* +- `.weapon setspells [Name] [Slot] [PrefabGUID] [Radius]` πŸ”’ + - Manually sets spells for testing (if you enter a radius it will apply to players around the entered name). + - Shortcut: *.wep spell [Name] [Slot] [PrefabGUID] [Radius]* +- `.weapon restorelevels` + - Fixes weapon levels if they are not correct. Don't use this unless you need to. + - Shortcut: *.wep restore* + +## Configuration + +### General +- **Language Localization**: `LanguageLocalization` (string, default: "English") + The language localization for prefabs displayed to users. English by default. Options: Brazilian, English, French, German, Hungarian, Italian, Japanese, Koreana, Latam, Polish, Russian, SimplifiedChinese, Spanish, TraditionalChinese, Thai, Turkish, Vietnamese +- **Client Companion**: `ClientCompanion` (bool, default: False) + Enable if using the client companion mod, can configure what's displayed in the client config. +- **Elite Shard Bearers**: `EliteShardBearers` (bool, default: False) + Enable or disable elite shard bearers. +- **Shard Bearer Level**: `ShardBearerLevel` (int, default: 0) + Sets level of shard bearers if elite shard bearers is enabled. Leave at 0 for no effect. +- **Potion Stacking**: `PotionStacking` (bool, default: True) + Enable or disable potion stacking (can have t01 effects and t02 effects at the same time. also requires professions enabled). + +### StarterKit +- **Starter Kit**: `StarterKit` (bool, default: False) + Enable or disable the starter kit. +- **Kit Prefabs**: `KitPrefabs` (string, default: "862477668,-1531666018,-1593377811,1821405450") + The PrefabGUID hashes for the starter kit. +- **Kit Quantities**: `KitQuantities` (string, default: "500,1000,1000,250") + The quantity of each item in the starter kit. + +### Quests +- **Quest System**: `QuestSystem` (bool, default: False) + Enable or disable quests (currently only kill quests). +- **Infinite Dailies**: `InfiniteDailies` (bool, default: False) + Enable or disable infinite dailies. +- **Quest Rewards**: `QuestRewards` (string, default: "28358550,576389135,-257494203") + The PrefabGUID hashes for quest reward pool. +- **Quest Reward Amounts**: `QuestRewardAmounts` (string, default: "50,250,50") + The amount of each reward in the pool. Will be multiplied accordingly for weeklies (*5) and vblood kill quests (*3). +- **Reroll Daily Prefab**: `RerollDailyPrefab` (int, default: -949672483) + Prefab item for rerolling daily. +- **Reroll Daily Amount**: `RerollDailyAmount` (int, default: 50) + Cost of prefab for rerolling daily. +- **Reroll Weekly Prefab**: `RerollWeeklyPrefab` (int, default: -949672483) + Prefab item for rerolling weekly. +- **Reroll Weekly Amount**: `RerollWeeklyAmount` (int, default: 50) + Cost of prefab for rerolling weekly. Won't work if already completed for the week. + +### Leveling +- **Leveling System**: `LevelingSystem` (bool, default: False) + Enable or disable the leveling system. +- **Rested XP System**: `RestedXPSystem` (bool, default: False) + Enable or disable rested experience for players logging out inside of coffins (half for wooden, full for stone). Prestiging level will reset accumulated rested xp. +- **Rested XP Rate**: `RestedXPRate` (float, default: 0.05) + Rate of Rested XP accumulation per tick (as a percentage of maximum allowed rested XP, if configured to one tick per hour 20 hours offline in a stone coffin will provide maximum current rested XP). +- **Rested XP Max**: `RestedXPMax` (int, default: 5) + Maximum extra levels worth of rested XP a player can accumulate. +- **Rested XP Tick Rate**: `RestedXPTickRate` (float, default: 120) + Minutes required to accumulate one tick of Rested XP. +- **Max Level**: `MaxLevel` (int, default: 90) + The maximum level a player can reach. +- **Starting Level**: `StartingLevel` (int, default: 0) + Starting level for players if no data is found. +- **Unit Leveling Multiplier**: `UnitLevelingMultiplier` (float, default: 7.5) + The multiplier for experience gained from units. +- **V Blood Leveling Multiplier**: `VBloodLevelingMultiplier` (float, default: 15) + The multiplier for experience gained from VBloods. +- **Docile Unit Multiplier**: `DocileUnitMultiplier` (float, default: 0.15) + The multiplier for experience gained from docile units. +- **War Event Multiplier**: `WarEventMultiplier` (float, default: 0.2) + The multiplier for experience gained from war event trash spawns. +- **Unit Spawner Multiplier**: `UnitSpawnerMultiplier` (float, default: 0) + The multiplier for experience gained from unit spawners (vermin nests, tombs). +- **Group Leveling Multiplier**: `GroupLevelingMultiplier` (float, default: 1) + The multiplier for experience gained from group kills. +- **Level Scaling Multiplier**: `LevelScalingMultiplier` (float, default: 0.05) + Reduces experience gained from kills with a large level gap between player and unit, increase to make harsher decrease or set to 0 to remove. +- **Player Parties**: `PlayerParties` (bool, default: False) + Enable or disable the ability to group with players not in your clan for experience/familiar unlock sharing. +- **Max Party Size**: `MaxPartySize` (int, default: 5) + The maximum number of players that can share experience in a group. +- **Exp Share Distance**: `ExpShareDistance` (float, default: 25) + Default is ~5 floor tile lengths. + +### Prestige +- **Prestige System**: `PrestigeSystem` (bool, default: False) + Enable or disable the prestige system (requires leveling to be enabled) +- **Prestige Buffs**: `PrestigeBuffs` (string, default: "1504279833,475045773,1643157297,946705138,-1266262267,-773025435,-1043659405,-1583573438,-1869022798,-536284884") + The PrefabGUID hashes for general prestige buffs, use 0 to skip otherwise buff applies at the prestige level +- **Prestige Levels To Unlock Class Spells**: `PrestigeLevelsToUnlockClassSpells` (string, default: "0,1,2,3,4,5") + The prestige levels at which class spells are unlocked. This should match the number of spells per class +1 to account for the default class spell. Can leave at 0 if you want them unlocked from the start. +- **Max Leveling Prestiges**: `MaxLevelingPrestiges` (int, default: 10) + The maximum number of prestiges a player can reach in leveling +- **Leveling Prestige Reducer**: `LevelingPrestigeReducer` (float, default: 0.05) + Flat factor by which experience is reduced per increment of prestige in leveling +- **Prestige Rates Reducer**: `PrestigeRatesReducer` (float, default: 0.1) + Flat factor by which rates are reduced in expertise/legacy per increment of prestige in expertise/legacy +- **Prestige Stat Multiplier**: `PrestigeStatMultiplier` (float, default: 0.1) + Flat factor by which stats are increased in expertise/legacy bonuses per increment of prestige in expertise/legacy +- **Prestige Rate Multiplier**: `PrestigeRateMultiplier` (float, default: 0.1) + Flat factor by which rates are increased in expertise/legacy per increment of prestige in leveling +- **Exo Prestiging**: `ExoPrestiging` (bool, default: False) + Enable or disable exo prestiges (need to max normal prestiges first). +- **Exo Prestiges**: `ExoPrestiges` (int, default: 100) + The number of exo prestiges available +- **Exo Prestige Reward**: `ExoPrestigeReward` (int, default: 28358550) + The reward for exo prestiging (tier 3 nether shards by default). +- **Exo Prestige Reward Quantity**: `ExoPrestigeRewardQuantity` (int, default: 500) + The quantity of the reward for exo prestiging. + +### Expertise +- **Expertise System**: `ExpertiseSystem` (bool, default: False) + Enable or disable the expertise system +- **Max Expertise Prestiges**: `MaxExpertisePrestiges` (int, default: 10) + The maximum number of prestiges a player can reach in expertise +- **Unarmed Slots**: `UnarmedSlots` (bool, default: False) + Enable or disable the ability to use extra unarmed spell slots +- **Shift Slot**: `ShiftSlot` (bool, default: False) + Enable or disable using class spell on shift +- **Max Expertise Level**: `MaxExpertiseLevel` (int, default: 100) + The maximum level a player can reach in weapon expertise +- **Unit Expertise Multiplier**: `UnitExpertiseMultiplier` (float, default: 2) + The multiplier for expertise gained from units +- **V Blood Expertise Multiplier**: `VBloodExpertiseMultiplier` (float, default: 5) + The multiplier for expertise gained from VBloods +- **Unit Spawner Expertise Factor**: `UnitSpawnerExpertiseFactor` (float, default: 1) + The multiplier for experience gained from unit spawners (vermin nests, tombs). +- **Expertise Stat Choices**: `ExpertiseStatChoices` (int, default: 2) + The maximum number of stat choices a player can pick for a weapon expertise. Max of 3 will be sent to client UI for display. +- **Reset Expertise Item**: `ResetExpertiseItem` (int, default: 576389135) + Item PrefabGUID cost for resetting weapon stats +- **Reset Expertise Item Quantity**: `ResetExpertiseItemQuantity` (int, default: 500) + Quantity of item required for resetting stats +- **Max Health**: `MaxHealth` (float, default: 250) + The base cap for maximum health +- **Movement Speed**: `MovementSpeed` (float, default: 0.25) + The base cap for movement speed +- **Primary Attack Speed**: `PrimaryAttackSpeed` (float, default: 0.1) + The base cap for primary attack speed +- **Physical Life Leech**: `PhysicalLifeLeech` (float, default: 0.1) + The base cap for physical life leech +- **Spell Life Leech**: `SpellLifeLeech` (float, default: 0.1) + The base cap for spell life leech +- **Primary Life Leech**: `PrimaryLifeLeech` (float, default: 0.15) + The base cap for primary life leech +- **Physical Power**: `PhysicalPower` (float, default: 20) + The base cap for physical power +- **Spell Power**: `SpellPower` (float, default: 10) + The base cap for spell power +- **Physical Crit Chance**: `PhysicalCritChance` (float, default: 0.1) + The base cap for physical critical strike chance +- **Physical Crit Damage**: `PhysicalCritDamage` (float, default: 0.5) + The base cap for physical critical strike damage +- **Spell Crit Chance**: `SpellCritChance` (float, default: 0.1) + The base cap for spell critical strike chance +- **Spell Crit Damage**: `SpellCritDamage` (float, default: 0.5) + The base cap for spell critical strike damage + +### Legacies +- **Blood System**: `BloodSystem` (bool, default: False) + Enable or disable the blood legacy system +- **Max Legacy Prestiges**: `MaxLegacyPrestiges` (int, default: 10) + The maximum number of prestiges a player can reach in blood legacies +- **Max Blood Level**: `MaxBloodLevel` (int, default: 100) + The maximum level a player can reach in blood legacies +- **Unit Legacy Multiplier**: `UnitLegacyMultiplier` (float, default: 1) + The multiplier for lineage gained from units +- **V Blood Legacy Multiplier**: `VBloodLegacyMultiplier` (float, default: 5) + The multiplier for lineage gained from VBloods +- **Legacy Stat Choices**: `LegacyStatChoices` (int, default: 2) + The maximum number of stat choices a player can pick for a blood legacy. Max of 3 will be sent to client UI for display. +- **Reset Legacy Item**: `ResetLegacyItem` (int, default: 576389135) + Item PrefabGUID cost for resetting blood stats +- **Reset Legacy Item Quantity**: `ResetLegacyItemQuantity` (int, default: 500) + Quantity of item required for resetting blood stats +- **Healing Received**: `HealingReceived` (float, default: 0.15) + The base cap for healing received +- **Damage Reduction**: `DamageReduction` (float, default: 0.05) + The base cap for damage reduction +- **Physical Resistance**: `PhysicalResistance` (float, default: 0.1) + The base cap for physical resistance +- **Spell Resistance**: `SpellResistance` (float, default: 0.1) + The base cap for spell resistance +- **Resource Yield**: `ResourceYield` (float, default: 0.25) + The base cap for resource yield +- **CC Reduction**: `CCReduction` (float, default: 0.2) + The base cap for crowd control reduction +- **Spell Cooldown Recovery Rate**: `SpellCooldownRecoveryRate` (float, default: 0.1) + The base cap for spell cooldown recovery rate +- **Weapon Cooldown Recovery Rate**: `WeaponCooldownRecoveryRate` (float, default: 0.1) + The base cap for weapon cooldown recovery rate +- **Ultimate Cooldown Recovery Rate**: `UltimateCooldownRecoveryRate` (float, default: 0.2) + The base cap for ultimate cooldown recovery rate +- **Minion Damage**: `MinionDamage` (float, default: 0.25) + The base cap for minion damage +- **Shield Absorb**: `ShieldAbsorb` (float, default: 0.5) + The base cap for shield absorb +- **Blood Efficiency**: `BloodEfficiency` (float, default: 0.1) + The base cap for blood efficiency + +### Professions +- **Profession System**: `ProfessionSystem` (bool, default: False) + Enable or disable the profession system +- **Max Profession Level**: `MaxProfessionLevel` (int, default: 100) + The maximum level a player can reach in professions +- **Profession Multiplier**: `ProfessionMultiplier` (float, default: 10) + The multiplier for profession experience gained +- **Extra Recipes**: `ExtraRecipes` (bool, default: False) + Enable or disable extra recipes + +### Familiars +- **Familiar System**: `FamiliarSystem` (bool, default: False) + Enable or disable the familiar system +- **Share Unlocks**: `ShareUnlocks` (bool, default: False) + Enable or disable sharing unlocks between players in clans or parties (uses exp share distance) +- **Familiar Combat**: `FamiliarCombat` (bool, default: True) + Enable or disable combat for familiars. +- **Familiar Pv P**: `FamiliarPvP` (bool, default: True) + Enable or disable PvP participation for familiars. (if set to false, familiars will be unbound when entering PvP combat) +- **Familiar Prestige**: `FamiliarPrestige` (bool, default: False) + Enable or disable the prestige system for familiars +- **Max Familiar Prestiges**: `MaxFamiliarPrestiges` (int, default: 10) + The maximum number of prestiges a familiar can reach +- **Familiar Prestige Stat Multiplier**: `FamiliarPrestigeStatMultiplier` (float, default: 0.1) + The multiplier for stats gained from familiar prestiges +- **Max Familiar Level**: `MaxFamiliarLevel` (int, default: 90) + The maximum level a familiar can reach +- **Allow V Bloods**: `AllowVBloods` (bool, default: False) + Allow VBloods to be unlocked as familiars (this includes shardbearers, if you want those excluded use the bannedUnits list) +- **Banned Units**: `BannedUnits` (string, default: "") + The PrefabGUID hashes for units that cannot be used as familiars. Same structure as the buff lists except unit prefabs +- **Banned Types**: `BannedTypes` (string, default: "") + The types of units that cannot be used as familiars go here (Human, Undead, Demon, Mechanical, Beast) +- **V Blood Damage Multiplier**: `VBloodDamageMultiplier` (float, default: 1) + Leave at 1 for no change (controls damage familiars do to VBloods) +- **Unit Familiar Multiplier**: `UnitFamiliarMultiplier` (float, default: 7.5) + The multiplier for experience gained from units +- **V Blood Familiar Multiplier**: `VBloodFamiliarMultiplier` (float, default: 15) + The multiplier for experience gained from VBloods +- **Unit Unlock Chance**: `UnitUnlockChance` (float, default: 0.05) + The chance for a unit to unlock a familiar +- **V Blood Unlock Chance**: `VBloodUnlockChance` (float, default: 0.01) + The chance for a VBlood to unlock a familiar +- **Shiny Chance**: `ShinyChance` (float, default: 0.2) + The chance for a visual when unlocking a familiar +- **Shiny Cost Item Prefab**: `ShinyCostItemPrefab` (int, default: -77477508) + Item PrefabGUID cost for changing shiny visual if one is already unlocked (currently demon fragment by default) +- **Shiny Cost Item Quantity**: `ShinyCostItemQuantity` (int, default: 1) + Quantity of item required for changing shiny visual + +### Classes +- **Soft Synergies**: `SoftSynergies` (bool, default: False) + Allow class synergies (turns on classes and does not restrict stat choices, do not use this and hard syergies at the same time) +- **Hard Synergies**: `HardSynergies` (bool, default: False) + Enforce class synergies (turns on classes and restricts stat choices, do not use this and soft syergies at the same time) +- **Change Class Item**: `ChangeClassItem` (int, default: 576389135) + Item PrefabGUID cost for changing class. +- **Change Class Quantity**: `ChangeClassQuantity` (int, default: 750) + Quantity of item required for changing class. +- **Class Spell School On Hit Effects**: `ClassSpellSchoolOnHitEffects` (bool, default: False) + Enable or disable class spell school on hit effects (respective debuff from spell school, leech chill condemn etc). +- **On Hit Proc Chance**: `OnHitProcChance` (float, default: 0.075) + The chance for a class effect to proc on hit. +- **Stat Synergy Multiplier**: `StatSynergyMultiplier` (float, default: 1.5) + Multiplier for class stat synergies to base stat cap +- **Blood Knight Weapon**: `BloodKnightWeapon` (string, default: "0,3,5,6") + Blood Knight weapon synergies +- **Blood Knight Blood**: `BloodKnightBlood` (string, default: "1,5,7,10") + Blood Knight blood synergies +- **Demon Hunter Weapon**: `DemonHunterWeapon` (string, default: "1,2,8,9") + Demon Hunter weapon synergies +- **Demon Hunter Blood**: `DemonHunterBlood` (string, default: "2,5,7,9") + Demon Hunter blood synergies +- **Vampire Lord Weapon**: `VampireLordWeapon` (string, default: "0,4,6,7") + Vampire Lord weapon synergies +- **Vampire Lord Blood**: `VampireLordBlood` (string, default: "1,3,8,11") + Vampire Lord blood synergies +- **Shadow Blade Weapon**: `ShadowBladeWeapon` (string, default: "1,2,6,9") + Shadow Blade weapon synergies +- **Shadow Blade Blood**: `ShadowBladeBlood` (string, default: "3,5,7,10") + Shadow Blade blood synergies +- **Arcane Sorcerer Weapon**: `ArcaneSorcererWeapon` (string, default: "4,7,10,11") + Arcane Sorcerer weapon synergies +- **Arcane Sorcerer Blood**: `ArcaneSorcererBlood` (string, default: "0,6,8,10") + Arcane Sorcerer blood synergies +- **Death Mage Weapon**: `DeathMageWeapon` (string, default: "0,4,7,11") + Death Mage weapon synergies +- **Death Mage Blood**: `DeathMageBlood` (string, default: "2,3,6,9") + Death Mage blood synergies +- **Default Class Spell**: `DefaultClassSpell` (int, default: -433204738) + Default spell (veil of shadow) available to all classes. +- **Blood Knight Buffs**: `BloodKnightBuffs` (string, default: "1828387635,-534491790,-1055766373,-584203677") + The PrefabGUID hashes for blood knight leveling blood buffs. Granted every MaxLevel/(# of blood buffs) +- **Blood Knight Spells**: `BloodKnightSpells` (string, default: "-880131926,651613264,2067760264,189403977,375131842") + Blood Knight shift spells, granted at levels of prestige +- **Demon Hunter Buffs**: `DemonHunterBuffs` (string, default: "-154702686,-285745649,-1510965956,-397097531") + The PrefabGUID hashes for demon hunter leveling blood buffs +- **Demon Hunter Spells**: `DemonHunterSpells` (string, default: "-356990326,-987810170,1071205195,1249925269,-914344112") + Demon Hunter shift spells, granted at levels of prestige +- **Vampire Lord Buffs**: `VampireLordBuffs` (string, default: "1558171501,997154800,-1413561088,1103099361") + The PrefabGUID hashes for vampire lord leveling blood buffs +- **Vampire Lord Spells**: `VampireLordSpells` (string, default: "78384915,295045820,-1000260252,91249849,1966330719") + Vampire Lord shift spells, granted at levels of prestige +- **Shadow Blade Buffs**: `ShadowBladeBuffs` (string, default: "894725875,-1596803256,-993492354,210193036") + The PrefabGUID hashes for shadow blade leveling blood buffs +- **Shadow Blade Spells**: `ShadowBladeSpells` (string, default: "1019568127,1575317901,1112116762,-358319417,1174831223") + Shadow Blade shift spells, granted at levels of prestige +- **Arcane Sorcerer Buffs**: `ArcaneSorcererBuffs` (string, default: "1614027598,884683323,-1576592687,-1859298707") + The PrefabGUID hashes for arcane leveling blood buffs +- **Arcane Sorcerer Spells**: `ArcaneSorcererSpells` (string, default: "247896794,268059675,-242769430,-2053450457,1650878435") + Arcane Sorcerer shift spells, granted at levels of prestige +- **Death Mage Buffs**: `DeathMageBuffs` (string, default: "-901503997,-651661301,1934870645,1201299233") + The PrefabGUID hashes for death mage leveling blood buffs +- **Death Mage Spells**: `DeathMageSpells` (string, default: "-1204819086,481411985,1961570821,2138402840,-1781779733") + Death Mage shift spells, granted at levels of prestige + +## Recommended +- [KindredCommands](https://thunderstore.io/c/v-rising/p/odjit/KindredCommands/) + Highly recommend getting this if you plan on using Bloodcraft or any other mods for V Rising in general. Invaluable set of tools and options that will greatly improve your modding experience. +- [KindredLogistics](https://thunderstore.io/c/v-rising/p/Kindred/KindredLogistics/) + If you mourn QuickStash, this is for you! It comes with much more that will drastically improve your inventory-managing experience in V Rising, only requires server installation. +- [KindredArenas](https://thunderstore.io/c/v-rising/p/odjit/KindredArenas/) + Allows for controlling the areas players can engage each other in on PvP servers. +- [KindredSchematics](https://thunderstore.io/c/v-rising/p/odjit/KindredSchematics/) + Closest thing to creative mode we'll likely ever get. Copy/paste castles, build wherever with whatever you want! +- [KindredPortals](https://thunderstore.io/c/v-rising/p/odjit/KindredPortals/) + Create custom portals and waygates for your server! Pairs great with KindredSchematics for making new areas. +- [Sanguis](https://thunderstore.io/c/v-rising/p/zfolmt/Sanguis/) + Simple login reward/reward players for being online mod. +- [BloodyNotify](https://thunderstore.io/c/v-rising/p/Trodi/Notify/) + Notifications for players coming online or going offline, VBlood kills, and more. +- [BloodyMerchants](https://thunderstore.io/c/v-rising/p/Trodi/BloodyMerchant/) + Custom merchants! Great for letting players buy items they normally can't or providing a use for otherwise unused prefabs. +- [XPRising](https://thunderstore.io/c/v-rising/p/XPRising/XPRising/) + If you like the idea of a mod with RPG features but Bloodcraft doesn't float your boat maybe this will! diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs index 25231c3..04c495c 100644 --- a/Services/ConfigService.cs +++ b/Services/ConfigService.cs @@ -7,6 +7,7 @@ namespace Bloodcraft.Services; public static class ConfigService { + /* public static string LanguageLocalization { get; private set; } public static bool ClientCompanion { get; private set; } public static bool EliteShardBearers { get; private set; } @@ -55,8 +56,6 @@ public static class ConfigService public static int ExoPrestiges { get; private set; } public static int ExoPrestigeReward { get; private set; } public static int ExoPrestigeRewardQuantity { get; private set; } - public static float ExoPrestigeDamageTakenMultiplier { get; private set; } - public static float ExoPrestigePowerBonus { get; private set; } public static bool ExpertiseSystem { get; private set; } public static int MaxExpertisePrestiges { get; private set; } public static bool UnarmedSlots { get; private set; } @@ -154,10 +153,445 @@ public static class ConfigService public static string ArcaneSorcererSpells { get; private set; } public static string DeathMageBuffs { get; private set; } public static string DeathMageSpells { get; private set; } + */ + static readonly Lazy _languageLocalization = new(() => GetConfigValue("LanguageLocalization")); + public static string LanguageLocalization => _languageLocalization.Value; + + static readonly Lazy _clientCompanion = new(() => GetConfigValue("ClientCompanion")); + public static bool ClientCompanion => _clientCompanion.Value; + + static readonly Lazy _eliteShardBearers = new(() => GetConfigValue("EliteShardBearers")); + public static bool EliteShardBearers => _eliteShardBearers.Value; + + static readonly Lazy _shardBearerLevel = new(() => GetConfigValue("ShardBearerLevel")); + public static int ShardBearerLevel => _shardBearerLevel.Value; + + static readonly Lazy _potionStacking = new(() => GetConfigValue("PotionStacking")); + public static bool PotionStacking => _potionStacking.Value; + + static readonly Lazy _starterKit = new(() => GetConfigValue("StarterKit")); + public static bool StarterKit => _starterKit.Value; + + static readonly Lazy _kitPrefabs = new(() => GetConfigValue("KitPrefabs")); + public static string KitPrefabs => _kitPrefabs.Value; + + static readonly Lazy _kitQuantities = new(() => GetConfigValue("KitQuantities")); + public static string KitQuantities => _kitQuantities.Value; + + static readonly Lazy _questSystem = new(() => GetConfigValue("QuestSystem")); + public static bool QuestSystem => _questSystem.Value; + + static readonly Lazy _infiniteDailies = new(() => GetConfigValue("InfiniteDailies")); + public static bool InfiniteDailies => _infiniteDailies.Value; + + static readonly Lazy _questRewards = new(() => GetConfigValue("QuestRewards")); + public static string QuestRewards => _questRewards.Value; + + static readonly Lazy _questRewardAmounts = new(() => GetConfigValue("QuestRewardAmounts")); + public static string QuestRewardAmounts => _questRewardAmounts.Value; + + static readonly Lazy _rerollDailyPrefab = new(() => GetConfigValue("RerollDailyPrefab")); + public static int RerollDailyPrefab => _rerollDailyPrefab.Value; + + static readonly Lazy _rerollDailyAmount = new(() => GetConfigValue("RerollDailyAmount")); + public static int RerollDailyAmount => _rerollDailyAmount.Value; + + static readonly Lazy _rerollWeeklyPrefab = new(() => GetConfigValue("RerollWeeklyPrefab")); + public static int RerollWeeklyPrefab => _rerollWeeklyPrefab.Value; + + static readonly Lazy _rerollWeeklyAmount = new(() => GetConfigValue("RerollWeeklyAmount")); + public static int RerollWeeklyAmount => _rerollWeeklyAmount.Value; + static readonly Lazy _levelingSystem = new(() => GetConfigValue("LevelingSystem")); + public static bool LevelingSystem => _levelingSystem.Value; + + static readonly Lazy _restedXPSystem = new(() => GetConfigValue("RestedXPSystem")); + public static bool RestedXPSystem => _restedXPSystem.Value; + + static readonly Lazy _restedXPRate = new(() => GetConfigValue("RestedXPRate")); + public static float RestedXPRate => _restedXPRate.Value; + + static readonly Lazy _restedXPMax = new(() => GetConfigValue("RestedXPMax")); + public static int RestedXPMax => _restedXPMax.Value; + + static readonly Lazy _restedXPTickRate = new(() => GetConfigValue("RestedXPTickRate")); + public static float RestedXPTickRate => _restedXPTickRate.Value; + + static readonly Lazy _maxLevel = new(() => GetConfigValue("MaxLevel")); + public static int MaxLevel => _maxLevel.Value; + + static readonly Lazy _startingLevel = new(() => GetConfigValue("StartingLevel")); + public static int StartingLevel => _startingLevel.Value; + + static readonly Lazy _unitLevelingMultiplier = new(() => GetConfigValue("UnitLevelingMultiplier")); + public static float UnitLevelingMultiplier => _unitLevelingMultiplier.Value; + + static readonly Lazy _vBloodLevelingMultiplier = new(() => GetConfigValue("VBloodLevelingMultiplier")); + public static float VBloodLevelingMultiplier => _vBloodLevelingMultiplier.Value; + + static readonly Lazy _docileUnitMultiplier = new(() => GetConfigValue("DocileUnitMultiplier")); + public static float DocileUnitMultiplier => _docileUnitMultiplier.Value; + + static readonly Lazy _warEventMultiplier = new(() => GetConfigValue("WarEventMultiplier")); + public static float WarEventMultiplier => _warEventMultiplier.Value; + + static readonly Lazy _unitSpawnerMultiplier = new(() => GetConfigValue("UnitSpawnerMultiplier")); + public static float UnitSpawnerMultiplier => _unitSpawnerMultiplier.Value; + + static readonly Lazy _unitSpawnerExpertiseFactor = new(() => GetConfigValue("UnitSpawnerExpertiseFactor")); + public static float UnitSpawnerExpertiseFactor => _unitSpawnerExpertiseFactor.Value; + + static readonly Lazy _changeClassItem = new(() => GetConfigValue("ChangeClassItem")); + public static int ChangeClassItem => _changeClassItem.Value; + + static readonly Lazy _changeClassQuantity = new(() => GetConfigValue("ChangeClassQuantity")); + public static int ChangeClassQuantity => _changeClassQuantity.Value; + + static readonly Lazy _groupLevelingMultiplier = new(() => GetConfigValue("GroupLevelingMultiplier")); + public static float GroupLevelingMultiplier => _groupLevelingMultiplier.Value; + + static readonly Lazy _levelScalingMultiplier = new(() => GetConfigValue("LevelScalingMultiplier")); + public static float LevelScalingMultiplier => _levelScalingMultiplier.Value; + + static readonly Lazy _playerParties = new(() => GetConfigValue("PlayerParties")); + public static bool PlayerParties => _playerParties.Value; + + static readonly Lazy _maxPartySize = new(() => GetConfigValue("MaxPartySize")); + public static int MaxPartySize => _maxPartySize.Value; + + static readonly Lazy _expShareDistance = new(() => GetConfigValue("ExpShareDistance")); + public static float ExpShareDistance => _expShareDistance.Value; + + static readonly Lazy _prestigeSystem = new(() => GetConfigValue("PrestigeSystem")); + public static bool PrestigeSystem => _prestigeSystem.Value; + + static readonly Lazy _prestigeBuffs = new(() => GetConfigValue("PrestigeBuffs")); + public static string PrestigeBuffs => _prestigeBuffs.Value; + + static readonly Lazy _prestigeLevelsToUnlockClassSpells = new(() => GetConfigValue("PrestigeLevelsToUnlockClassSpells")); + public static string PrestigeLevelsToUnlockClassSpells => _prestigeLevelsToUnlockClassSpells.Value; + + static readonly Lazy _maxLevelingPrestiges = new(() => GetConfigValue("MaxLevelingPrestiges")); + public static int MaxLevelingPrestiges => _maxLevelingPrestiges.Value; + + static readonly Lazy _levelingPrestigeReducer = new(() => GetConfigValue("LevelingPrestigeReducer")); + public static float LevelingPrestigeReducer => _levelingPrestigeReducer.Value; + + static readonly Lazy _prestigeRatesReducer = new(() => GetConfigValue("PrestigeRatesReducer")); + public static float PrestigeRatesReducer => _prestigeRatesReducer.Value; + + static readonly Lazy _prestigeStatMultiplier = new(() => GetConfigValue("PrestigeStatMultiplier")); + public static float PrestigeStatMultiplier => _prestigeStatMultiplier.Value; + + static readonly Lazy _prestigeRateMultiplier = new(() => GetConfigValue("PrestigeRateMultiplier")); + public static float PrestigeRateMultiplier => _prestigeRateMultiplier.Value; + + static readonly Lazy _exoPrestiging = new(() => GetConfigValue("ExoPrestiging")); + public static bool ExoPrestiging => _exoPrestiging.Value; + + static readonly Lazy _exoPrestiges = new(() => GetConfigValue("ExoPrestiges")); + public static int ExoPrestiges => _exoPrestiges.Value; + + static readonly Lazy _exoPrestigeReward = new(() => GetConfigValue("ExoPrestigeReward")); + public static int ExoPrestigeReward => _exoPrestigeReward.Value; + + static readonly Lazy _exoPrestigeRewardQuantity = new(() => GetConfigValue("ExoPrestigeRewardQuantity")); + public static int ExoPrestigeRewardQuantity => _exoPrestigeRewardQuantity.Value; + + static readonly Lazy _expertiseSystem = new(() => GetConfigValue("ExpertiseSystem")); + public static bool ExpertiseSystem => _expertiseSystem.Value; + + static readonly Lazy _maxExpertisePrestiges = new(() => GetConfigValue("MaxExpertisePrestiges")); + public static int MaxExpertisePrestiges => _maxExpertisePrestiges.Value; + + static readonly Lazy _unarmedSlots = new(() => GetConfigValue("UnarmedSlots")); + public static bool UnarmedSlots => _unarmedSlots.Value; + + static readonly Lazy _shiftSlot = new(() => GetConfigValue("ShiftSlot")); + public static bool ShiftSlot => _shiftSlot.Value; + + static readonly Lazy _maxExpertiseLevel = new(() => GetConfigValue("MaxExpertiseLevel")); + public static int MaxExpertiseLevel => _maxExpertiseLevel.Value; + + static readonly Lazy _unitExpertiseMultiplier = new(() => GetConfigValue("UnitExpertiseMultiplier")); + public static float UnitExpertiseMultiplier => _unitExpertiseMultiplier.Value; + + static readonly Lazy _vBloodExpertiseMultiplier = new(() => GetConfigValue("VBloodExpertiseMultiplier")); + public static float VBloodExpertiseMultiplier => _vBloodExpertiseMultiplier.Value; + + static readonly Lazy _expertiseStatChoices = new(() => GetConfigValue("ExpertiseStatChoices")); + public static int ExpertiseStatChoices => _expertiseStatChoices.Value; + + static readonly Lazy _resetExpertiseItem = new(() => GetConfigValue("ResetExpertiseItem")); + public static int ResetExpertiseItem => _resetExpertiseItem.Value; + + static readonly Lazy _resetExpertiseItemQuantity = new(() => GetConfigValue("ResetExpertiseItemQuantity")); + public static int ResetExpertiseItemQuantity => _resetExpertiseItemQuantity.Value; + + static readonly Lazy _maxHealth = new(() => GetConfigValue("MaxHealth")); + public static float MaxHealth => _maxHealth.Value; + + static readonly Lazy _movementSpeed = new(() => GetConfigValue("MovementSpeed")); + public static float MovementSpeed => _movementSpeed.Value; + + static readonly Lazy _primaryAttackSpeed = new(() => GetConfigValue("PrimaryAttackSpeed")); + public static float PrimaryAttackSpeed => _primaryAttackSpeed.Value; + + static readonly Lazy _physicalLifeLeech = new(() => GetConfigValue("PhysicalLifeLeech")); + public static float PhysicalLifeLeech => _physicalLifeLeech.Value; + + static readonly Lazy _spellLifeLeech = new(() => GetConfigValue("SpellLifeLeech")); + public static float SpellLifeLeech => _spellLifeLeech.Value; + + static readonly Lazy _primaryLifeLeech = new(() => GetConfigValue("PrimaryLifeLeech")); + public static float PrimaryLifeLeech => _primaryLifeLeech.Value; + + static readonly Lazy _physicalPower = new(() => GetConfigValue("PhysicalPower")); + public static float PhysicalPower => _physicalPower.Value; + + static readonly Lazy _spellPower = new(() => GetConfigValue("SpellPower")); + public static float SpellPower => _spellPower.Value; + + static readonly Lazy _physicalCritChance = new(() => GetConfigValue("PhysicalCritChance")); + public static float PhysicalCritChance => _physicalCritChance.Value; + + static readonly Lazy _physicalCritDamage = new(() => GetConfigValue("PhysicalCritDamage")); + public static float PhysicalCritDamage => _physicalCritDamage.Value; + + static readonly Lazy _spellCritChance = new(() => GetConfigValue("SpellCritChance")); + public static float SpellCritChance => _spellCritChance.Value; + + static readonly Lazy _spellCritDamage = new(() => GetConfigValue("SpellCritDamage")); + public static float SpellCritDamage => _spellCritDamage.Value; + + static readonly Lazy _bloodSystem = new(() => GetConfigValue("BloodSystem")); + public static bool BloodSystem => _bloodSystem.Value; + + static readonly Lazy _maxLegacyPrestiges = new(() => GetConfigValue("MaxLegacyPrestiges")); + public static int MaxLegacyPrestiges => _maxLegacyPrestiges.Value; + + static readonly Lazy _bloodQualityBonus = new(() => GetConfigValue("BloodQualityBonus")); + public static bool BloodQualityBonus => _bloodQualityBonus.Value; + + static readonly Lazy _prestigeBloodQuality = new(() => GetConfigValue("PrestigeBloodQuality")); + public static float PrestigeBloodQuality => _prestigeBloodQuality.Value; + + static readonly Lazy _maxBloodLevel = new(() => GetConfigValue("MaxBloodLevel")); + public static int MaxBloodLevel => _maxBloodLevel.Value; + + static readonly Lazy _unitLegacyMultiplier = new(() => GetConfigValue("UnitLegacyMultiplier")); + public static float UnitLegacyMultiplier => _unitLegacyMultiplier.Value; + + static readonly Lazy _vBloodLegacyMultiplier = new(() => GetConfigValue("VBloodLegacyMultiplier")); + public static float VBloodLegacyMultiplier => _vBloodLegacyMultiplier.Value; + + static readonly Lazy _legacyStatChoices = new(() => GetConfigValue("LegacyStatChoices")); + public static int LegacyStatChoices => _legacyStatChoices.Value; + + static readonly Lazy _resetLegacyItem = new(() => GetConfigValue("ResetLegacyItem")); + public static int ResetLegacyItem => _resetLegacyItem.Value; + + static readonly Lazy _resetLegacyItemQuantity = new(() => GetConfigValue("ResetLegacyItemQuantity")); + public static int ResetLegacyItemQuantity => _resetLegacyItemQuantity.Value; + + static readonly Lazy _healingReceived = new(() => GetConfigValue("HealingReceived")); + public static float HealingReceived => _healingReceived.Value; + + static readonly Lazy _damageReduction = new(() => GetConfigValue("DamageReduction")); + public static float DamageReduction => _damageReduction.Value; + + static readonly Lazy _physicalResistance = new(() => GetConfigValue("PhysicalResistance")); + public static float PhysicalResistance => _physicalResistance.Value; + + static readonly Lazy _spellResistance = new(() => GetConfigValue("SpellResistance")); + public static float SpellResistance => _spellResistance.Value; + + static readonly Lazy _resourceYield = new(() => GetConfigValue("ResourceYield")); + public static float ResourceYield => _resourceYield.Value; + + static readonly Lazy _ccReduction = new(() => GetConfigValue("CCReduction")); + public static float CCReduction => _ccReduction.Value; + + static readonly Lazy _spellCooldownRecoveryRate = new(() => GetConfigValue("SpellCooldownRecoveryRate")); + public static float SpellCooldownRecoveryRate => _spellCooldownRecoveryRate.Value; + + static readonly Lazy _weaponCooldownRecoveryRate = new(() => GetConfigValue("WeaponCooldownRecoveryRate")); + public static float WeaponCooldownRecoveryRate => _weaponCooldownRecoveryRate.Value; + + static readonly Lazy _ultimateCooldownRecoveryRate = new(() => GetConfigValue("UltimateCooldownRecoveryRate")); + public static float UltimateCooldownRecoveryRate => _ultimateCooldownRecoveryRate.Value; + + static readonly Lazy _minionDamage = new(() => GetConfigValue("MinionDamage")); + public static float MinionDamage => _minionDamage.Value; + + static readonly Lazy _shieldAbsorb = new(() => GetConfigValue("ShieldAbsorb")); + public static float ShieldAbsorb => _shieldAbsorb.Value; + + static readonly Lazy _bloodEfficiency = new(() => GetConfigValue("BloodEfficiency")); + public static float BloodEfficiency => _bloodEfficiency.Value; + + static readonly Lazy _professionSystem = new(() => GetConfigValue("ProfessionSystem")); + public static bool ProfessionSystem => _professionSystem.Value; + + static readonly Lazy _maxProfessionLevel = new(() => GetConfigValue("MaxProfessionLevel")); + public static int MaxProfessionLevel => _maxProfessionLevel.Value; + + static readonly Lazy _professionMultiplier = new(() => GetConfigValue("ProfessionMultiplier")); + public static float ProfessionMultiplier => _professionMultiplier.Value; + + static readonly Lazy _extraRecipes = new(() => GetConfigValue("ExtraRecipes")); + public static bool ExtraRecipes => _extraRecipes.Value; + + static readonly Lazy _familiarSystem = new(() => GetConfigValue("FamiliarSystem")); + public static bool FamiliarSystem => _familiarSystem.Value; + + static readonly Lazy _shareUnlocks = new(() => GetConfigValue("ShareUnlocks")); + public static bool ShareUnlocks => _shareUnlocks.Value; + + static readonly Lazy _familiarCombat = new(() => GetConfigValue("FamiliarCombat")); + public static bool FamiliarCombat => _familiarCombat.Value; + + static readonly Lazy _familiarPvP = new(() => GetConfigValue("FamiliarPvP")); + public static bool FamiliarPvP => _familiarPvP.Value; + + static readonly Lazy _familiarPrestige = new(() => GetConfigValue("FamiliarPrestige")); + public static bool FamiliarPrestige => _familiarPrestige.Value; + + static readonly Lazy _maxFamiliarPrestiges = new(() => GetConfigValue("MaxFamiliarPrestiges")); + public static int MaxFamiliarPrestiges => _maxFamiliarPrestiges.Value; + + static readonly Lazy _familiarPrestigeStatMultiplier = new(() => GetConfigValue("FamiliarPrestigeStatMultiplier")); + public static float FamiliarPrestigeStatMultiplier => _familiarPrestigeStatMultiplier.Value; + + static readonly Lazy _maxFamiliarLevel = new(() => GetConfigValue("MaxFamiliarLevel")); + public static int MaxFamiliarLevel => _maxFamiliarLevel.Value; + + static readonly Lazy _allowVBloods = new(() => GetConfigValue("AllowVBloods")); + public static bool AllowVBloods => _allowVBloods.Value; + + static readonly Lazy _bannedUnits = new(() => GetConfigValue("BannedUnits")); + public static string BannedUnits => _bannedUnits.Value; + + static readonly Lazy _bannedTypes = new(() => GetConfigValue("BannedTypes")); + public static string BannedTypes => _bannedTypes.Value; + + static readonly Lazy _vBloodDamageMultiplier = new(() => GetConfigValue("VBloodDamageMultiplier")); + public static float VBloodDamageMultiplier => _vBloodDamageMultiplier.Value; + + static readonly Lazy _unitFamiliarMultiplier = new(() => GetConfigValue("UnitFamiliarMultiplier")); + public static float UnitFamiliarMultiplier => _unitFamiliarMultiplier.Value; + + static readonly Lazy _vBloodFamiliarMultiplier = new(() => GetConfigValue("VBloodFamiliarMultiplier")); + public static float VBloodFamiliarMultiplier => _vBloodFamiliarMultiplier.Value; + + static readonly Lazy _unitUnlockChance = new(() => GetConfigValue("UnitUnlockChance")); + public static float UnitUnlockChance => _unitUnlockChance.Value; + + static readonly Lazy _vBloodUnlockChance = new(() => GetConfigValue("VBloodUnlockChance")); + public static float VBloodUnlockChance => _vBloodUnlockChance.Value; + + static readonly Lazy _shinyChance = new(() => GetConfigValue("ShinyChance")); + public static float ShinyChance => _shinyChance.Value; + + static readonly Lazy _shinyCostItemPrefab = new(() => GetConfigValue("ShinyCostItemPrefab")); + public static int ShinyCostItemPrefab => _shinyCostItemPrefab.Value; + + static readonly Lazy _shinyCostItemQuantity = new(() => GetConfigValue("ShinyCostItemQuantity")); + public static int ShinyCostItemQuantity => _shinyCostItemQuantity.Value; + + static readonly Lazy _softSynergies = new(() => GetConfigValue("SoftSynergies")); + public static bool SoftSynergies => _softSynergies.Value; + + static readonly Lazy _hardSynergies = new(() => GetConfigValue("HardSynergies")); + public static bool HardSynergies => _hardSynergies.Value; + + static readonly Lazy _classSpellSchoolOnHitEffects = new(() => GetConfigValue("ClassSpellSchoolOnHitEffects")); + public static bool ClassSpellSchoolOnHitEffects => _classSpellSchoolOnHitEffects.Value; + + static readonly Lazy _onHitProcChance = new(() => GetConfigValue("OnHitProcChance")); + public static float OnHitProcChance => _onHitProcChance.Value; + + static readonly Lazy _statSynergyMultiplier = new(() => GetConfigValue("StatSynergyMultiplier")); + public static float StatSynergyMultiplier => _statSynergyMultiplier.Value; + + static readonly Lazy _bloodKnightWeapon = new(() => GetConfigValue("BloodKnightWeapon")); + public static string BloodKnightWeapon => _bloodKnightWeapon.Value; + + static readonly Lazy _bloodKnightBlood = new(() => GetConfigValue("BloodKnightBlood")); + public static string BloodKnightBlood => _bloodKnightBlood.Value; + + static readonly Lazy _demonHunterWeapon = new(() => GetConfigValue("DemonHunterWeapon")); + public static string DemonHunterWeapon => _demonHunterWeapon.Value; + + static readonly Lazy _demonHunterBlood = new(() => GetConfigValue("DemonHunterBlood")); + public static string DemonHunterBlood => _demonHunterBlood.Value; + + static readonly Lazy _vampireLordWeapon = new(() => GetConfigValue("VampireLordWeapon")); + public static string VampireLordWeapon => _vampireLordWeapon.Value; + + static readonly Lazy _vampireLordBlood = new(() => GetConfigValue("VampireLordBlood")); + public static string VampireLordBlood => _vampireLordBlood.Value; + + static readonly Lazy _shadowBladeWeapon = new(() => GetConfigValue("ShadowBladeWeapon")); + public static string ShadowBladeWeapon => _shadowBladeWeapon.Value; + + static readonly Lazy _shadowBladeBlood = new(() => GetConfigValue("ShadowBladeBlood")); + public static string ShadowBladeBlood => _shadowBladeBlood.Value; + + static readonly Lazy _arcaneSorcererWeapon = new(() => GetConfigValue("ArcaneSorcererWeapon")); + public static string ArcaneSorcererWeapon => _arcaneSorcererWeapon.Value; + + static readonly Lazy _arcaneSorcererBlood = new(() => GetConfigValue("ArcaneSorcererBlood")); + public static string ArcaneSorcererBlood => _arcaneSorcererBlood.Value; + + static readonly Lazy _deathMageWeapon = new(() => GetConfigValue("DeathMageWeapon")); + public static string DeathMageWeapon => _deathMageWeapon.Value; + + static readonly Lazy _deathMageBlood = new(() => GetConfigValue("DeathMageBlood")); + public static string DeathMageBlood => _deathMageBlood.Value; + + static readonly Lazy _defaultClassSpell = new(() => GetConfigValue("DefaultClassSpell")); + public static int DefaultClassSpell => _defaultClassSpell.Value; + + static readonly Lazy _bloodKnightBuffs = new(() => GetConfigValue("BloodKnightBuffs")); + public static string BloodKnightBuffs => _bloodKnightBuffs.Value; + + static readonly Lazy _bloodKnightSpells = new(() => GetConfigValue("BloodKnightSpells")); + public static string BloodKnightSpells => _bloodKnightSpells.Value; + + static readonly Lazy _demonHunterBuffs = new(() => GetConfigValue("DemonHunterBuffs")); + public static string DemonHunterBuffs => _demonHunterBuffs.Value; + + static readonly Lazy _demonHunterSpells = new(() => GetConfigValue("DemonHunterSpells")); + public static string DemonHunterSpells => _demonHunterSpells.Value; + + static readonly Lazy _vampireLordBuffs = new(() => GetConfigValue("VampireLordBuffs")); + public static string VampireLordBuffs => _vampireLordBuffs.Value; + + static readonly Lazy _vampireLordSpells = new(() => GetConfigValue("VampireLordSpells")); + public static string VampireLordSpells => _vampireLordSpells.Value; + + static readonly Lazy _shadowBladeBuffs = new(() => GetConfigValue("ShadowBladeBuffs")); + public static string ShadowBladeBuffs => _shadowBladeBuffs.Value; + + static readonly Lazy _shadowBladeSpells = new(() => GetConfigValue("ShadowBladeSpells")); + public static string ShadowBladeSpells => _shadowBladeSpells.Value; + + static readonly Lazy _arcaneSorcererBuffs = new(() => GetConfigValue("ArcaneSorcererBuffs")); + public static string ArcaneSorcererBuffs => _arcaneSorcererBuffs.Value; + + static readonly Lazy _arcaneSorcererSpells = new(() => GetConfigValue("ArcaneSorcererSpells")); + public static string ArcaneSorcererSpells => _arcaneSorcererSpells.Value; + + static readonly Lazy _deathMageBuffs = new(() => GetConfigValue("DeathMageBuffs")); + public static string DeathMageBuffs => _deathMageBuffs.Value; + + static readonly Lazy _deathMageSpells = new(() => GetConfigValue("DeathMageSpells")); + public static string DeathMageSpells => _deathMageSpells.Value; public static class ConfigInitialization { static readonly Regex regex = new(@"^\[(.+)\]$"); + /* public static readonly List DirectoryPaths = [ Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME), // 0 @@ -170,8 +604,27 @@ public static class ConfigInitialization Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Familiars", "FamiliarLeveling"), // 7 Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Familiars", "FamiliarUnlocks") // 8 ]; + */ - static readonly List SectionOrder = + // Lazy initialization for DirectoryPaths + static readonly Lazy> _directoryPaths = new(() => + { + return + [ + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "PlayerLeveling"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Quests"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "WeaponExpertise"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "BloodLegacies"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Professions"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Familiars"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Familiars", "FamiliarLeveling"), + Path.Combine(BepInEx.Paths.ConfigPath, MyPluginInfo.PLUGIN_NAME, "Familiars", "FamiliarUnlocks") + ]; + }); + public static List DirectoryPaths => _directoryPaths.Value; + + public static readonly List SectionOrder = [ "General", "StarterKit", @@ -191,8 +644,7 @@ public class ConfigEntryDefinition(string section, string key, object defaultVal public object DefaultValue { get; } = defaultValue; public string Description { get; } = description; } - - static readonly List ConfigEntries = + public static readonly List ConfigEntries = [ new ConfigEntryDefinition("General", "LanguageLocalization", "English", "The language localization for prefabs displayed to users. English by default. Options: Brazilian, English, French, German, Hungarian, Italian, Japanese, Koreana, Latam, Polish, Russian, SimplifiedChinese, Spanish, TraditionalChinese, Thai, Turkish, Vietnamese"), new ConfigEntryDefinition("General", "ClientCompanion", false, "Enable if using the client companion mod, can configure what's displayed in the client config."), @@ -669,4 +1121,9 @@ static void CleanAndOrganizeConfig(string configFile) } } } + static T GetConfigValue(string key) + { + var entry = ConfigInitialization.ConfigEntries.FirstOrDefault(e => e.Key == key); + return entry == null ? throw new InvalidOperationException($"Config entry for key '{key}' not found.") : (T)entry.DefaultValue; + } } diff --git a/Services/EclipseInterface.cs b/Services/EclipseInterface.cs index 66e1f0a..b1c5abb 100644 --- a/Services/EclipseInterface.cs +++ b/Services/EclipseInterface.cs @@ -22,7 +22,7 @@ public static class VersionHandler // Dictionary to store version handlers public static readonly Dictionary VersionHandlers = new() { - { "1.1.2", new VersionHandler_1_1_2() }, // legacy version, took me too long to realize truly older versions won't send their version to the server :P + { "1.1.2", new VersionHandler_1_1_2() }, // legacy version, took me too long to realize true legacy versions won't send their version to the server :P { "1.2.2", new VersionHandler_1_2_2() } }; diff --git a/Utilities/EntityUtilities.cs b/Utilities/EntityUtilities.cs index 7dac600..d3a5bcd 100644 --- a/Utilities/EntityUtilities.cs +++ b/Utilities/EntityUtilities.cs @@ -84,7 +84,10 @@ public static IEnumerable GetEntitiesEnumerable(EntityQuery entityQuery, else if (entity.TryGetComponent(out PrefabGUID unitPrefab)) { string prefabName = unitPrefab.LookupName(); - if (!FilteredTargets.Any(part => prefabName.Contains(part))) yield return entity; + bool customSpawned = entity.TryGetComponent(out IsMinion isMinion) && isMinion.Value; + + if (customSpawned) continue; + else if (!FilteredTargets.Any(part => prefabName.Contains(part))) yield return entity; } } else if (targetType == 1)