PropManager.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using BepInEx.Configuration;
  6. using UnityEngine;
  7. using Object = UnityEngine.Object;
  8. namespace MeidoPhotoStudio.Plugin
  9. {
  10. using static ModelUtility;
  11. public class PropManager : IManager
  12. {
  13. public const string header = "PROP";
  14. private static readonly ConfigEntry<bool> modItemsOnly;
  15. private static bool cubeActive = true;
  16. private static Dictionary<string, string> modFileToFullPath;
  17. private static Dictionary<string, string> ModFileToFullPath
  18. {
  19. get
  20. {
  21. if (modFileToFullPath != null) return modFileToFullPath;
  22. string[] modFiles = Menu.GetModFiles();
  23. modFileToFullPath = new Dictionary<string, string>(modFiles.Length, StringComparer.OrdinalIgnoreCase);
  24. foreach (var mod in modFiles)
  25. {
  26. var key = Path.GetFileName(mod);
  27. if (!modFileToFullPath.ContainsKey(key)) modFileToFullPath[key] = mod;
  28. }
  29. return modFileToFullPath;
  30. }
  31. }
  32. public static bool CubeActive
  33. {
  34. get => cubeActive;
  35. set
  36. {
  37. if (value != cubeActive)
  38. {
  39. cubeActive = value;
  40. CubeActiveChange?.Invoke(null, EventArgs.Empty);
  41. }
  42. }
  43. }
  44. private static bool cubeSmall;
  45. public static bool CubeSmall
  46. {
  47. get => cubeSmall;
  48. set
  49. {
  50. if (value != cubeSmall)
  51. {
  52. cubeSmall = value;
  53. CubeSmallChange?.Invoke(null, EventArgs.Empty);
  54. }
  55. }
  56. }
  57. private static event EventHandler CubeActiveChange;
  58. private static event EventHandler CubeSmallChange;
  59. public static bool ModItemsOnly => modItemsOnly.Value;
  60. private readonly List<DragPointProp> propList = new List<DragPointProp>();
  61. public string[] PropNameList => propList.Count == 0
  62. ? new[] { Translation.Get("systemMessage", "noProps") }
  63. : propList.Select(prop => prop.Name).ToArray();
  64. public int PropCount => propList.Count;
  65. private int currentPropIndex;
  66. private MeidoManager meidoManager;
  67. public int CurrentPropIndex
  68. {
  69. get => currentPropIndex;
  70. set
  71. {
  72. if (PropCount == 0)
  73. {
  74. currentPropIndex = 0;
  75. return;
  76. }
  77. if ((uint) value >= (uint) PropCount) throw new ArgumentOutOfRangeException(nameof(value));
  78. if (currentPropIndex == value) return;
  79. currentPropIndex = value;
  80. PropSelectionChange?.Invoke(this, EventArgs.Empty);
  81. }
  82. }
  83. public DragPointProp CurrentProp => PropCount == 0 ? null : propList[CurrentPropIndex];
  84. public event EventHandler PropSelectionChange;
  85. public event EventHandler FromPropSelect;
  86. public event EventHandler PropListChange;
  87. static PropManager() => modItemsOnly = Configuration.Config.Bind(
  88. "Prop", "ModItemsOnly",
  89. false,
  90. "Disable waiting for and loading base game clothing"
  91. );
  92. public PropManager(MeidoManager meidoManager)
  93. {
  94. this.meidoManager = meidoManager;
  95. meidoManager.BeginCallMeidos += OnBeginCallMeidos;
  96. meidoManager.EndCallMeidos += OnEndCallMeidos;
  97. Activate();
  98. }
  99. public bool AddFromPropInfo(PropInfo propInfo)
  100. {
  101. switch (propInfo.Type)
  102. {
  103. case PropInfo.PropType.Mod:
  104. ModItem modItem;
  105. if (!string.IsNullOrEmpty(propInfo.SubFilename))
  106. {
  107. modItem = ModItem.OfficialMod(ModFileToFullPath[propInfo.Filename]);
  108. modItem.BaseMenuFile = propInfo.SubFilename;
  109. }
  110. else modItem = ModItem.Mod(propInfo.Filename);
  111. return AddModProp(modItem);
  112. case PropInfo.PropType.MyRoom:
  113. return AddMyRoomProp(new MyRoomItem { ID = propInfo.MyRoomID, PrefabName = propInfo.Filename });
  114. case PropInfo.PropType.Bg:
  115. return AddBgProp(propInfo.Filename);
  116. case PropInfo.PropType.Odogu:
  117. return AddGameProp(propInfo.Filename);
  118. default: throw new ArgumentOutOfRangeException();
  119. }
  120. }
  121. public bool AddModProp(ModItem modItem)
  122. {
  123. var model = LoadMenuModel(modItem);
  124. if (!model) return false;
  125. var name = modItem.MenuFile;
  126. if (modItem.IsOfficialMod) name = Path.GetFileName(name) + ".menu"; // add '.menu' for partsedit support
  127. model.name = name;
  128. var dragPoint = AttachDragPoint(model);
  129. dragPoint.Info = PropInfo.FromModItem(modItem);
  130. AddProp(dragPoint);
  131. return true;
  132. }
  133. public bool AddMyRoomProp(MyRoomItem myRoomItem)
  134. {
  135. var model = LoadMyRoomModel(myRoomItem);
  136. if (!model) return false;
  137. model.name = Translation.Get("myRoomPropNames", myRoomItem.PrefabName);
  138. var dragPoint = AttachDragPoint(model);
  139. dragPoint.Info = PropInfo.FromMyRoom(myRoomItem);
  140. AddProp(dragPoint);
  141. return true;
  142. }
  143. public bool AddBgProp(string assetName)
  144. {
  145. var model = LoadBgModel(assetName);
  146. if (!model) return false;
  147. model.name = Translation.Get("bgNames", assetName);
  148. var dragPoint = AttachDragPoint(model);
  149. dragPoint.Info = PropInfo.FromBg(assetName);
  150. AddProp(dragPoint);
  151. return true;
  152. }
  153. public bool AddGameProp(string assetName)
  154. {
  155. var isMenu = assetName.EndsWith(".menu");
  156. var model = isMenu ? LoadMenuModel(assetName) : LoadGameModel(assetName);
  157. if (!model) return false;
  158. model.name = Translation.Get("propNames", isMenu ? Utility.HandItemToOdogu(assetName) : assetName, !isMenu);
  159. var dragPoint = AttachDragPoint(model);
  160. dragPoint.Info = PropInfo.FromGameProp(assetName);
  161. AddProp(dragPoint);
  162. return true;
  163. }
  164. public void CopyProp(int propIndex)
  165. {
  166. if ((uint) propIndex >= (uint) PropCount) throw new ArgumentOutOfRangeException(nameof(propIndex));
  167. AddFromPropInfo(propList[propIndex].Info);
  168. }
  169. public void DeleteAllProps()
  170. {
  171. foreach (var prop in propList) DestroyProp(prop);
  172. propList.Clear();
  173. CurrentPropIndex = 0;
  174. EmitPropListChange();
  175. }
  176. public void RemoveProp(int index)
  177. {
  178. if ((uint) index >= (uint) PropCount) throw new ArgumentOutOfRangeException(nameof(index));
  179. DestroyProp(propList[index]);
  180. propList.RemoveAt(index);
  181. CurrentPropIndex = Utility.Bound(CurrentPropIndex, 0, PropCount - 1);
  182. EmitPropListChange();
  183. }
  184. public void AttachProp(DragPointProp prop, AttachPoint point, int index)
  185. {
  186. if ((uint) index >= (uint) meidoManager.ActiveMeidoList.Count) return;
  187. var meido = meidoManager.ActiveMeidoList[index];
  188. prop.AttachTo(meido, point);
  189. }
  190. private DragPointProp AttachDragPoint(GameObject model)
  191. {
  192. var dragPoint = DragPoint.Make<DragPointProp>(PrimitiveType.Cube, Vector3.one * 0.12f);
  193. dragPoint.Initialize(() => model.transform.position, () => Vector3.zero);
  194. dragPoint.Set(model.transform);
  195. dragPoint.AddGizmo(0.45f, CustomGizmo.GizmoMode.World);
  196. dragPoint.ConstantScale = true;
  197. dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
  198. dragPoint.Delete += OnDeleteProp;
  199. dragPoint.Select += OnSelectProp;
  200. return dragPoint;
  201. }
  202. private void AddProp(DragPointProp dragPoint)
  203. {
  204. propList.Add(dragPoint);
  205. EmitPropListChange();
  206. }
  207. private void DestroyProp(DragPointProp prop)
  208. {
  209. if (!prop) return;
  210. prop.Delete -= OnDeleteProp;
  211. prop.Select -= OnSelectProp;
  212. Object.Destroy(prop.gameObject);
  213. }
  214. private void EmitPropListChange() => PropListChange?.Invoke(this, EventArgs.Empty);
  215. private void OnBeginCallMeidos(object sender, EventArgs args)
  216. {
  217. foreach (var prop in propList.Where(p => p.AttachPointInfo.AttachPoint != AttachPoint.None))
  218. prop.DetachTemporary();
  219. }
  220. private void OnEndCallMeidos(object sender, EventArgs args)
  221. {
  222. foreach (var prop in propList.Where(p => p.AttachPointInfo.AttachPoint != AttachPoint.None))
  223. {
  224. var info = prop.AttachPointInfo;
  225. var meido = meidoManager.GetMeido(info.MaidGuid);
  226. prop.AttachTo(meido, info.AttachPoint, meido == null);
  227. }
  228. }
  229. private void OnDeleteProp(object sender, EventArgs args)
  230. => RemoveProp(propList.IndexOf((DragPointProp) sender));
  231. private void OnSelectProp(object sender, EventArgs args)
  232. {
  233. CurrentPropIndex = propList.IndexOf((DragPointProp) sender);
  234. FromPropSelect?.Invoke(this, EventArgs.Empty);
  235. }
  236. private void OnCubeSmall(object sender, EventArgs args)
  237. {
  238. foreach (var dragPoint in propList) dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
  239. }
  240. private void OnCubeActive(object sender, EventArgs args)
  241. {
  242. foreach (var dragPoint in propList) dragPoint.gameObject.SetActive(CubeActive);
  243. }
  244. public void Update() { }
  245. public void Activate()
  246. {
  247. CubeSmallChange += OnCubeSmall;
  248. CubeActiveChange += OnCubeActive;
  249. }
  250. public void Deactivate()
  251. {
  252. DeleteAllProps();
  253. CubeSmallChange -= OnCubeSmall;
  254. CubeActiveChange -= OnCubeActive;
  255. }
  256. }
  257. }