MeidoManager.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEngine;
  5. namespace COM3D2.MeidoPhotoStudio.Plugin
  6. {
  7. public class MeidoManager : IManager
  8. {
  9. public const string header = "MEIDO";
  10. private static readonly CharacterMgr characterMgr = GameMain.Instance.CharacterMgr;
  11. private int undress;
  12. private int numberOfMeidos;
  13. private int tempEditMaidIndex = -1;
  14. public Meido[] Meidos { get; private set; }
  15. public HashSet<int> SelectedMeidoSet { get; } = new HashSet<int>();
  16. public List<int> SelectMeidoList { get; } = new List<int>();
  17. public List<Meido> ActiveMeidoList { get; } = new List<Meido>();
  18. public Meido ActiveMeido => ActiveMeidoList.Count > 0 ? ActiveMeidoList[SelectedMeido] : null;
  19. public Meido EditMeido => tempEditMaidIndex >= 0 ? Meidos[tempEditMaidIndex] : Meidos[EditMaidIndex];
  20. public bool HasActiveMeido => ActiveMeido != null;
  21. public event EventHandler<MeidoUpdateEventArgs> UpdateMeido;
  22. public event EventHandler EndCallMeidos;
  23. public event EventHandler BeginCallMeidos;
  24. private int selectedMeido;
  25. public int SelectedMeido
  26. {
  27. get => selectedMeido;
  28. private set => selectedMeido = Utility.Bound(value, 0, ActiveMeidoList.Count - 1);
  29. }
  30. public int EditMaidIndex { get; private set; }
  31. public bool Busy => ActiveMeidoList.Any(meido => meido.Busy);
  32. private bool globalGravity;
  33. public bool GlobalGravity
  34. {
  35. get => globalGravity;
  36. set
  37. {
  38. globalGravity = value;
  39. if (!HasActiveMeido) return;
  40. Meido activeMeido = ActiveMeido;
  41. int activeMeidoSlot = activeMeido.Slot;
  42. foreach (Meido meido in ActiveMeidoList)
  43. {
  44. if (meido.Slot != activeMeidoSlot)
  45. {
  46. meido.HairGravityActive = value && activeMeido.HairGravityActive;
  47. meido.SkirtGravityActive = value && activeMeido.SkirtGravityActive;
  48. }
  49. }
  50. }
  51. }
  52. static MeidoManager() => InputManager.Register(MpsKey.MeidoUndressing, KeyCode.H, "All maid undressing");
  53. public MeidoManager() => Activate();
  54. public void ChangeMaid(int index) => OnUpdateMeido(null, new MeidoUpdateEventArgs(index));
  55. public void Activate()
  56. {
  57. GameMain.Instance.CharacterMgr.ResetCharaPosAll();
  58. numberOfMeidos = characterMgr.GetStockMaidCount();
  59. Meidos = new Meido[numberOfMeidos];
  60. tempEditMaidIndex = -1;
  61. for (int stockMaidIndex = 0; stockMaidIndex < numberOfMeidos; stockMaidIndex++)
  62. {
  63. Meidos[stockMaidIndex] = new Meido(stockMaidIndex);
  64. }
  65. if (MeidoPhotoStudio.EditMode)
  66. {
  67. Maid editMaid = GameMain.Instance.CharacterMgr.GetMaid(0);
  68. EditMaidIndex = Array.FindIndex(Meidos, meido => meido.Maid.status.guid == editMaid.status.guid);
  69. EditMeido.IsEditMaid = true;
  70. var editOkCancel = UTY.GetChildObject(GameObject.Find("UI Root"), "OkCancel")
  71. .GetComponent<EditOkCancel>();
  72. // Ensure MPS resets editor state before setting maid
  73. EditOkCancel.OnClick newEditOnClick = () => SetEditMaid(Meidos[EditMaidIndex]);
  74. newEditOnClick += OkCancelDelegate();
  75. Utility.SetFieldValue(editOkCancel, "m_dgOnClickOk", newEditOnClick);
  76. // Only for setting custom parts placement animation just in case body was changed before activating MPS
  77. SetEditMaid(Meidos[EditMaidIndex]);
  78. }
  79. ClearSelectList();
  80. }
  81. public void Deactivate()
  82. {
  83. foreach (Meido meido in Meidos)
  84. {
  85. meido.UpdateMeido -= OnUpdateMeido;
  86. meido.GravityMove -= OnGravityMove;
  87. meido.Deactivate();
  88. }
  89. ActiveMeidoList.Clear();
  90. if (MeidoPhotoStudio.EditMode && !GameMain.Instance.MainCamera.IsFadeOut())
  91. {
  92. Meido meido = Meidos[EditMaidIndex];
  93. meido.Maid.Visible = true;
  94. meido.Stop = false;
  95. meido.EyeToCam = true;
  96. SetEditMaid(meido);
  97. // Restore original OK button functionality
  98. GameObject okButton = UTY.GetChildObjectNoError(GameObject.Find("UI Root"), "OkCancel");
  99. if (okButton)
  100. {
  101. EditOkCancel editOkCancel = okButton.GetComponent<EditOkCancel>();
  102. Utility.SetFieldValue(editOkCancel, "m_dgOnClickOk", OkCancelDelegate());
  103. }
  104. }
  105. }
  106. private EditOkCancel.OnClick OkCancelDelegate()
  107. {
  108. return (EditOkCancel.OnClick)Delegate
  109. .CreateDelegate(typeof(EditOkCancel.OnClick), SceneEdit.Instance, "OnEditOk");
  110. }
  111. public void Update()
  112. {
  113. if (InputManager.GetKeyDown(MpsKey.MeidoUndressing)) UndressAll();
  114. }
  115. private void UnloadMeidos()
  116. {
  117. SelectedMeido = 0;
  118. foreach (Meido meido in ActiveMeidoList)
  119. {
  120. meido.UpdateMeido -= OnUpdateMeido;
  121. meido.GravityMove -= OnGravityMove;
  122. meido.Unload();
  123. }
  124. ActiveMeidoList.Clear();
  125. }
  126. public void CallMeidos()
  127. {
  128. BeginCallMeidos?.Invoke(this, EventArgs.Empty);
  129. bool moreThanEditMaid = ActiveMeidoList.Count > 1;
  130. UnloadMeidos();
  131. if (SelectMeidoList.Count == 0)
  132. {
  133. OnEndCallMeidos(this, EventArgs.Empty);
  134. return;
  135. }
  136. void callMeidos() => GameMain.Instance.StartCoroutine(LoadMeidos());
  137. if (MeidoPhotoStudio.EditMode && !moreThanEditMaid && SelectMeidoList.Count == 1) callMeidos();
  138. else GameMain.Instance.MainCamera.FadeOut(0.01f, f_bSkipable: false, f_dg: callMeidos);
  139. }
  140. private System.Collections.IEnumerator LoadMeidos()
  141. {
  142. foreach (int slot in SelectMeidoList) ActiveMeidoList.Add(Meidos[slot]);
  143. for (int i = 0; i < ActiveMeidoList.Count; i++) ActiveMeidoList[i].Load(i);
  144. while (Busy) yield return null;
  145. yield return new WaitForEndOfFrame();
  146. OnEndCallMeidos(this, EventArgs.Empty);
  147. }
  148. public void SelectMeido(int index)
  149. {
  150. if (SelectedMeidoSet.Contains(index))
  151. {
  152. if (!MeidoPhotoStudio.EditMode || index != EditMaidIndex)
  153. {
  154. SelectedMeidoSet.Remove(index);
  155. SelectMeidoList.Remove(index);
  156. }
  157. }
  158. else
  159. {
  160. SelectedMeidoSet.Add(index);
  161. SelectMeidoList.Add(index);
  162. }
  163. }
  164. public void ClearSelectList()
  165. {
  166. SelectedMeidoSet.Clear();
  167. SelectMeidoList.Clear();
  168. if (MeidoPhotoStudio.EditMode)
  169. {
  170. SelectedMeidoSet.Add(EditMaidIndex);
  171. SelectMeidoList.Add(EditMaidIndex);
  172. }
  173. }
  174. public void SetEditMaid(Meido meido)
  175. {
  176. if (!MeidoPhotoStudio.EditMode) return;
  177. EditMeido.IsEditMaid = false;
  178. tempEditMaidIndex = meido.Maid.status.guid == Meidos[EditMaidIndex].Maid.status.guid
  179. ? -1
  180. : Array.FindIndex(Meidos, maid => maid.Maid.status.guid == meido.Maid.status.guid);
  181. EditMeido.IsEditMaid = true;
  182. Maid newEditMaid = EditMeido.Maid;
  183. GameObject uiRoot = GameObject.Find("UI Root");
  184. var presetCtrl = UTY.GetChildObjectNoError(uiRoot, "PresetPanel")?.GetComponent<PresetCtrl>();
  185. var presetButton = UTY.GetChildObjectNoError(uiRoot, "PresetButtonPanel")?.GetComponent<PresetButtonCtrl>();
  186. var profileCtrl = UTY.GetChildObjectNoError(uiRoot, "ProfilePanel")?.GetComponent<ProfileCtrl>();
  187. var customPartsWindow = UTY.GetChildObjectNoError(uiRoot, "Window/CustomPartsWindow")
  188. ?.GetComponent<SceneEditWindow.CustomPartsWindow>();
  189. if (!(presetCtrl || presetButton || profileCtrl || customPartsWindow)) return;
  190. // Preset application
  191. Utility.SetFieldValue(presetCtrl, "m_maid", newEditMaid);
  192. // Preset saving
  193. Utility.SetFieldValue(presetButton, "m_maid", newEditMaid);
  194. // Maid profile (name, description, experience etc)
  195. Utility.SetFieldValue(profileCtrl, "m_maidStatus", newEditMaid.status);
  196. // Accessory/Parts placement
  197. Utility.SetFieldValue(customPartsWindow, "maid", newEditMaid);
  198. // Stopping maid animation and head movement when customizing parts placement
  199. Utility.SetFieldValue(customPartsWindow, "animation", newEditMaid.GetAnimation());
  200. // Clothing/body in general and maybe other things
  201. Utility.SetFieldValue(SceneEdit.Instance, "m_maid", newEditMaid);
  202. // Body status, parts colours and maybe more
  203. Utility.GetFieldValue<CharacterMgr, Maid[]>(
  204. GameMain.Instance.CharacterMgr, "m_gcActiveMaid"
  205. )[0] = newEditMaid;
  206. }
  207. public Meido GetMeido(string guid)
  208. {
  209. return string.IsNullOrEmpty(guid) ? null : ActiveMeidoList.Find(meido => meido.Maid.status.guid == guid);
  210. }
  211. public Meido GetMeido(int activeIndex)
  212. {
  213. return activeIndex >= 0 && activeIndex < ActiveMeidoList.Count ? ActiveMeidoList[activeIndex] : null;
  214. }
  215. public void PlaceMeidos(string placementType)
  216. {
  217. MaidPlacementUtility.ApplyPlacement(placementType, ActiveMeidoList);
  218. }
  219. private void UndressAll()
  220. {
  221. if (!HasActiveMeido) return;
  222. undress = ++undress % Enum.GetNames(typeof(Meido.Mask)).Length;
  223. foreach (Meido activeMeido in ActiveMeidoList) activeMeido.SetMaskMode((Meido.Mask)undress);
  224. UpdateMeido?.Invoke(ActiveMeido, new MeidoUpdateEventArgs(SelectedMeido));
  225. }
  226. private void OnUpdateMeido(object sender, MeidoUpdateEventArgs args)
  227. {
  228. if (!args.IsEmpty) SelectedMeido = args.SelectedMeido;
  229. UpdateMeido?.Invoke(ActiveMeido, args);
  230. }
  231. private void OnEndCallMeidos(object sender, EventArgs args)
  232. {
  233. GameMain.Instance.MainCamera.FadeIn(1f);
  234. EndCallMeidos?.Invoke(this, EventArgs.Empty);
  235. foreach (Meido meido in ActiveMeidoList)
  236. {
  237. meido.UpdateMeido += OnUpdateMeido;
  238. meido.GravityMove += OnGravityMove;
  239. }
  240. if (MeidoPhotoStudio.EditMode && tempEditMaidIndex >= 0 && !SelectedMeidoSet.Contains(tempEditMaidIndex))
  241. {
  242. SetEditMaid(Meidos[EditMaidIndex]);
  243. }
  244. }
  245. private void OnGravityMove(object sender, GravityEventArgs args)
  246. {
  247. if (!GlobalGravity) return;
  248. foreach (Meido meido in ActiveMeidoList)
  249. {
  250. meido.ApplyGravity(args.LocalPosition, args.IsSkirt);
  251. }
  252. }
  253. }
  254. public class MeidoUpdateEventArgs : EventArgs
  255. {
  256. public static new MeidoUpdateEventArgs Empty { get; } = new MeidoUpdateEventArgs(-1);
  257. public bool IsEmpty => (this == Empty) || (SelectedMeido == -1 && !FromMeido && IsBody);
  258. public int SelectedMeido { get; }
  259. public bool IsBody { get; }
  260. public bool FromMeido { get; }
  261. public MeidoUpdateEventArgs(int meidoIndex = -1, bool fromMaid = false, bool isBody = true)
  262. {
  263. SelectedMeido = meidoIndex;
  264. IsBody = isBody;
  265. FromMeido = fromMaid;
  266. }
  267. }
  268. }