MeidoPhotoStudio.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. using System.IO;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.SceneManagement;
  6. using Ionic.Zlib;
  7. using BepInEx;
  8. namespace COM3D2.MeidoPhotoStudio.Plugin
  9. {
  10. [BepInPlugin(pluginGuid, pluginName, pluginVersion)]
  11. public class MeidoPhotoStudio : BaseUnityPlugin
  12. {
  13. private static CameraMain mainCamera = GameMain.Instance.MainCamera;
  14. private static event EventHandler<ScreenshotEventArgs> ScreenshotEvent;
  15. private const string pluginGuid = "com.habeebweeb.com3d2.meidophotostudio";
  16. public const string pluginName = "MeidoPhotoStudio";
  17. public const string pluginVersion = "0.0.0";
  18. public const int sceneVersion = 1000;
  19. public static string pluginString = $"{pluginName} {pluginVersion}";
  20. private WindowManager windowManager;
  21. private MeidoManager meidoManager;
  22. private EnvironmentManager environmentManager;
  23. private MessageWindowManager messageWindowManager;
  24. private LightManager lightManager;
  25. private PropManager propManager;
  26. private EffectManager effectManager;
  27. private Constants.Scene currentScene;
  28. private bool initialized = false;
  29. private bool active = false;
  30. private bool uiActive = false;
  31. private void Awake()
  32. {
  33. ScreenshotEvent += OnScreenshotEvent;
  34. DontDestroyOnLoad(this);
  35. }
  36. private void Start()
  37. {
  38. Constants.Initialize();
  39. Translation.Initialize(Configuration.CurrentLanguage);
  40. SceneManager.sceneLoaded += OnSceneLoaded;
  41. }
  42. public void Serialize(bool quickSave)
  43. {
  44. string sceneName = quickSave ? "mpstempscene" : $"mpsscene{System.DateTime.Now:yyyyMMddHHmmss}.scene";
  45. string scenePath = Path.Combine(Constants.scenesPath, sceneName);
  46. File.WriteAllBytes(scenePath, Serialize());
  47. }
  48. public byte[] Serialize()
  49. {
  50. if (meidoManager.Busy) return null;
  51. MemoryStream memoryStream;
  52. using (memoryStream = new MemoryStream())
  53. using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress))
  54. using (BinaryWriter binaryWriter = new BinaryWriter(deflateStream, System.Text.Encoding.UTF8))
  55. {
  56. binaryWriter.Write("MPS_SCENE");
  57. binaryWriter.Write(sceneVersion);
  58. messageWindowManager.Serialize(binaryWriter);
  59. effectManager.Serialize(binaryWriter);
  60. environmentManager.Serialize(binaryWriter);
  61. lightManager.Serialize(binaryWriter);
  62. // meidomanager has to be serialized before propmanager because reattached props will be in the wrong
  63. // place after a maid's pose is deserialized.
  64. meidoManager.Serialize(binaryWriter);
  65. propManager.Serialize(binaryWriter);
  66. binaryWriter.Write("END");
  67. }
  68. return memoryStream.ToArray();
  69. }
  70. public void Deserialize()
  71. {
  72. string path = Path.Combine(Constants.scenesPath, "mpstempscene");
  73. Deserialize(path);
  74. }
  75. public void Deserialize(string filePath)
  76. {
  77. if (meidoManager.Busy) return;
  78. byte[] data = DeflateStream.UncompressBuffer(File.ReadAllBytes(filePath));
  79. using (MemoryStream memoryStream = new MemoryStream(data))
  80. using (BinaryReader binaryReader = new BinaryReader(memoryStream, System.Text.Encoding.UTF8))
  81. {
  82. try
  83. {
  84. if (binaryReader.ReadString() != "MPS_SCENE") return;
  85. if (binaryReader.ReadInt32() > sceneVersion)
  86. {
  87. Utility.LogWarning($"'{filePath}' is made in a newer version of {pluginName}");
  88. return;
  89. }
  90. string previousHeader = string.Empty;
  91. string header;
  92. while ((header = binaryReader.ReadString()) != "END")
  93. {
  94. switch (header)
  95. {
  96. case MessageWindowManager.header:
  97. messageWindowManager.Deserialize(binaryReader);
  98. break;
  99. case EnvironmentManager.header:
  100. environmentManager.Deserialize(binaryReader);
  101. break;
  102. case MeidoManager.header:
  103. meidoManager.Deserialize(binaryReader);
  104. break;
  105. case PropManager.header:
  106. propManager.Deserialize(binaryReader);
  107. break;
  108. case LightManager.header:
  109. lightManager.Deserialize(binaryReader);
  110. break;
  111. case EffectManager.header:
  112. effectManager.Deserialize(binaryReader);
  113. break;
  114. default: throw new System.Exception($"Unknown header '{header}'. Last {previousHeader}");
  115. }
  116. previousHeader = header;
  117. }
  118. }
  119. catch (System.Exception e)
  120. {
  121. Utility.LogError($"Failed to deserialize scene '{filePath}' because {e.Message}");
  122. return;
  123. }
  124. }
  125. }
  126. public static void TakeScreenshot(ScreenshotEventArgs args) => ScreenshotEvent?.Invoke(null, args);
  127. public static void TakeScreenshot(string path = "", int superSize = -1, bool hideMaids = false)
  128. {
  129. TakeScreenshot(new ScreenshotEventArgs() { Path = path, SuperSize = superSize, HideMaids = hideMaids });
  130. }
  131. private void OnScreenshotEvent(object sender, ScreenshotEventArgs args)
  132. {
  133. StartCoroutine(Screenshot(args));
  134. }
  135. private void Update()
  136. {
  137. if (currentScene == Constants.Scene.Daily)
  138. {
  139. if (Input.GetKeyDown(KeyCode.F6))
  140. {
  141. if (active) Deactivate();
  142. else Activate();
  143. }
  144. if (active)
  145. {
  146. if (Utility.GetModKey(Utility.ModKey.Control))
  147. {
  148. if (Input.GetKeyDown(KeyCode.S)) Serialize(true);
  149. else if (Input.GetKeyDown(KeyCode.A)) Deserialize();
  150. }
  151. else if (!Input.GetKey(KeyCode.Q) && Input.GetKeyDown(KeyCode.S))
  152. {
  153. TakeScreenshot();
  154. }
  155. meidoManager.Update();
  156. environmentManager.Update();
  157. windowManager.Update();
  158. effectManager.Update();
  159. }
  160. }
  161. }
  162. private IEnumerator Screenshot(ScreenshotEventArgs args)
  163. {
  164. // Hide UI and dragpoints
  165. GameObject editUI = GameObject.Find("/UI Root/Camera");
  166. GameObject fpsViewer =
  167. UTY.GetChildObject(GameMain.Instance.gameObject, "SystemUI Root/FpsCounter", false);
  168. GameObject sysDialog =
  169. UTY.GetChildObject(GameMain.Instance.gameObject, "SystemUI Root/SystemDialog", false);
  170. GameObject sysShortcut =
  171. UTY.GetChildObject(GameMain.Instance.gameObject, "SystemUI Root/SystemShortcut", false);
  172. editUI.SetActive(false);
  173. fpsViewer.SetActive(false);
  174. sysDialog.SetActive(false);
  175. sysShortcut.SetActive(false);
  176. uiActive = false;
  177. // TODO: Hide cubes for bg, maid, lights etc.
  178. List<Meido> activeMeidoList = this.meidoManager.ActiveMeidoList;
  179. bool[] isIK = new bool[activeMeidoList.Count];
  180. bool[] isVisible = new bool[activeMeidoList.Count];
  181. for (int i = 0; i < activeMeidoList.Count; i++)
  182. {
  183. Meido meido = activeMeidoList[i];
  184. isIK[i] = meido.IK;
  185. isVisible[i] = meido.Maid.Visible;
  186. if (meido.IK) meido.IK = false;
  187. if (args.HideMaids) meido.Maid.Visible = false;
  188. }
  189. GizmoRender.UIVisible = false;
  190. yield return new WaitForEndOfFrame();
  191. // Take Screenshot
  192. int[] defaultSuperSize = new[] { 1, 2, 4 };
  193. int selectedSuperSize = args.SuperSize < 1
  194. ? defaultSuperSize[(int)GameMain.Instance.CMSystem.ScreenShotSuperSize]
  195. : args.SuperSize;
  196. string path = string.IsNullOrEmpty(args.Path)
  197. ? Utility.ScreenshotFilename()
  198. : args.Path;
  199. Application.CaptureScreenshot(path, selectedSuperSize);
  200. GameMain.Instance.SoundMgr.PlaySe("se022.ogg", false);
  201. yield return new WaitForEndOfFrame();
  202. // Show UI and dragpoints
  203. uiActive = true;
  204. editUI.SetActive(true);
  205. fpsViewer.SetActive(GameMain.Instance.CMSystem.ViewFps);
  206. sysDialog.SetActive(true);
  207. sysShortcut.SetActive(true);
  208. for (int i = 0; i < activeMeidoList.Count; i++)
  209. {
  210. Meido meido = activeMeidoList[i];
  211. if (isIK[i]) meido.IK = true;
  212. if (args.HideMaids && isVisible[i]) meido.Maid.Visible = true;
  213. }
  214. GizmoRender.UIVisible = true;
  215. }
  216. private void OnGUI()
  217. {
  218. if (uiActive)
  219. {
  220. windowManager.DrawWindows();
  221. if (DropdownHelper.Visible) DropdownHelper.HandleDropdown();
  222. }
  223. }
  224. private void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode)
  225. {
  226. currentScene = (Constants.Scene)scene.buildIndex;
  227. if (active) Deactivate();
  228. ResetCalcNearClip();
  229. }
  230. private void Initialize()
  231. {
  232. if (initialized) return;
  233. initialized = true;
  234. meidoManager = new MeidoManager();
  235. environmentManager = new EnvironmentManager(meidoManager);
  236. messageWindowManager = new MessageWindowManager();
  237. lightManager = new LightManager();
  238. propManager = new PropManager(meidoManager);
  239. effectManager = new EffectManager();
  240. MaidSwitcherPane maidSwitcherPane = new MaidSwitcherPane(meidoManager);
  241. windowManager = new WindowManager()
  242. {
  243. [Constants.Window.Main] = new MainWindow(meidoManager)
  244. {
  245. [Constants.Window.Call] = new CallWindowPane(meidoManager),
  246. [Constants.Window.Pose] = new PoseWindowPane(meidoManager, maidSwitcherPane),
  247. [Constants.Window.Face] = new FaceWindowPane(meidoManager, maidSwitcherPane),
  248. [Constants.Window.BG] = new BGWindowPane(environmentManager, lightManager, effectManager),
  249. [Constants.Window.BG2] = new BG2WindowPane(meidoManager, propManager)
  250. },
  251. [Constants.Window.Message] = new MessageWindow(messageWindowManager)
  252. };
  253. meidoManager.BeginCallMeidos += (s, a) => uiActive = false;
  254. meidoManager.EndCallMeidos += (s, a) => uiActive = true;
  255. }
  256. private void Activate()
  257. {
  258. if (!initialized) Initialize();
  259. SetNearClipPlane();
  260. uiActive = true;
  261. active = true;
  262. meidoManager.Activate();
  263. environmentManager.Activate();
  264. propManager.Activate();
  265. lightManager.Activate();
  266. effectManager.Activate();
  267. windowManager.Activate();
  268. messageWindowManager.Activate();
  269. GameObject dailyPanel = GameObject.Find("UI Root").transform.Find("DailyPanel").gameObject;
  270. dailyPanel.SetActive(false);
  271. }
  272. private void Deactivate()
  273. {
  274. if (meidoManager.Busy) return;
  275. ResetCalcNearClip();
  276. uiActive = false;
  277. active = false;
  278. meidoManager.Deactivate();
  279. environmentManager.Deactivate();
  280. propManager.Deactivate();
  281. lightManager.Deactivate();
  282. effectManager.Deactivate();
  283. messageWindowManager.Deactivate();
  284. windowManager.Deactivate();
  285. GameObject dailyPanel = GameObject.Find("UI Root")?.transform.Find("DailyPanel")?.gameObject;
  286. dailyPanel?.SetActive(true);
  287. }
  288. private void SetNearClipPlane()
  289. {
  290. mainCamera.StopAllCoroutines();
  291. mainCamera.m_bCalcNearClip = false;
  292. mainCamera.camera.nearClipPlane = 0.01f;
  293. }
  294. private void ResetCalcNearClip()
  295. {
  296. if (mainCamera.m_bCalcNearClip) return;
  297. mainCamera.StopAllCoroutines();
  298. mainCamera.m_bCalcNearClip = true;
  299. mainCamera.Start();
  300. }
  301. }
  302. public class ScreenshotEventArgs : EventArgs
  303. {
  304. public string Path { get; set; } = string.Empty;
  305. public int SuperSize { get; set; } = -1;
  306. public bool HideMaids { get; set; } = false;
  307. }
  308. }