PropManager.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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 COM3D2.MeidoPhotoStudio.Plugin
  9. {
  10. using static ModelUtility;
  11. public class PropManager : IManager, ISerializable
  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 void 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
  111. modItem = ModItem.Mod(propInfo.Filename);
  112. AddModProp(modItem);
  113. break;
  114. case PropInfo.PropType.MyRoom:
  115. AddMyRoomProp(new MyRoomItem { ID = propInfo.MyRoomID, PrefabName = propInfo.Filename });
  116. break;
  117. case PropInfo.PropType.Bg:
  118. AddBgProp(propInfo.Filename);
  119. break;
  120. case PropInfo.PropType.Odogu:
  121. AddGameProp(propInfo.Filename);
  122. break;
  123. default: throw new ArgumentOutOfRangeException();
  124. }
  125. }
  126. public bool AddModProp(ModItem modItem)
  127. {
  128. var model = LoadMenuModel(modItem);
  129. if (!model) return false;
  130. var name = modItem.MenuFile;
  131. if (modItem.IsOfficialMod) name = Path.GetFileName(name) + ".menu"; // add '.menu' for partsedit support
  132. model.name = name;
  133. var dragPoint = AttachDragPoint(model);
  134. dragPoint.Info = PropInfo.FromModItem(modItem);
  135. AddProp(dragPoint);
  136. return true;
  137. }
  138. public bool AddMyRoomProp(MyRoomItem myRoomItem)
  139. {
  140. var model = LoadMyRoomModel(myRoomItem);
  141. if (!model) return false;
  142. model.name = Translation.Get("myRoomPropNames", myRoomItem.PrefabName);
  143. var dragPoint = AttachDragPoint(model);
  144. dragPoint.Info = PropInfo.FromMyRoom(myRoomItem);
  145. AddProp(dragPoint);
  146. return true;
  147. }
  148. public bool AddBgProp(string assetName)
  149. {
  150. var model = LoadBgModel(assetName);
  151. if (!model) return false;
  152. model.name = Translation.Get("bgNames", assetName);
  153. var dragPoint = AttachDragPoint(model);
  154. dragPoint.Info = PropInfo.FromBg(assetName);
  155. AddProp(dragPoint);
  156. return true;
  157. }
  158. public bool AddGameProp(string assetName)
  159. {
  160. var isMenu = assetName.EndsWith(".menu");
  161. var model = isMenu ? LoadMenuModel(assetName) : LoadGameModel(assetName);
  162. if (!model) return false;
  163. model.name = Translation.Get("propNames", isMenu ? Utility.HandItemToOdogu(assetName) : assetName, !isMenu);
  164. var dragPoint = AttachDragPoint(model);
  165. dragPoint.Info = PropInfo.FromGameProp(assetName);
  166. AddProp(dragPoint);
  167. return true;
  168. }
  169. public void CopyProp(int propIndex)
  170. {
  171. if ((uint) propIndex >= (uint) PropCount) throw new ArgumentOutOfRangeException(nameof(propIndex));
  172. AddFromPropInfo(propList[propIndex].Info);
  173. }
  174. public void DeleteAllProps()
  175. {
  176. foreach (var prop in propList) DestroyProp(prop);
  177. propList.Clear();
  178. CurrentPropIndex = 0;
  179. EmitPropListChange();
  180. }
  181. public void RemoveProp(int index)
  182. {
  183. if ((uint) index >= (uint) PropCount) throw new ArgumentOutOfRangeException(nameof(index));
  184. DestroyProp(propList[index]);
  185. propList.RemoveAt(index);
  186. CurrentPropIndex = Utility.Bound(CurrentPropIndex, 0, PropCount - 1);
  187. EmitPropListChange();
  188. }
  189. private DragPointProp AttachDragPoint(GameObject model)
  190. {
  191. var dragPoint = DragPoint.Make<DragPointProp>(PrimitiveType.Cube, Vector3.one * 0.12f);
  192. dragPoint.Initialize(() => model.transform.position, () => Vector3.zero);
  193. dragPoint.Set(model.transform);
  194. dragPoint.AddGizmo(0.45f, CustomGizmo.GizmoMode.World);
  195. dragPoint.ConstantScale = true;
  196. dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
  197. dragPoint.Delete += OnDeleteProp;
  198. dragPoint.Select += OnSelectProp;
  199. return dragPoint;
  200. }
  201. private void AddProp(DragPointProp dragPoint)
  202. {
  203. propList.Add(dragPoint);
  204. EmitPropListChange();
  205. }
  206. private void DestroyProp(DragPointProp prop)
  207. {
  208. if (!prop) return;
  209. prop.Delete -= OnDeleteProp;
  210. prop.Select -= OnSelectProp;
  211. Object.Destroy(prop.gameObject);
  212. }
  213. private void EmitPropListChange() => PropListChange?.Invoke(this, EventArgs.Empty);
  214. private void OnBeginCallMeidos(object sender, EventArgs args)
  215. {
  216. foreach (var prop in propList.Where(p => p.AttachPointInfo.AttachPoint != AttachPoint.None))
  217. prop.DetachTemporary();
  218. }
  219. private void OnEndCallMeidos(object sender, EventArgs args)
  220. {
  221. foreach (var prop in propList.Where(p => p.AttachPointInfo.AttachPoint != AttachPoint.None))
  222. {
  223. var info = prop.AttachPointInfo;
  224. var meido = meidoManager.GetMeido(info.MaidGuid);
  225. prop.AttachTo(meido, info.AttachPoint, meido == null);
  226. }
  227. }
  228. private void OnDeleteProp(object sender, EventArgs args)
  229. => RemoveProp(propList.IndexOf((DragPointProp) sender));
  230. private void OnSelectProp(object sender, EventArgs args)
  231. {
  232. CurrentPropIndex = propList.IndexOf((DragPointProp) sender);
  233. FromPropSelect?.Invoke(this, EventArgs.Empty);
  234. }
  235. private void OnCubeSmall(object sender, EventArgs args)
  236. {
  237. foreach (var dragPoint in propList) dragPoint.DragPointScale = CubeSmall ? DragPointGeneral.smallCube : 1f;
  238. }
  239. private void OnCubeActive(object sender, EventArgs args)
  240. {
  241. foreach (var dragPoint in propList) dragPoint.gameObject.SetActive(CubeActive);
  242. }
  243. public void Update() { }
  244. public void Activate()
  245. {
  246. CubeSmallChange += OnCubeSmall;
  247. CubeActiveChange += OnCubeActive;
  248. }
  249. public void Deactivate()
  250. {
  251. DeleteAllProps();
  252. CubeSmallChange -= OnCubeSmall;
  253. CubeActiveChange -= OnCubeActive;
  254. }
  255. public void Serialize(BinaryWriter binaryWriter) => Utility.LogMessage("no prop serialization :(");
  256. public void Deserialize(BinaryReader binaryReader) => Utility.LogMessage("no prop deserialization :(");
  257. }
  258. }