private static void OnCopyAcs(CvsAccessoryCopy instance) { if (AccessoriesCopied == null) { return; } try { var selected = instance.GetComponentsInChildren <UnityEngine.UI.Toggle>() .Where(x => x.isOn) .Select(x => x.transform.parent.name) .Select(n => int.Parse(n.Substring("kind".Length))) .ToList(); var dropdowns = Traverse.Create(instance).Field("ddCoordeType").GetValue <TMP_Dropdown[]>(); var args = new AccessoryCopyEventArgs(selected, (ChaFileDefine.CoordinateType)dropdowns[1].value, (ChaFileDefine.CoordinateType)dropdowns[0].value); AccessoriesCopied(instance, args); } catch (Exception ex) { KoikatuAPI.Log(LogLevel.Error, "Crash in AccessoriesCopied event: " + ex); } }
private void LateUpdate() { if (NeedsFullRefresh) { OnReload(KoikatuAPI.GetCurrentGameMode(), true); NeedsFullRefresh = false; return; } if (_baselineKnown == true) { if (NeedsBaselineUpdate) { UpdateBaseline(); } foreach (var modifier in Modifiers) { var additionalModifiers = _additionalBoneEffects .Select(x => x.GetEffect(modifier.BoneName, this, CurrentCoordinate.Value)) .Where(x => x != null) .ToList(); modifier.Apply(CurrentCoordinate.Value, additionalModifiers, _isDuringHScene); } } else if (_baselineKnown == false) { _baselineKnown = null; CollectBaseline(); } NeedsBaselineUpdate = false; }
public void UpdateHover() { if (KoikatuAPI.GetCurrentGameMode() != GameMode.Studio) { if (!isShoeActive || currentConfig == null || !GroundAnim) { DisableHover(); } else { EnableHover(); } } else { if (!isShoeActive || currentConfig == null) { DisableHover(); } else { EnableHover(); } } }
/// <summary> /// Save your custom data to the character card under the ID you specified when registering this controller. /// This should be used inside the <see cref="OnCardBeingSaved"/> event. /// Consider using one of the other "Get___ExtData" and "Set___ExtData" methods instead since they are more reliable and handle copying and transferring outfits and they conform to built in maker load toggles. /// </summary> /// <param name="data">Your custom data to be written to the character card. Can be null to remove the data.</param> public void SetExtendedData(PluginData data) { if (ExtendedDataId == null) { throw new ArgumentException(nameof(ExtendedDataId)); } ExtendedSave.SetExtendedDataById(ChaFileControl, ExtendedDataId, data); #if KK //todo || KKS // Needed for propagating changes back to the original charFile since they don't get copied back. var heroine = ChaControl.GetHeroine(); if (heroine != null) { ExtendedSave.SetExtendedDataById(heroine.charFile, ExtendedDataId, data); if (ChaControl != heroine.chaCtrl) { ExtendedSave.SetExtendedDataById(heroine.chaCtrl.chaFile, ExtendedDataId, data); // Update other instance to reflect the new ext data var other = heroine.chaCtrl.GetComponent(GetType()) as CharaCustomFunctionController; if (other != null) { other.OnReloadInternal(KoikatuAPI.GetCurrentGameMode()); } } var npc = heroine.GetNPC(); if (npc != null && npc.chaCtrl != null && npc.chaCtrl != ChaControl) { ExtendedSave.SetExtendedDataById(npc.chaCtrl.chaFile, ExtendedDataId, data); // Update other instance to reflect the new ext data var other = npc.chaCtrl.GetComponent(GetType()) as CharaCustomFunctionController; if (other != null) { other.OnReloadInternal(KoikatuAPI.GetCurrentGameMode()); } } } var player = ChaControl.GetPlayer(); if (player != null) { ExtendedSave.SetExtendedDataById(player.charFile, ExtendedDataId, data); if (ChaControl != player.chaCtrl) { ExtendedSave.SetExtendedDataById(player.chaCtrl.chaFile, ExtendedDataId, data); // Update other instance to reflect the new ext data var other = player.chaCtrl.GetComponent(GetType()) as CharaCustomFunctionController; if (other != null) { other.OnReloadInternal(KoikatuAPI.GetCurrentGameMode()); } } } #endif }
public static Transform AddNewSubCategory(UI_ToggleGroupCtrl mainCategory, MakerCategory subCategory) { KoikatuAPI.Log(LogLevel.Debug, $"[MakerAPI] Adding custom subcategory {subCategory.SubCategoryName} to {mainCategory.transform.name}"); var tr = Object.Instantiate(SubCategoryCopy.gameObject, mainCategory.transform, true).transform; tr.name = subCategory.SubCategoryName; var trTop = tr.Find("CustomSubcategoryTop"); trTop.name = subCategory.SubCategoryName + "Top"; foreach (Transform oldObj in trTop.transform) { Object.Destroy(oldObj.gameObject); } tr.GetComponentInChildren <TextMeshProUGUI>().text = GetSubcategoryDisplayName(subCategory); var tgl = tr.GetComponent <Toggle>(); tgl.group = mainCategory.transform.GetComponent <ToggleGroup>(); var cgroup = trTop.GetComponent <CanvasGroup>(); mainCategory.items = mainCategory.items.AddToArray(new UI_ToggleGroupCtrl.ItemInfo { tglItem = tgl, cgItem = cgroup }); KoikatuAPI.Instance.StartCoroutine(FinishInit(trTop)); tr.gameObject.SetActive(true); return(tr); }
/// <summary> /// Get extended data for character's face (face sliders, eye settings). /// Do not store this data because it might change without notice, for example when clothing is copied. Always call Get at the point where you need the data, not earlier. /// If you change any of the data, remember to call the corresponding Set method or the change might not be saved. /// This data is saved alongside game data, which means it is automatically copied and moved as necessary. /// If no extended data of this plugin was set yet, this method will return null. /// In maker, you can update controls that use this data in the <see cref="MakerAPI.ReloadCustomInterface"/> event. /// </summary> public PluginData GetFaceExtData() { KoikatuAPI.Assert(ChaFileControl.custom != null, "ChaFileControl.custom != null"); KoikatuAPI.Assert(ChaFileControl.custom.face != null, "ChaFileControl.custom.face != null"); ChaFileControl.custom.face.TryGetExtendedDataById(ExtendedDataId, out var data); return(data); }
private static void CreateCustomControlsInSubCategory(Transform subCategoryTransform, ICollection <BaseGuiEntry> entriesToAdd) { if (entriesToAdd.Count == 0) { return; } var contentParent = FindSubcategoryContentParent(subCategoryTransform); BaseUnityPlugin lastOwner = contentParent.childCount > 1 ? KoikatuAPI.Instance : null; foreach (var customControl in entriesToAdd) { if (lastOwner != customControl.Owner && lastOwner != null) { new MakerSeparator(new MakerCategory(null, null), KoikatuAPI.Instance).CreateControl(contentParent); } customControl.CreateControl(contentParent); lastOwner = customControl.Owner; } var category = entriesToAdd.First().Category; KoikatuAPI.Log(LogLevel.Debug, $"[MakerAPI] Added {entriesToAdd.Count()} custom controls " + $"to {category.CategoryName}/{category.SubCategoryName}"); }
private static void OnMakerBaseLoaded() { if (MakerBaseLoaded != null) { var args = new RegisterCustomControlsEvent(); foreach (var handler in MakerBaseLoaded.GetInvocationList()) { try { ((EventHandler <RegisterCustomControlsEvent>)handler).Invoke(KoikatuAPI.Instance, args); } catch (Exception e) { KoikatuAPI.Log(LogLevel.Error, e); } } } DebugControls(); foreach (var baseGuiEntry in _guiEntries) { baseGuiEntry.Initialize(); } }
internal void UpdateCurrentCardTranslationMode() { var origMode = CurrentCardLoadTranslationMode; if (StudioAPI.InsideStudio) { CurrentCardLoadTranslationMode = StudioTranslateCardNameOnLoad?.Value ?? CardLoadTranslationMode.Disabled; CurrentGameMode = GameMode.Studio; } else if (MakerAPI.InsideMaker) { CurrentCardLoadTranslationMode = MakerTranslateCardNameOnLoad?.Value ?? CardLoadTranslationMode.Disabled; CurrentGameMode = GameMode.Maker; } else { CurrentGameMode = KoikatuAPI.GetCurrentGameMode(); if (CurrentGameMode == GameMode.MainGame) { CurrentCardLoadTranslationMode = GameTranslateCardNameOnLoad?.Value ?? CardLoadTranslationMode.Disabled; } else { CurrentCardLoadTranslationMode = CardLoadTranslationMode.Disabled; } } Logger.LogDebug($"UpdateCurrentCardTranslationMode: {origMode} => {CurrentCardLoadTranslationMode}"); }
private void LateUpdate() { if (NeedsFullRefresh) { OnReload(KoikatuAPI.GetCurrentGameMode(), true); NeedsFullRefresh = false; return; } if (_baselineKnown == true) { if (NeedsBaselineUpdate) { UpdateBaseline(); } foreach (var modifier in Modifiers) { modifier.Apply(CurrentCoordinate.Value); } } else if (_baselineKnown == false) { _baselineKnown = null; StartCoroutine(CollectBaselineCo()); } NeedsBaselineUpdate = false; }
private void Start() { EnableBld = new ConfigWrapper <bool>(nameof(EnableBld), this, true); EnableBldAlways = new ConfigWrapper <bool>(nameof(EnableBldAlways), this, false); EnableCum = new ConfigWrapper <bool>(nameof(EnableCum), this, true); EnableSwt = new ConfigWrapper <bool>(nameof(EnableSwt), this, true); EnableTear = new ConfigWrapper <bool>(nameof(EnableTear), this, true); EnableDrl = new ConfigWrapper <bool>(nameof(EnableDrl), this, true); EnablePersistance = new ConfigWrapper <bool>(nameof(EnablePersistance), this, true); Hooks.InstallHook(); CharacterApi.RegisterExtraBehaviour <SkinEffectsController>(GUID); GameAPI.RegisterExtraBehaviour <SkinEffectGameController>(GUID); SkinEffectsGui.Init(this); if (KoikatuAPI.GetCurrentGameMode() != GameMode.Studio) { SceneManager.sceneLoaded += (arg0, mode) => { // Preload effects for H scene in case they didn't get loaded yet to prevent freeze on first effect appearing if (arg0.name == "H") { TextureLoader.InitializeTextures(); } }; } }
/// <summary> /// Needs to run before UI_ToggleGroupCtrl.Start of the category runs, or it won't get added properly /// </summary> private static void AddMissingSubCategories(UI_ToggleGroupCtrl mainCategory) { var categoryTransfrom = mainCategory.transform; // Can break stuff, 06_SystemTop might be fine but needs testing if (categoryTransfrom.name == "04_AccessoryTop" || categoryTransfrom.name == "06_SystemTop") { return; } // Sorting breaks hair selector layout, too lazy to fix var noSorting = categoryTransfrom.name == "02_HairTop"; var transformsToSort = new List <UniRx.Tuple <Transform, int> >(); foreach (var category in _categories) { if (categoryTransfrom.name != category.CategoryName) { continue; } var categorySubTransform = categoryTransfrom.Find(category.SubCategoryName) ?? SubCategoryCreator.AddNewSubCategory(mainCategory, category); transformsToSort.Add(new UniRx.Tuple <Transform, int>(categorySubTransform, category.Position)); } if (noSorting) { return; } foreach (Transform subTransform in categoryTransfrom) { if (transformsToSort.Any(x => x.Item1 == subTransform)) { continue; } var builtInCategory = MakerConstants.GetBuiltInCategory(categoryTransfrom.name, subTransform.name); if (builtInCategory != null) { transformsToSort.Add(new UniRx.Tuple <Transform, int>(subTransform, builtInCategory.Position)); } else { KoikatuAPI.Log(LogLevel.Warning, $"[MakerAPI] Missing MakerCategory for existing transfrom {categoryTransfrom.name} / {subTransform.name}"); } } var index = 0; foreach (var tuple in transformsToSort.OrderBy(x => x.Item2)) { tuple.Item1.SetSiblingIndex(index++); } KoikatuAPI.Instance.StartCoroutine(FixCategoryContentOffsets(mainCategory)); }
internal static void Init() { DetectMoreAccessories(); HarmonyPatcher.PatchAll(typeof(Hooks)); MakerAPI.InsideMakerChanged += MakerAPI_InsideMakerChanged; MakerAPI.MakerFinishedLoading += (sender, args) => OnSelectedMakerSlotChanged(sender, 0); if (MoreAccessoriesInstalled) { var getAccCmpM = AccessTools.Method(_moreAccessoriesType, "GetChaAccessoryComponent"); _getChaAccessoryCmp = (control, componentIndex) => (ChaAccessoryComponent)getAccCmpM.Invoke(_moreAccessoriesInstance, new object[] { control, componentIndex }); var getAccCmpIndexM = AccessTools.Method(_moreAccessoriesType, "GetChaAccessoryComponentIndex"); _getChaAccessoryCmpIndex = (control, component) => (int)getAccCmpIndexM.Invoke(_moreAccessoriesInstance, new object[] { control, component }); } else { _getChaAccessoryCmp = (control, i) => control.cusAcsCmp[i]; _getChaAccessoryCmpIndex = (control, component) => Array.IndexOf(control.cusAcsCmp, component); } if (KoikatuAPI.EnableDebugLogging) { SelectedMakerAccSlotChanged += (sender, args) => KoikatuAPI.Log(LogLevel.Message, $"SelectedMakerAccSlotChanged - id: {args.SlotIndex}, cvs: {args.CvsAccessory.transform.name}, component: {args.AccessoryComponent?.name ?? "null"}"); #if KK AccessoriesCopied += (sender, args) => KoikatuAPI.Log(LogLevel.Message, $"AccessoriesCopied - ids: {string.Join(", ", args.CopiedSlotIndexes.Select(x => x.ToString()).ToArray())}, src:{args.CopySource}, dst:{args.CopyDestination}"); #endif AccessoryTransferred += (sender, args) => KoikatuAPI.Log(LogLevel.Message, $"AccessoryTransferred - srcId:{args.SourceSlotIndex}, dstId:{args.DestinationSlotIndex}"); } }
private void LateUpdate() { if (NeedsFullRefresh) { OnReload(KoikatuAPI.GetCurrentGameMode(), true); NeedsFullRefresh = false; return; } if (_baselineKnown == true) { if (NeedsBaselineUpdate) { UpdateBaseline(); } ApplyEffects(); } else if (_baselineKnown == false) { _baselineKnown = null; CollectBaseline(); } NeedsBaselineUpdate = false; }
public static bool GetFileNamePatch(ref string __result, string path) { // Should only work during main game var gmode = KoikatuAPI.GetCurrentGameMode(); if (gmode != GameMode.MainGame && gmode != GameMode.Maker) { return(true); } try { if (File.Exists(UserData.Path + "chara/female/" + path)) { __result = path; return(false); } var lookedAtPath = new Uri(path); var basePathForChara = new Uri(UserData.Path + "chara/female/"); if (basePathForChara.IsBaseOf(lookedAtPath)) { __result = MainGameFolders.GetRelativePath(path, UserData.Path + "chara/female/"); return(false); } return(true); } catch { return(true); } }
public static bool ConvertCharaFilePath(ref ChaFileControl __instance, ref string __result, string path) { // Should only work during main game var gmode = KoikatuAPI.GetCurrentGameMode(); if (gmode != GameMode.MainGame && gmode != GameMode.Maker) { return(true); } if (path == null) { return(true); } if (!path.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) { path += ".png"; } if (File.Exists(path)) { __result = path; return(false); } if (File.Exists(UserData.Path + "chara/female/" + path)) { __result = UserData.Path + "chara/female/" + path; return(false); } return(true); }
private static void DetectMoreAccessories() { try { _moreAccessoriesType = Type.GetType("MoreAccessoriesKOI.MoreAccessories, MoreAccessories, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); if (_moreAccessoriesType != null) { _moreAccessoriesInstance = Object.FindObjectOfType(_moreAccessoriesType); var slotAddEvent = _moreAccessoriesType.GetEvent("onCharaMakerSlotAdded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (slotAddEvent != null) { slotAddEvent.AddEventHandler(_moreAccessoriesInstance, new Action <int, Transform>((i, transform) => OnMakerAccSlotAdded(_moreAccessoriesInstance, i, transform))); } else { _moreAccessoriesType = null; KoikatuAPI.Log(LogLevel.Message | LogLevel.Error, "[KKAPI] WARNING: Your MoreAccesories is outdated! Some features won't work correctly until you update to the latest version."); } } } catch (Exception e) { _moreAccessoriesType = null; KoikatuAPI.Log(LogLevel.Warning, "Failed to detect MoreAccessories!"); KoikatuAPI.Log(LogLevel.Debug, e); } }
public bool Install(Harmony instance, ConfigFile config) { if (KoikatuAPI.IsVR()) { return(false); } _statDecay = config.Bind(KoikatuGameplayMod.ConfCatMainGame, "Player stats slowly decay overnight", true, "Player's stats slowly decrease every day to keep the training points relevant."); _changeLewdDaily = config.Bind(KoikatuGameplayMod.ConfCatMainGame, "Change lewdness overnight", false, "H bar of all heroines either increases or decreases overnight depending on their status."); SceneManager.sceneLoaded += (arg0, mode) => { if (arg0.name != "MyRoom" || Singleton <Scene> .Instance.LoadSceneName == "H") { _inNightMenu = false; } else { if (!_inNightMenu && !_firstNightMenu) { try { OnNightStarted(); } catch (Exception ex) { UnityEngine.Debug.LogException(ex); } } _inNightMenu = true; _firstNightMenu = false; } }; return(true); }
private static void CreateCustomControls() { // Craete controls in tabs foreach (Transform categoryTransfrom in GameObject.Find("CvsMenuTree").transform) { CreateCustomControlsInCategory(categoryTransfrom); } // Create sidebar controls if (_sidebarEntries.Any()) { var sidebarTop = GameObject.Find("CustomScene/CustomRoot/FrontUIGroup/CvsDraw/Top").transform; var sep = new SidebarSeparator(KoikatuAPI.Instance); sep.CreateControl(sidebarTop); foreach (var sidebarEntry in _sidebarEntries) { sidebarEntry.CreateControl(sidebarTop); } KoikatuAPI.Log(LogLevel.Debug, $"[MakerAPI] Added {_sidebarEntries.Count} custom controls " + "to Control Panel sidebar"); } if (_accessoryWindowEntries.Any()) { CreateCustomAccessoryWindowControls(); } }
private static void OnSelectedMakerSlotChanged(object source, int newSlotIndex) { if (newSlotIndex == SelectedMakerAccSlot) { return; } SelectedMakerAccSlot = newSlotIndex; if (KoikatuAPI.EnableDebugLogging) { KoikatuAPI.Log(LogLevel.Message, "SelectedMakerSlotChanged - slot:" + newSlotIndex); } if (SelectedMakerAccSlotChanged == null) { return; } try { SelectedMakerAccSlotChanged(source, new AccessorySlotEventArgs(newSlotIndex)); } catch (Exception ex) { KoikatuAPI.Log(LogLevel.Error, "Subscription to SelectedMakerSlot crashed: " + ex); } }
private ChaFile GetExtDataTargetChaFile(bool setDirty) { #if KK || KKS // In main game store ext data for the character inside of the main chaFile object (the one that gets saved to game saves) instead of its copies. // This allows saving ext data inside talk scenes and H scenes without losing it after exiting to main map. if (KoikatuAPI.GetCurrentGameMode() == GameMode.MainGame) { var heroine = ChaControl.GetHeroine(); if (heroine != null) { if (setDirty) { CharacterApi.Hooks.SetDirty(heroine, true); } return(heroine.charFile); } var player = ChaControl.GetPlayer(); if (player != null) { if (setDirty) { CharacterApi.Hooks.SetDirty(player, true); } return(player.charFile); } } #endif return(ChaFileControl); }
/// <summary> /// Warning: When overriding make sure to call the base method at the end of your logic! /// </summary> protected virtual void OnEnable() { // Order is Awake - OnEnable - Start, so need to check if we started yet if (Started) { OnReloadInternal(KoikatuAPI.GetCurrentGameMode()); } }
private void Start() { KoikatuAPI.CheckRequiredPlugin(this, KoikatuAPI.GUID, new Version(KoikatuAPI.VersionConst)); MakerAPI.RegisterCustomSubCategories += OnRegisterCustomSubCategories; MakerAPI.MakerBaseLoaded += OnEarlyMakerFinishedLoading; MakerAPI.MakerExiting += OnMakerExiting; }
/// <summary> /// Get extended data for character's parameters (personality, preferences, traits). /// Do not store this data because it might change without notice, for example when clothing is copied. Always call Get at the point where you need the data, not earlier. /// If you change any of the data, remember to call the corresponding Set method or the change might not be saved. /// This data is saved alongside game data, which means it is automatically copied and moved as necessary. /// If no extended data of this plugin was set yet, this method will return null. /// In maker, you can update controls that use this data in the <see cref="MakerAPI.ReloadCustomInterface"/> event. /// </summary> public PluginData GetParameterExtData() { var chafile = GetExtDataTargetChaFile(false); KoikatuAPI.Assert(chafile.parameter != null, "chafile.parameter != null"); chafile.parameter.TryGetExtendedDataById(ExtendedDataId, out var data); return(data); }
/// <summary> /// Get extended data for character's face (face sliders, eye settings). /// Do not store this data because it might change without notice, for example when clothing is copied. Always call Get at the point where you need the data, not earlier. /// If you change any of the data, remember to call the corresponding Set method or the change might not be saved. /// This data is saved alongside game data, which means it is automatically copied and moved as necessary. /// If no extended data of this plugin was set yet, this method will return null. /// In maker, you can update controls that use this data in the <see cref="MakerAPI.ReloadCustomInterface"/> event. /// </summary> public PluginData GetFaceExtData() { var chafile = GetExtDataTargetChaFile(false); KoikatuAPI.Assert(chafile.custom != null, "chafile.custom != null"); KoikatuAPI.Assert(chafile.custom.face != null, "chafile.custom.face != null"); chafile.custom.face.TryGetExtendedDataById(ExtendedDataId, out var data); return(data); }
internal static void ChaFile_SaveFile_Postfix(ChaFile __instance) { // ReSharper disable once UseNullPropagation if (__instance == null) { return; } __instance.GetTranslationHelperController() .SafeProcObject(c => c.OnCardSaveComplete(KoikatuAPI.GetCurrentGameMode())); }
private static void ChaControl_ChangeSettingEyelineUp(ChaControl __instance) { //Only care about maker and studio, other modes have problems that aren't worth the effort if (KoikatuAPI.GetCurrentGameMode() == GameMode.MainGame) { return; } EyebrowFix.SetEyeliners(__instance, __instance.chaFile.custom.face.foregroundEyes); }
/// <summary> /// Set extended data for specific clothes. /// Always call Set right after changing any of the data, or the change might not be saved if the data is changed for whatever reason (clothing change, reload, etc.) /// This data is saved alongside game data, which means it is automatically copied and moved as necessary. /// </summary> /// <param name="data">Extended data to save.</param> /// <param name="coordinateId">The coordinate number to open extened data for if the game supports multiple coords (0-indexed). -1 will use the current coordinate.</param> public void SetClothesExtData(PluginData data, int coordinateId = -1) { var coord = GetCoordinate(coordinateId); KoikatuAPI.Assert(coord != null, nameof(coord) + " != null"); KoikatuAPI.Assert(coord.clothes != null, "coord.clothes != null"); var clothes = coord.clothes; clothes.SetExtendedDataById(ExtendedDataId, data); }
/// <summary> /// Set extended data for a specific accessory. /// Always call Set right after changing any of the data, or the change might not be saved if the data is changed for whatever reason (clothing change, reload, etc.) /// This data is saved alongside game data, which means it is automatically copied and moved as necessary. /// </summary> /// <param name="data">Extended data to save.</param> /// <param name="accessoryPartId">The accessory part number to open extened data for (0-indexed).</param> /// <param name="coordinateId">The coordinate number to open extened data for if the game supports multiple coords (0-indexed). -1 will use the current coordinate.</param> public void SetAccessoryExtData(PluginData data, int accessoryPartId, int coordinateId = -1) { var coord = GetCoordinate(coordinateId); KoikatuAPI.Assert(coord != null, nameof(coord) + " != null"); KoikatuAPI.Assert(coord.accessory != null, "coord.accessory != null"); var accessoryPart = coord.accessory.parts[accessoryPartId]; accessoryPart.SetExtendedDataById(ExtendedDataId, data); }
private static void NotBraShortsOverride(ChaControl __instance, ref bool value) { if (__instance.GetCurrentCrest() == CrestType.liberated) { if (KoikatuAPI.GetCurrentGameMode() == GameMode.MainGame || GameAPI.InsideHScene) { // Force underwear to be off value = true; } } }