diff --git a/AI_ReverseTrap/AI_ReverseTrap.csproj b/AI_ReverseTrap/AI_ReverseTrap.csproj
new file mode 100644
index 0000000..9468c2f
--- /dev/null
+++ b/AI_ReverseTrap/AI_ReverseTrap.csproj
@@ -0,0 +1,89 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {24E7EF57-8992-404A-B9A6-FFB9ABFEB4AB}
+ Library
+ Properties
+ AI_ReverseTrap
+ AI_ReverseTrap
+ v4.6
+ 512
+ true
+
+
+ true
+ embedded
+ false
+ ..\bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ embedded
+ true
+ ..\bin\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\BepInEx\0Harmony.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\bin\AIAPI.dll
+ False
+
+
+ ..\..\BepisPlugins\bin\BepInEx\plugins\AI_BepisPlugins\AI_ExtensibleSaveFormat.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\AISyoujyo\Assembly-CSharp.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\BepInEx\BepInEx.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\BepInEx\BepInEx.Harmony.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\AISyoujyo\Sirenix.Serialization.dll
+ False
+
+
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\AISyoujyo\UnityEngine.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\AISyoujyo\UnityEngine.AnimationModule.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\AISyoujyo\UnityEngine.AssetBundleModule.dll
+ False
+
+
+ ..\..\..\KKEC\KKAPI\paket-files\IllusionMods\IllusionLibs\lib\AISyoujyo\UnityEngine.CoreModule.dll
+ False
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AI_ReverseTrap/Properties/AssemblyInfo.cs b/AI_ReverseTrap/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..b285c09
--- /dev/null
+++ b/AI_ReverseTrap/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+using AI_ReverseTrap;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AI_ReverseTrap")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AI_ReverseTrap")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("24e7ef57-8992-404a-b9a6-ffb9abfeb4ab")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion(ReverseTrap.Version)]
diff --git a/AI_ReverseTrap/ReverseTrap.Hooks.cs b/AI_ReverseTrap/ReverseTrap.Hooks.cs
new file mode 100644
index 0000000..30e4352
--- /dev/null
+++ b/AI_ReverseTrap/ReverseTrap.Hooks.cs
@@ -0,0 +1,34 @@
+using AIChara;
+using AIProject;
+using HarmonyLib;
+using UnityEngine;
+
+namespace AI_ReverseTrap
+{
+ public partial class ReverseTrap
+ {
+ private static class Hooks
+ {
+ [HarmonyPostfix]
+ // void ActorAnimation.SetAnimatorController(RuntimeAnimatorController rac)
+ [HarmonyPatch(typeof(ActorAnimation), nameof(ActorAnimation.SetAnimatorController), typeof(RuntimeAnimatorController))]
+ private static void SetAnimatorPost(ActorAnimation __instance)
+ {
+ if (__instance.Actor != null && __instance.Actor.ChaControl != null)
+ {
+ var ctrl = __instance.Actor.ChaControl.GetComponent();
+ ctrl?.RefreshOverrideAnimations();
+ }
+ }
+
+ [HarmonyPostfix]
+ // RuntimeAnimatorController LoadAnimation(string assetBundleName, string assetName, string manifestName)
+ [HarmonyPatch(typeof(ChaControl), nameof(ChaControl.LoadAnimation), typeof(string), typeof(string), typeof(string))]
+ private static void SetAnimatorPost2(ChaControl __instance)
+ {
+ var ctrl = __instance.GetComponent();
+ ctrl?.RefreshOverrideAnimations();
+ }
+ }
+ }
+}
diff --git a/AI_ReverseTrap/ReverseTrap.cs b/AI_ReverseTrap/ReverseTrap.cs
new file mode 100644
index 0000000..5b69b84
--- /dev/null
+++ b/AI_ReverseTrap/ReverseTrap.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using BepInEx;
+using BepInEx.Harmony;
+using BepInEx.Logging;
+using KKAPI;
+using KKAPI.Chara;
+using KKAPI.Maker;
+using KKAPI.Maker.UI;
+using UnityEngine;
+
+namespace AI_ReverseTrap
+{
+ [BepInPlugin(GUID, "Reverse Trap", Version)]
+ [BepInProcess("AI-Syoujyo")]
+ [BepInDependency(KoikatuAPI.GUID, KoikatuAPI.VersionConst)]
+ public partial class ReverseTrap : BaseUnityPlugin
+ {
+ public const string GUID = "ReverseTrap";
+ public const string Version = "1.0";
+
+ internal static new ManualLogSource Logger { get; private set; }
+
+ internal const int MaleSex = 0;
+ internal static AnimationClip[] MaleAnimations { get; private set; }
+ internal static readonly Dictionary ToMaleAnimationLookup = new Dictionary
+ {
+ {"Idle_00", "m_Idle_00"},
+ {"mc_f_move_00", "mc_m_move_00"},
+ {"mc_f_move_05", "mc_m_move_01"},
+ {"mc_f_move_08_L", "mc_m_move_05_L"},
+ {"mc_f_move_07_L", "mc_m_move_04_L"},
+ {"Turn_Idle_00", "m_Turn_Idle_00"},
+ {"mc_f_move_07_R", "mc_m_move_04_R"},
+ {"mc_f_move_08_R", "mc_m_move_05_R"},
+ {"mc_pf_move_02_S_in", "mc_m_move_02_S_in"},
+ {"mc_pf_move_02_M_in", "mc_m_move_02_M_in"},
+ {"mc_pf_move_02_L_in", "mc_m_move_02_L_in"},
+ {"mc_pf_move_02_S_loop", "mc_m_move_02_S_loop"},
+ {"mc_pf_move_02_M_loop", "mc_m_move_02_M_loop"},
+ {"mc_pf_move_02_L_loop", "mc_m_move_02_L_loop"},
+ {"mc_pf_move_03_S", "mc_m_move_03_S"},
+ {"mc_pf_move_03_M", "mc_m_move_03_M"},
+ {"mc_pf_move_03_L", "mc_m_move_03_L"},
+ {"mc_f_action_00", "mc_m_action_00"},
+ {"mc_pf_action_01", "mc_m_action_01"},
+ {"mc_pf_action_02", "mc_m_action_02"},
+ {"mc_pf_action_03", "mc_m_action_03"},
+ {"mc_pf_action_04", "mc_m_action_04"},
+ {"neko_01", "m_neko_00"},
+ {"pf_neko_04_in", "m_neko_01_in"},
+ {"pf_neko_04_loop", "m_neko_01_loop"},
+ {"pf_neko_05", "m_neko_02"},
+ {"pf_neko_06", "m_neko_03"},
+ {"chair_Idle_00_M", "m_chair_Idle_00"},
+ {"chair_16_S", "m_chair_00_S"},
+ {"chair_16_M", "m_chair_00_M"},
+ {"chair_16_L", "m_chair_00_L"},
+ {"chair_15_S", "m_chair_01_S"},
+ {"chair_15_M", "m_chair_01_M"},
+ {"chair_15_L", "m_chair_01_L"},
+ {"deskchair_Idle_00_M", "m_deskchair_Idle_00"},
+ {"mc_pf_move_00_01", "mc_m_move_00_01"},
+ {"mc_pf_move_05_01", "mc_m_move_01_01"},
+ {"mc_pf_move_00_02", "mc_m_move_00_02"},
+ {"mc_pf_move_05_02", "mc_m_move_01_02"},
+ {"pf_Idle_00_03_S", "m_Idle_00_03_S"},
+ {"pf_Idle_00_03_M", "m_Idle_00_03_M"},
+ {"pf_Idle_00_03_L", "m_Idle_00_03_L"},
+ {"mc_pf_move_00_03_S", "mc_m_move_00_03_S"},
+ {"mc_pf_move_00_03_M", "mc_m_move_00_03_M"},
+ {"mc_pf_move_00_03_L", "mc_m_move_00_03_L"}
+ };
+
+ private void Start()
+ {
+ Logger = base.Logger;
+
+ try
+ {
+ // Load male animation clips for overriding
+ var ab = AssetBundle.LoadFromFile(Application.dataPath + @"\..\abdata\animator\action\male\00.unity3d");
+ var anim = ab.LoadAsset("m_player.controller");
+ MaleAnimations = anim.animationClips.ToArray();
+ ab.Unload(false);
+ Destroy(anim);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError("Failed to read male player animation data - " + ex);
+ }
+
+ if (MaleAnimations != null && MaleAnimations.Any())
+ {
+ CharacterApi.RegisterExtraBehaviour(GUID);
+
+ MakerAPI.RegisterCustomSubCategories += MakerAPI_RegisterCustomSubCategories;
+
+ HarmonyWrapper.PatchAll(typeof(Hooks));
+ }
+ }
+
+ private void MakerAPI_RegisterCustomSubCategories(object sender, RegisterSubCategoriesEvent e)
+ {
+ if (MakerAPI.GetMakerSex() != MaleSex)
+ {
+ var makerToggle = e.AddControl(new MakerToggle(MakerConstants.Body.All, "Male walking animations", this));
+
+ makerToggle.BindToFunctionController(
+ controller => controller.ForceMaleAnimations,
+ (controller, value) => controller.ForceMaleAnimations = value);
+ }
+ }
+ }
+}
diff --git a/AI_ReverseTrap/ReverseTrapController.cs b/AI_ReverseTrap/ReverseTrapController.cs
new file mode 100644
index 0000000..62070ea
--- /dev/null
+++ b/AI_ReverseTrap/ReverseTrapController.cs
@@ -0,0 +1,91 @@
+using System.Linq;
+using ExtensibleSaveFormat;
+using KKAPI;
+using KKAPI.Chara;
+using UnityEngine;
+
+namespace AI_ReverseTrap
+{
+ public class ReverseTrapController : CharaCustomFunctionController
+ {
+ private bool _forceMaleAnimations;
+
+ public bool ForceMaleAnimations
+ {
+ get => _forceMaleAnimations;
+ set
+ {
+ value = value && ChaControl.sex != ReverseTrap.MaleSex;
+
+ if (_forceMaleAnimations != value)
+ {
+ _forceMaleAnimations = value;
+
+ RefreshOverrideAnimations();
+ }
+ }
+ }
+
+ protected override void OnCardBeingSaved(GameMode currentGameMode)
+ {
+ if (ChaControl.sex != ReverseTrap.MaleSex)
+ {
+ var data = new PluginData { data = { [nameof(ForceMaleAnimations)] = ForceMaleAnimations } };
+ SetExtendedData(data);
+ }
+ else
+ SetExtendedData(null);
+ }
+
+ protected override void OnReload(GameMode currentGameMode)
+ {
+ if (ChaControl.sex != ReverseTrap.MaleSex)
+ {
+ var data = GetExtendedData()?.data;
+ ForceMaleAnimations = data != null && data.TryGetValue(nameof(ForceMaleAnimations), out var force) && force as bool? == true;
+ }
+ else
+ ForceMaleAnimations = false;
+ }
+
+ internal void RefreshOverrideAnimations()
+ {
+ var animBody = ChaControl.animBody;
+
+ if (ReverseTrap.MaleAnimations == null || animBody == null || animBody.runtimeAnimatorController == null) return;
+
+ var overrideControler = animBody.runtimeAnimatorController as AnimatorOverrideController;
+ if (overrideControler == null)
+ {
+ if (!ForceMaleAnimations) return;
+
+ overrideControler = new AnimatorOverrideController(animBody.runtimeAnimatorController);
+ animBody.runtimeAnimatorController = overrideControler;
+ }
+ else if (!ForceMaleAnimations)
+ {
+ animBody.runtimeAnimatorController = overrideControler.runtimeAnimatorController;
+ return;
+ }
+
+ var animationClips = animBody.runtimeAnimatorController.animationClips.ToArray();
+
+ foreach (var animationClip in animationClips)
+ {
+ if (ReverseTrap.ToMaleAnimationLookup.TryGetValue(animationClip.name, out var targetClipName))
+ {
+ var replacement = ReverseTrap.MaleAnimations.FirstOrDefault(clip => clip.name == targetClipName);
+ if (replacement != null)
+ {
+ ReverseTrap.Logger.LogDebug($"Replacing animation {animationClip.name} with {replacement.name}");
+ overrideControler[animationClip] = replacement;
+ }
+ else
+ {
+ ReverseTrap.Logger.LogWarning($"Failed to replace animation {animationClip.name} with {targetClipName} because replacement clip was not found");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/KK_BecomeTrap.sln b/KK_BecomeTrap.sln
index 4bd5879..1ff5208 100644
--- a/KK_BecomeTrap.sln
+++ b/KK_BecomeTrap.sln
@@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.28010.2016
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KK_BecomeTrap", "KK_BecomeTrap\KK_BecomeTrap.csproj", "{F47E187C-9A8F-45E7-A778-5057E1D9B3F0}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AI_ReverseTrap", "AI_ReverseTrap\AI_ReverseTrap.csproj", "{24E7EF57-8992-404A-B9A6-FFB9ABFEB4AB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
{F47E187C-9A8F-45E7-A778-5057E1D9B3F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F47E187C-9A8F-45E7-A778-5057E1D9B3F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F47E187C-9A8F-45E7-A778-5057E1D9B3F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {24E7EF57-8992-404A-B9A6-FFB9ABFEB4AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {24E7EF57-8992-404A-B9A6-FFB9ABFEB4AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {24E7EF57-8992-404A-B9A6-FFB9ABFEB4AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {24E7EF57-8992-404A-B9A6-FFB9ABFEB4AB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/KK_BecomeTrap/KK_BecomeTrap.csproj b/KK_BecomeTrap/KK_BecomeTrap.csproj
index caa4894..cd4425e 100644
--- a/KK_BecomeTrap/KK_BecomeTrap.csproj
+++ b/KK_BecomeTrap/KK_BecomeTrap.csproj
@@ -15,20 +15,21 @@
true
- full
+ embedded
false
- ..\..\..\..\Games\Koikatsu\BepInEx\
+ ..\bin\
DEBUG;TRACE
prompt
4
- pdbonly
+ embedded
true
- ..\..\..\..\Games\Koikatsu\BepInEx\
+ ..\bin\
TRACE
prompt
4
+ true