private void SelectCharasById(CharaId matchId) { var selected = 0; var origCharCount = StudioAPI.GetSelectedCharacters().Count(); var origObjCount = StudioAPI.GetSelectedObjects().Count() - origCharCount; foreach (var objectCtrlInfo in EnumerateObjects()) { Logger.DebugLogDebug($"SelectCharasById: {objectCtrlInfo}"); if (objectCtrlInfo is OCIChar ociChar) { if (DoesCharaMatch(matchId, ociChar)) { ociChar.MultiSelectInWorkarea(); selected++; } else { if (ociChar.IsSelectedInWorkarea()) { ociChar.UnselectInWorkarea(); } } } else { objectCtrlInfo.UnselectInWorkarea(); } } Logger.Log(BepInLogLevel.Info | BepInLogLevel.Message, $"characters selected: {selected} ({selected - origCharCount} new selections, {origObjCount} non-characters unselected)"); GeBoAPI.Instance.PlayNotificationSound(NotificationSound.Success); }
public static void Log(string str) { if (UsingLog) { LogInstance?.Log(LogLevel.Message, str); } }
/// <summary> /// When cards are added or removed from the folder set a flag /// </summary> private static void CardEvent(string filePath, CardEventType eventType) { if (filePath.Contains("_autosave")) { return; } //Needs to be locked since dumping a bunch of cards in the folder will trigger this event a whole bunch of times that all run at once rwlock.EnterWriteLock(); try { EventType = eventType; //Start a timer which will be reset every time a card is added/removed for when the user dumps in a whole bunch at once //Once the timer elapses, a flag will be set to do the refresh, which will then happen on the next Update. //If the time is already running, dispose of it so it can be restarted if (CardTimer != null) { CardTimer.Dispose(); } CardTimer = new Timer(1000); CardTimer.Elapsed += (o, ee) => DoRefresh = true; CardTimer.Start(); } catch (Exception ex) { Logger.Log(LogLevel.Error, ex); CardTimer?.Dispose(); } rwlock.ExitWriteLock(); }
private static void MoveToRecycleBin(string path) { if (!File.Exists(path)) { return; } try { var fullPath = Path.GetFullPath(path); if (fullPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && fullPath.StartsWith(_fullUserDataPath, StringComparison.OrdinalIgnoreCase)) { if (!RecycleBinUtil.MoveToRecycleBin(fullPath)) { throw new Exception("Call returned false, check if recycle bin is enabled"); } _logger.Log(LogLevel.Info, $"Moved \"{fullPath}\" to recycle bin before it was removed or overwritten."); } } catch (Exception e) { _logger.Log(LogLevel.Warning, $"Failed to move \"{path}\" to recycle bin, it will be permanently deleted or overwritten.\n{e}"); } }
private void Main() { Logger = base.Logger; //Don't even bother if there's no mods directory if (!Directory.Exists(Path.Combine(Paths.GameRootPath, "mods")) || !Directory.Exists(Paths.PluginPath)) { Logger.Log(LogLevel.Warning, "KK_GUIDMigration was not loaded due to missing mods folder."); return; } //Only do migration if there's a .csv file and it has stuff in it if (!File.Exists(GUIDMigrationFilePath)) { Logger.Log(LogLevel.Error | LogLevel.Message, "KK_GUIDMigration was not loaded due to missing KK_GUIDMigration.csv file."); return; } GenerateMigrationInfo(); if (MigrationInfoList.Count == 0) { Logger.Log(LogLevel.Error | LogLevel.Message, "KK_GUIDMigration was not loaded due to empty KK_GUIDMigration.csv file."); return; } HarmonyWrapper.PatchAll(typeof(Hooks)); }
// Token: 0x06000006 RID: 6 RVA: 0x000020F8 File Offset: 0x000002F8 private static void ExtendedSceneLoadInUpdate() { try { Logger.Log(LogLevel.Info, "Start loading VMDPlay info from scene data."); object obj; if (ExtendedSave.GetSceneExtendedDataById("KKVMDPlayExtSave").data.TryGetValue("xml", out obj)) { if (obj != null && obj is byte[]) { Logger.Log(LogLevel.Info, string.Format("Found VMDPlay info XML data: {0}", ((byte[])obj).Length)); MemoryStream inStream = new MemoryStream((byte[])obj); Console.WriteLine("ExtSave: Loading from PNG."); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(inStream); Console.WriteLine(xmlDocument.ToString()); KKVMDPlayExtSavePlugin.OnLoad(xmlDocument.DocumentElement); } else { Logger.Log(LogLevel.Message, "Data not found."); } } else { Logger.Log(LogLevel.Message, "Data not found."); } } catch (Exception ex) { Logger.Log(LogLevel.Error, string.Format("Failed to load data. {0}", ex.StackTrace)); } }
/// <summary> /// When cards are added or removed from the folder set a flag /// </summary> private static void CardEvent(CardEventType eventType) { //Needs to be locked since dumping a bunch of cards in the folder will trigger this event a whole bunch of times that all run at once rwlock.EnterWriteLock(); try { EventType = eventType; //Start a timer which will be reset every time a card is added/removed for when the user dumps in a whole bunch at once //Once the timer elapses, a flag will be set to do the refresh, which will then happen on the next Update. if (CardTimer == null) { //First file, start timer CardTimer = new Timer(1000); CardTimer.Elapsed += (o, ee) => DoRefresh = true; CardTimer.Start(); } else { //Subsequent file, reset timer CardTimer.Stop(); CardTimer.Start(); } } catch (Exception ex) { Logger.Log(LogLevel.Error, ex); CardTimer?.Dispose(); } rwlock.ExitWriteLock(); }
public void DumpResource(Scene?scene) { if (scene == null) { return; } var scenePath = Path.Combine(ResourceDumpDir, SceneManager.GetActiveScene().name); Directory.CreateDirectory(scenePath); var textures = Resources.FindObjectsOfTypeAll(typeof(Texture2D)); foreach (var texture in textures) { try { var targetFile = Path.Combine(scenePath, texture.name + ".png"); if (File.Exists(targetFile)) { continue; } DumpTexture2D((Texture2D)texture, Path.Combine(scenePath, texture.name + ".png")); Logger.Log(LogLevel.Debug, $"Resource '{texture.name}' dumped."); } catch (Exception ex) { Logger.Log(LogLevel.Error, ex); } } }
private void Awake() { Logger = base.Logger; Hooks.InstallHooks(); AutoResolver.Hooks.InstallHooks(); ResourceRedirector.ResourceRedirector.AssetResolvers.Add(RedirectHook); ResourceRedirector.ResourceRedirector.AssetBundleResolvers.Add(AssetBundleRedirectHook); MissingModWarning = Config.GetSetting("Settings", "Show missing mod warnings", true, new ConfigDescription("Whether missing mod warnings will be displayed on screen. Messages will still be written to the log.")); DebugLogging = Config.GetSetting("Settings", "Debug logging", false, new ConfigDescription("Enable additional logging useful for debugging issues with Sideloader and sideloader mods.\nWarning: Will increase load and save times noticeably and will result in very large log sizes.")); DebugResolveInfoLogging = Config.GetSetting("Settings", "Debug resolve info logging", false, new ConfigDescription("Enable verbose logging for debugging issues with Sideloader and sideloader mods.\nWarning: Will increase game start up time and will result in very large log sizes.")); KeepMissingAccessories = Config.GetSetting("Settings", "Keep missing accessories", false, new ConfigDescription("Missing accessories will be replaced by a default item with color and position information intact when loaded in the character maker.")); AdditionalModsDirectory = Config.GetSetting("General", "Additional mods directory", FindKoiZipmodDir(), new ConfigDescription("Additional directory to load zipmods from.")); if (!Directory.Exists(ModsDirectory)) { Logger.Log(LogLevel.Warning, "Could not find the mods directory: " + ModsDirectory); } if (!AdditionalModsDirectory.Value.IsNullOrWhiteSpace() && !Directory.Exists(AdditionalModsDirectory.Value)) { Logger.Log(LogLevel.Warning, "Could not find the additional mods directory specified in config: " + AdditionalModsDirectory.Value); } LoadModsFromDirectories(ModsDirectory, AdditionalModsDirectory.Value); }
internal static void LoadListInfoAllPostfix(ChaListControl __instance) { if (!Directory.Exists(ListOverrideFolder)) { return; } int counter = 0; Dictionary <ChaListDefine.CategoryNo, Dictionary <int, ListInfoBase> > dictListInfo = Traverse.Create(__instance).Field("dictListInfo").GetValue() as Dictionary <ChaListDefine.CategoryNo, Dictionary <int, ListInfoBase> >; foreach (var fileName in Directory.GetFiles(ListOverrideFolder)) { try { XDocument doc = XDocument.Load(fileName); foreach (var overrideElement in doc.Root.Elements()) { ChaListDefine.CategoryNo categoryNo; if (int.TryParse(overrideElement.Attribute("Category").Value, out int category)) { categoryNo = (ChaListDefine.CategoryNo)category; } else { categoryNo = (ChaListDefine.CategoryNo)Enum.Parse(typeof(ChaListDefine.CategoryNo), overrideElement.Attribute("Category").Value); } ChaListDefine.KeyType keyType; if (int.TryParse(overrideElement.Attribute("KeyType").Value, out int key)) { keyType = (ChaListDefine.KeyType)key; } else { keyType = (ChaListDefine.KeyType)Enum.Parse(typeof(ChaListDefine.KeyType), overrideElement.Attribute("KeyType").Value); } int id = int.Parse(overrideElement.Attribute("ID").Value); string value = overrideElement.Attribute("Value").Value; //Don't allow people to change IDs, that's sure to break everything. if (keyType == ChaListDefine.KeyType.ID) { continue; } dictListInfo[categoryNo][id].dictInfo[(int)keyType] = value; counter++; } } catch (Exception ex) { Logger.Log(LogLevel.Error, $"Failed to load {PluginNameInternal} xml file."); Logger.Log(LogLevel.Error, ex); } } Logger.Log(LogLevel.Debug, $"[{PluginNameInternal}] Loaded {counter} overrides"); }
internal HashSet <string> LoadRequested(PluginScanner?pluginScanner) { _moduleSet = new HashSet <string>(); void AddModuleToSet(IEnumerable <CustomAttributeArgument> arguments) { foreach (var arg in arguments) { foreach (var stringElement in (CustomAttributeArgument[])arg.Value) { _moduleSet.Add((string)stringElement.Value); } } } void CallWhenAssembliesAreScanned() { var moduleTypes = Assembly.GetExecutingAssembly().GetTypes().Where(APISubmoduleFilter).ToList(); foreach (var moduleType in moduleTypes) { R2API.Logger.LogInfo($"Enabling R2API Submodule: {moduleType.Name}"); } var faults = new Dictionary <Type, Exception>(); LoadedModules = new HashSet <string>(); moduleTypes .ForEachTry(t => InvokeStage(t, InitStage.SetHooks, null), faults); moduleTypes.Where(t => !faults.ContainsKey(t)) .ForEachTry(t => InvokeStage(t, InitStage.Load, null), faults); faults.Keys.ForEachTry(t => { _logger?.Log(LogLevel.Error, $"{t.Name} could not be initialized and has been disabled:\n\n{faults[t]}"); InvokeStage(t, InitStage.UnsetHooks, null); }); moduleTypes.Where(t => !faults.ContainsKey(t)) .ForEachTry(t => t.SetFieldValue("_loaded", true)); moduleTypes.Where(t => !faults.ContainsKey(t)) .ForEachTry(t => LoadedModules.Add(t.Name)); } var scanRequest = new PluginScanner.AttributeScanRequest(attributeTypeFullName: typeof(R2APISubmoduleDependency).FullName, attributeTargets: AttributeTargets.Assembly | AttributeTargets.Class, CallWhenAssembliesAreScanned, oneMatchPerAssembly: false, foundOnAssemblyAttributes: (assembly, arguments) => AddModuleToSet(arguments), foundOnAssemblyTypes: (type, arguments) => AddModuleToSet(arguments) ); pluginScanner.AddScanRequest(scanRequest); return(LoadedModules); }
[HarmonyPatch(typeof(CustomBase.CustomSettingSave), nameof(CustomBase.CustomSettingSave.Load))] // maker config private static Exception CatchCrash(MethodBase __originalMethod, Exception __exception) { if (__exception != null) { Logger.Log(LogLevel.Warning | LogLevel.Message, $"Corrupted save file detected in {__originalMethod.DeclaringType?.Name}, some progress might be lost."); Logger.LogWarning(__exception); } return(null); }
internal static void DebugMsg(LogLevel _level, string _meg) { if (_cfgDebugMode.Value) { _logger.Log(_level, _meg); } else { _logger.Log(LogLevel.Debug, _meg); } }
internal static void DebugMsg(LogLevel LogLevel, string LogMsg) { if (CfgDebugMode.Value) { Logger.Log(LogLevel, LogMsg); } else { Logger.Log(LogLevel.Debug, LogMsg); } }
internal static void DebugLog(LogLevel _level, object _msg) { if (_cfgDebugMsg.Value) { _logger.Log(_level, _msg); } else { _logger.Log(LogLevel.Debug, _msg); } }
private void Awake() { Logger = base.Logger; Logger.Log(LogLevel.Debug, "Core pre"); _timeStopsOnJump = Config.Bind("General", "Time Stops On Jump", false, "Insert funny JoJoke™ here. This one gets better with the timescale plugin from the CursedDlls."); _shortcutPrintLayerAndTagsInfo = Config.Bind("Keybinds - Debug", "Print Layer and Tags Info", new KeyboardShortcut(KeyCode.L, KeyCode.LeftShift), "Prints all Layers and Tags."); _shortcutPrintAllStreamingAssetsBundles = Config.Bind("Keybinds - Debug", "Print All StreamingAssets Bundles", new KeyboardShortcut(KeyCode.A, KeyCode.LeftShift), "Prints all prefabs in StreamingAssets, and every ItemSpawnerID."); _shortcutTelePlayerToOrigin = Config.Bind("Keybinds - Player", "Teleport Player to Origin", new KeyboardShortcut(KeyCode.Z), "Teleports the player rig to the origin of the level."); _shortcutTelePlayerToReset = Config.Bind("Keybinds - Player", "Teleport Player to Reset", new KeyboardShortcut(KeyCode.R), "Teleports the player rig to the reset point of the level."); _shortcutTelePlayer2mForward = Config.Bind("Keybinds - Player", "Teleport Player 2m Forward", new KeyboardShortcut(KeyCode.F), "Teleports the player rig 2 meters in whatever direction the player is looking in."); _shortcutSpawnModPanelV2 = Config.Bind("Keybinds - Misc", "Spawn ModPanelV2", new KeyboardShortcut(KeyCode.M, KeyCode.LeftAlt), "Spawns ModPanelV2 in an empty quickbelt slot, or on the floor if none are free."); _shortcutScrambleMaterials = Config.Bind("Keybinds - Misc", "Scramble Materials", new KeyboardShortcut(KeyCode.T, KeyCode.LeftAlt), "WARNING: SLOW AND DUMB\nScrambles all materials in the scene."); _shortcutScrambleMeshes = Config.Bind("Keybinds - Misc", "Scramble Meshes", new KeyboardShortcut(KeyCode.Y, KeyCode.LeftAlt), "WARNING: SLOW AND DUMB\nScrambles all meshes in the scene."); //Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "cecil"); //Environment.SetEnvironmentVariable("MONOMOD_DMD_DUMP", "./mmdump"); Harmony harmony = Harmony.CreateAndPatchAll(typeof(HarmonyPatches)); //this removes any other patches to the wrist menu's update function, //it's kind of terrible but it's the best method I could think to do //based on the types of patches that are made to the wrist menu MethodInfo FVRWristMenuUpdate = AccessTools.Method(typeof(FVRWristMenu), nameof(FVRWristMenu.Update)); if (FVRWristMenuUpdate != null) { Patches wristUpdatePatches = Harmony.GetPatchInfo(FVRWristMenuUpdate); foreach (Patch postfix in wristUpdatePatches.Postfixes) { if (postfix.owner != harmony.Id) { harmony.Unpatch(FVRWristMenuUpdate, HarmonyPatchType.All, postfix.owner); } } } //Environment.SetEnvironmentVariable("MONOMOD_DMD_DUMP", null); Logger.Log(LogLevel.Debug, "Core post"); }
internal static void PushMessageToBepInEx(string namesection, string message, LogLevel level) { message ??= "<null>"; if (!string.IsNullOrEmpty(namesection)) { BepInExLog.Log(level, $"[{namesection}] {message}"); } else { BepInExLog.Log(level, message); } }
private void Awake() { Logger = base.Logger; Logger.Log(LogLevel.Debug, "SmartPalming pre"); _enableSmartPalming = Config.Bind("General", "Enable Smart Palming", true, "Enables smart palming. Smart Palming occurs when you duplicate palmed rounds with a mag/gun in your other hand, where it will only take as many rounds as needed out of the palm."); Harmony.CreateAndPatchAll(typeof(SmartPalmingPlugin)); Logger.Log(LogLevel.Debug, "SmartPalming post"); }
public static void Start() { var preloaderListener = new PreloaderConsoleListener(); Logger.Listeners.Add(preloaderListener); var entrypointAssemblyPath = Path.ChangeExtension(Process.GetCurrentProcess().MainModule.FileName, "dll"); Paths.SetExecutablePath(entrypointAssemblyPath); TypeLoader.SearchDirectories.Add(Path.GetDirectoryName(entrypointAssemblyPath)); Logger.Sources.Add(TraceLogSource.CreateSource()); ChainloaderLogHelper.PrintLogInfo(Log); Log.Log(LogLevel.Info, $"CLR runtime version: {Environment.Version}"); AssemblyBuildInfo executableInfo; using (var entrypointAssembly = AssemblyDefinition.ReadAssembly(entrypointAssemblyPath)) executableInfo = AssemblyBuildInfo.DetermineInfo(entrypointAssembly); Log.LogInfo($"Game executable build architecture: {executableInfo}"); Log.Log(LogLevel.Message, "Preloader started"); using (var assemblyPatcher = new AssemblyPatcher()) { assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.PatchDefinitions.Count} patcher definition(s) loaded"); assemblyPatcher.LoadAssemblyDirectories(new[] { Paths.GameRootPath }, new[] { "dll", "exe" }); Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.AvailableAssemblies.Count} assemblies discovered"); assemblyPatcher.PatchAndLoad(); } Log.LogMessage("Preloader finished"); Logger.Listeners.Remove(preloaderListener); var chainloader = new NetChainloader(); chainloader.Initialize(); chainloader.Execute(); }
public void Awake() { logger = Logger; logger.Log(LogLevel.All, "DeathMatch V1.0 Initialized."); // Disable mobs Run.onRunStartGlobal += (r) => { RoR2.Console.instance.FindConVar("director_combat_disable").SetString("1"); logger.Log(LogLevel.Info, "Game started... Initializing TDM."); TeamManager.instance.OnStart(); }; Run.onRunDestroyGlobal += (r) => { logger.Log(LogLevel.Info, "Game ended... Destorying TDM session."); TeamManager.instance.OnDestroy(); }; }
private void GenerateTrampolineInner(out int trampolineLength, out int jmpLength) { if (TrampolinePtr != IntPtr.Zero) { trampolineLength = TrampolineSize; jmpLength = TrampolineJmpSize; return; } var instructionBuffer = new byte[32]; Marshal.Copy(OriginalFunctionPtr, instructionBuffer, 0, 32); var trampolineAlloc = PageAllocator.Instance.Allocate(OriginalFunctionPtr); logger.Log(LogLevel.Debug, $"Original: {OriginalFunctionPtr.ToInt64():X}, Trampoline: {trampolineAlloc:X}, diff: {Math.Abs(OriginalFunctionPtr.ToInt64() - trampolineAlloc):X}; is within +-1GB range: {PageAllocator.IsInRelJmpRange(OriginalFunctionPtr, trampolineAlloc)}"); DetourHelper.Native.MakeWritable(trampolineAlloc, PageAllocator.PAGE_SIZE); var arch = IntPtr.Size == 8 ? Architecture.X64 : Architecture.X86; DetourGenerator.CreateTrampolineFromFunction(instructionBuffer, OriginalFunctionPtr, trampolineAlloc, DetourGenerator.GetDetourLength(arch), arch, out trampolineLength, out jmpLength); DetourHelper.Native.MakeExecutable(trampolineAlloc, PageAllocator.PAGE_SIZE); TrampolinePtr = trampolineAlloc; TrampolineSize = trampolineLength; TrampolineJmpSize = jmpLength; }
private void Update() { if (SpawnMobKey.Value.IsDown()) { var mapNo = GetCurrentMapNo(); var player = Game.Instance.Player.transform; var position = player.position; var rotation = player.rotation; if (Input.GetKey(KeyCode.LeftShift)) { MobManager.RemoveClosestMob(mapNo, position); } else { MobManager.SpawnMob(position, rotation, true, mapNo); MobManager.AddMobPosition(mapNo, position, rotation); } } else if (SaveMobPositionDataKey.Value.IsDown()) { try { MobManager.SaveCsv(); } catch (Exception ex) { Logger.Log(LogLevel.Error | LogLevel.Message, $"Failed to save .csv file with mob data: {ex.Message}"); } } }
/// <summary> /// Exports all currently loaded characters. Probably wont export characters that have not been loaded yet, like characters in a different classroom. /// </summary> public void ExportCharacters() { int counter = 0; var charas = FindObjectsOfType <ChaControl>(); string filenamePrefix = Path.Combine(ExportPath, $"_CharacterExport_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}"); bool openedFile = false; for (; counter < charas.Length; counter++) { string sex = charas[counter].chaFile.parameter.sex == 0 ? "Male" : "Female"; string filename = $"{filenamePrefix}_{counter:00}_{sex}.png"; #if AI || EC Traverse.Create(charas[counter].chaFile).Method("SaveFile", filename, 0).GetValue(); #elif KK Traverse.Create(charas[counter].chaFile).Method("SaveFile", filename).GetValue(); #else Logger.LogError($"Exporting not yet implemented"); #endif if (!openedFile) { if (OpenFolderAfterExport.Value) { CC.OpenFileInExplorer(filename); } openedFile = true; } } string s = counter == 1 ? "" : "s"; Logger.Log(BepInEx.Logging.LogLevel.Info | BepInEx.Logging.LogLevel.Message, $"Exported {counter} character{s}."); }
public static byte[] MessagePackSerialize <T>(T obj) { try { return(MessagePackSerializer.Serialize(obj, StandardResolver.Instance)); } catch (FormatterNotRegisteredException) { return(MessagePackSerializer.Serialize(obj, ContractlessStandardResolver.Instance)); } catch (InvalidOperationException) { Logger.Log(LogLevel.Warning, "Only primitive types are supported. Serialize your data first."); throw; } }
private IEnumerator Start() { Logger = base.Logger; _showCheatWindow = Config.Bind("Hotkeys", "Toggle cheat window", new KeyboardShortcut(KeyCode.Pause)); _noclip = Config.Bind("Hotkeys", "Toggle player noclip", KeyboardShortcut.Empty); BuildAnywhere = Config.Bind("Cheats", "Allow building anywhere", false); BuildAnywhere.SettingChanged += (sender, args) => BuildAnywhereHooks.Enabled = BuildAnywhere.Value; BuildAnywhereHooks.Enabled = BuildAnywhere.Value; BuildOverlap = Config.Bind("Cheats", "Allow building overlap", false); BuildOverlap.SettingChanged += (sender, args) => BuildOverlapHooks.Enabled = BuildOverlap.Value; BuildOverlapHooks.Enabled = BuildOverlap.Value; // Wait for runtime editor to init yield return(null); _runtimeUnityEditorCore = RuntimeUnityEditor5.Instance; if (_runtimeUnityEditorCore == null) { Logger.Log(BepInEx.Logging.LogLevel.Error, "Could not get the instance of RuntimeUnityEditorCore, aborting"); enabled = false; yield break; } // Disable the default hotkey since we'll be controlling the show state manually _runtimeUnityEditorCore.ShowHotkey = KeyCode.None; }
private IEnumerator Start() { Logger = base.Logger; _showCheatWindow = Config.Bind("Hotkeys", "Toggle cheat window", new KeyboardShortcut(KeyCode.Pause)); _noclip = Config.Bind("Hotkeys", "Toggle player noclip", KeyboardShortcut.Empty); UnlockAllPositions = Config.Bind("Cheats", "Unlock all H positions", false, "Reload the H scene to see changes."); UnlockAllPositions.SettingChanged += (sender, args) => UnlockPositionsHooks.Enabled = UnlockAllPositions.Value; UnlockPositionsHooks.Enabled = UnlockAllPositions.Value; UnlockAllPositionsIndiscriminately = Config.Bind("Cheats", "Unlock invalid H positions as well", false, "This will unlock all positions even if they should not be possible.\nWARNING: Can result in bugs and even game crashes in some cases.\nReload the H scene to see changes."); UnlockAllPositionsIndiscriminately.SettingChanged += (sender, args) => UnlockPositionsHooks.UnlockAll = UnlockAllPositionsIndiscriminately.Value; UnlockPositionsHooks.UnlockAll = UnlockAllPositionsIndiscriminately.Value; // Wait for runtime editor to init yield return(null); _runtimeUnityEditorCore = RuntimeUnityEditor5.Instance; if (_runtimeUnityEditorCore == null) { Logger.Log(LogLevel.Error | LogLevel.Message, "Failed to get RuntimeUnityEditor! Make sure you don't have multiple versions of it installed!"); enabled = false; yield break; } // Disable the default hotkey since we'll be controlling the show state manually _runtimeUnityEditorCore.ShowHotkey = KeyCode.None; }
public HCSLoader() { Logger = base.Logger; var harmonyInstance = HarmonyWrapper.PatchAll(); harmonyInstance.Patch(AccessTools.Constructor(typeof(Game), Type.EmptyTypes), postfix: new HarmonyMethod(typeof(HCSLoader), nameof(LoadGirlsHook))); string modsDirectory = Path.Combine(Paths.GameRootPath, "Mods"); if (!Directory.Exists(modsDirectory)) { Directory.CreateDirectory(modsDirectory); } foreach (var subdir in Directory.GetDirectories(modsDirectory, "*", SearchOption.TopDirectoryOnly)) { if (CharacterAdditionMod.TryLoad(subdir, out var addMod)) { CharacterAdditions.Add(addMod); } else if (CharacterModificationMod.TryLoad(subdir, out var editMod)) { CharacterModifications.Add(editMod); } else { Logger.LogWarning($"Unknown mod type for folder '{Path.GetFileName(subdir)}', skipping"); } } Logger.Log(LogLevel.Message, $"Found {CharacterAdditions.Count + CharacterModifications.Count} mods."); }
public MakerOptimizations() { Logger = base.Logger; var stilettoInstalled = BepInEx.Bootstrap.Chainloader.PluginInfos.Values.Any(x => x.Metadata.GUID == "com.essu.stiletto"); DisableIKCalc = Config.Bind(Utilities.ConfigSectionTweaks, "Disable IK in maker", !stilettoInstalled, "This setting prevents the character's limbs from being readjusted to match body proportions. It can fix weirdly bent limbs on characters that use ABMX sliders, but will break Stiletto if it's installed.\nWarning: This setting will get reset to false if Stiletto is installed to avoid issues!\nChanges take effect after game restart."); DisableCharaName = Config.Bind(Utilities.ConfigSectionTweaks, "Disable character name box in maker", true, "Hides the name box in the bottom right part of the maker, giving you a clearer look at the character."); DisableHiddenTabs = Config.Bind(Utilities.ConfigSectionTweaks, "Disable hidden tabs in maker", true, "Major performance improvement in chara maker.\nRecommended to be used together with list virtualization, otherwise switching between tabs becomes slower.\nChanges take effect after maker restart."); ManageCursor = Config.Bind(Utilities.ConfigSectionTweaks, "Manage cursor in maker", true, "Lock and hide the cursor when moving the camera in maker."); var itemListsCat = "Maker item lists"; DisableNewAnimation = Config.Bind(itemListsCat, "Disable NEW indicator animation", true, "Performance improvement in maker if there are many new items.\nChanges take effect after maker restart."); DisableNewIndicator = Config.Bind(itemListsCat, "Disable NEW indicator", false, "Turn off the New! mark on items in character maker that weren't used yet.\nChanges take effect after maker restart."); ListWidth = Config.Bind(itemListsCat, "Width of maker item lists", 3, new ConfigDescription("How many items fit horizontally in a single row of the item lists in character maker.\n Changes require a restart of character maker.", new AcceptableValueRange <int>(3, 8))); var virtualize = Config.Bind(itemListsCat, "Virtualize maker lists", true, "Major load time reduction and performance improvement in character maker. Eliminates lag when switching tabs.\nCan cause some compatibility issues with other plugins.\nChanges take effect after game restart."); ScrollListsToSelection = Config.Bind(itemListsCat, "Scroll to selected items automatically", true, "Automatically scroll maker lists to show their selected items in view (usually after loading a character card).\nRequires 'Virtualize maker lists' to be enabled."); if (virtualize.Value) { VirtualizeMakerLists.InstallHooks(); } if (DisableIKCalc.Value && BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey("com.essu.stiletto")) { Logger.Log(LogLevel.Warning, "Stiletto is installed but Disable maker IK is enabled! Heels will not work properly in character maker until this setting is turned off"); } }
public static void Debug(object data, LogLevel logLevel, bool isActive) { if (isActive) { Logger.Log(logLevel, data); } }
void Update() { if (Input.GetMouseButtonDown(1)) { var mousePos = Input.mousePosition; Logger.Log(LogLevel.Info, mousePos); } }