Menu.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. namespace OpenVIII
  8. {
  9. public abstract class Menu : Menu_Base
  10. {
  11. #region Fields
  12. public static Slide<float> BlinkSlider = new Slide<float>(_fadedout, _fadedin, _blinkinspeed, MathHelper.Lerp) { ReversedTime = _blinkoutspeed };
  13. public static EventHandler FadedInHandler;
  14. public static EventHandler FadedOutHandler;
  15. public static Slide<float> FadeSlider = new Slide<float>(_fadedout, _fadedin, _fadeinspeed, MathHelper.Lerp) { ReversedTime = _fadeoutspeed };
  16. public ConcurrentDictionary<Enum, Menu_Base> Data;
  17. protected Enum _mode;
  18. protected bool skipdata;
  19. private const float _fadedin = 1f;
  20. private const float _fadedout = 0f;
  21. /// <summary>
  22. /// Scale of Menu Items (Background and Cursor)
  23. /// </summary>
  24. private const float _menuitemscale = 2f;
  25. private static BattleMenus _battlemenus;
  26. private static Debug_Menu _debug_menu;
  27. //private static bool _blinkstate;
  28. //private static bool _fadeout = false;
  29. private static IGM _igm;
  30. private static IGMItems _igm_items;
  31. private static Junction _junction;
  32. private static IGMLoadSaveGame _igmLoadSaveGame;
  33. private static IGM_Lobby _igm_lobby;
  34. private static object _igm_lock = new object();
  35. private bool _backup = false;
  36. private Vector2 _size;
  37. private static MenuModule _module;
  38. #endregion Fields
  39. #region Events
  40. public event EventHandler<Enum> ModeChangeHandler;
  41. #endregion Events
  42. #region Properties
  43. public static BattleMenus BattleMenus => _battlemenus;
  44. //public Menu(Damageable damageable)
  45. //{
  46. // _damageable = damageable;
  47. // InitConstructor(); // because base() would always run first :(
  48. //}
  49. /// <summary>
  50. /// Blink works like Fade except it goes up to 1f then to 0f and back.
  51. /// </summary>
  52. public static float Blink_Amount { get; private set; } = _fadedin;
  53. /// <summary>
  54. /// Debug Menu
  55. /// </summary>
  56. public static Debug_Menu Debug_Menu => _debug_menu;
  57. /// <summary>
  58. /// Fade by default scales from 0f to 1f. Unless FadeOut then it goes from 1f to 0f.
  59. /// </summary>
  60. public static float Fade { get; private set; } = _fadedin;
  61. public static bool FadingOut => FadeSlider.Reversed;
  62. //_fadeout;
  63. /// <summary>
  64. /// In Game Menu
  65. /// </summary>
  66. public static IGM IGM => _igm;
  67. /// <summary>
  68. /// In Game Menu - Items Menu
  69. /// </summary>
  70. public static IGMItems IGMItems => _igm_items;
  71. /// <summary>
  72. /// In Game Menu - Junction Menu
  73. /// </summary>
  74. public static Junction Junction => _junction;
  75. /// <summary>
  76. /// Lobby Menu
  77. /// </summary>
  78. public static IGMLoadSaveGame IGMLoadSaveGame => _igmLoadSaveGame;
  79. /// <summary>
  80. /// Lobby Menu
  81. /// </summary>
  82. public static IGM_Lobby IGM_Lobby => _igm_lobby;
  83. public static Vector2 StaticSize { get; protected set; }
  84. /// <summary>
  85. /// Size of text the real game doesn't use a 1:1 ratio.
  86. /// </summary>
  87. public static Vector2 TextScale { get; } = new Vector2(2.545455f, 3.0375f);
  88. /// <summary>
  89. /// If true Inputs won't be called from Update(). So will need to be called sepperately.
  90. /// </summary>
  91. public bool NoInputOnUpdate { get; set; } = false;
  92. /// <summary>
  93. /// Size of the menu. If kept in a 4:3 region it won't scale down till after losing enough width.
  94. /// </summary>
  95. public Vector2 Size { get => _size; protected set => _size = value; }
  96. public bool SkipFocus { get; set; } = false;
  97. /// <summary>
  98. /// Viewport dimensions
  99. /// </summary>
  100. protected Vector2 vp => new Vector2(Memory.Graphics.GraphicsDevice.Viewport.Width, Memory.Graphics.GraphicsDevice.Viewport.Height);
  101. /// <summary>
  102. /// <para>Time to fade out in milliseconds</para>
  103. /// <para>Larger is slower</para>
  104. /// </summary>
  105. private static TimeSpan _blinkinspeed => TimeSpan.FromMilliseconds(500d);
  106. /// <summary>
  107. /// <para>Time to fade out in milliseconds</para>
  108. /// <para>Larger is slower</para>
  109. /// </summary>
  110. private static TimeSpan _blinkoutspeed => TimeSpan.FromMilliseconds(900d);
  111. /// <summary>
  112. /// <para>Time to fade out in milliseconds</para>
  113. /// <para>Larger is slower</para>
  114. /// </summary>
  115. private static TimeSpan _fadeinspeed => TimeSpan.FromMilliseconds(700d);
  116. /// <summary>
  117. /// <para>Time to fade out in milliseconds</para>
  118. /// <para>Larger is slower</para>
  119. /// </summary>
  120. private static TimeSpan _fadeoutspeed => TimeSpan.FromMilliseconds(1500d);
  121. /// <summary>
  122. /// if canceled don't init menu.
  123. /// </summary>
  124. private bool cancel => Memory.Token.IsCancellationRequested;
  125. public static MenuModule Module { get => _module; set => _module = value; }
  126. #endregion Properties
  127. #region Methods
  128. //public Menu() => InitConstructor();
  129. public static T Create<T>(Damageable damageable = null) where T : Menu, new()
  130. {
  131. Memory.Log.WriteLine($"{nameof(Menu)} :: {nameof(Create)} :: {typeof(T)} :: {nameof(Damageable)} :: {damageable}");
  132. var r = new T
  133. {
  134. Damageable = damageable,
  135. };
  136. r.InitConstructor();
  137. return r;
  138. }
  139. public static BoxReturn DrawBox(Rectangle dst, FF8String buffer = null, Icons.ID? title = null, Vector2? textScale = null, Vector2? boxScale = null, Box_Options options = Box_Options.Default)
  140. {
  141. if (textScale == null) textScale = Vector2.One;
  142. if (boxScale == null) boxScale = Vector2.One;
  143. var cursor = Point.Zero;
  144. var font = new Rectangle();
  145. var backup = dst;
  146. if (buffer != null && buffer.Length > 0)
  147. {
  148. font = Memory.Font.RenderBasicText(buffer, dst.Location.ToVector2(), TextScale * textScale.Value, Fade: Fade, skipdraw: true);
  149. if (dst.Size == Point.Zero)
  150. {
  151. dst.Size = font.Size;
  152. if (title == null)
  153. {
  154. dst.Inflate(20, 10);
  155. }
  156. else
  157. dst.Inflate(20, 30);
  158. dst.Location = backup.Location;
  159. backup = dst;
  160. }
  161. }
  162. var bgscale = new Vector2(_menuitemscale) * textScale.Value;
  163. var box = dst.Scale(boxScale.Value);
  164. var hotspot = dst;
  165. if ((options & Box_Options.SkipDraw) == 0 && dst.Size != Point.Zero)
  166. {
  167. if (dst.Width > 256 * bgscale.X)
  168. Memory.Icons.Draw(Icons.ID.Menu_BG_368, 0, box, bgscale, Fade);
  169. else
  170. Memory.Icons.Draw(Icons.ID.Menu_BG_256, 0, box, bgscale, Fade);
  171. if (title != null)
  172. {
  173. dst.Offset(15, 0);
  174. dst.Y = (int)(dst.Y * boxScale.Value.Y);
  175. Memory.Icons.Draw(title.Value, 2, dst, (bgscale + new Vector2(.5f)), Fade);
  176. }
  177. dst = backup;
  178. }
  179. if (buffer != null && buffer.Length > 0)
  180. {
  181. //font = Memory.font.RenderBasicText(buffer, dst.Location.ToVector2(), TextScale * textScale.Value, Fade: Fade, skipdraw: true);
  182. if ((options & Box_Options.Indent) != 0)
  183. dst.Offset(70 * textScale.Value.X, 0);
  184. else if ((options & Box_Options.Center) != 0)
  185. dst.Offset(dst.Width / 2 - font.Width / 2, 0);
  186. else if ((options & Box_Options.Right) != 0)
  187. dst.Offset(dst.Width - font.Width - 5 * textScale.Value.X, 0);
  188. else
  189. dst.Offset(25 * textScale.Value.X, 0);
  190. if ((options & Box_Options.Buttom) != 0)
  191. dst.Offset(0, (dst.Height - 48));
  192. else if ((options & Box_Options.Middle) != 0)
  193. dst.Offset(0, dst.Height / 2 - font.Height / 2 + 2);
  194. else
  195. dst.Offset(0, 21);
  196. dst.Y = (int)(dst.Y * boxScale.Value.Y);
  197. font = Memory.Font.RenderBasicText(buffer, dst.Location.ToVector2(), TextScale * textScale.Value, Fade: Fade, skipdraw: (options & Box_Options.SkipDraw) != 0);
  198. cursor = dst.Location;
  199. cursor.Y += (int)(TextScale.Y * 6); // 12 * (3.0375/2)
  200. }
  201. return new BoxReturn(hotspot, cursor, font);
  202. }
  203. public static void DrawPointer(Point cursor, Vector2? offset = null, bool blink = false)
  204. {
  205. if (offset == null)
  206. offset = new Vector2(-1.15f, -.3f);
  207. var scale = new Vector2(_menuitemscale);
  208. var Finger_Right = Memory.Icons.GetEntry(Icons.ID.Finger_Right, 0);
  209. if (Finger_Right != null)
  210. {
  211. var size = Finger_Right.Size * scale;
  212. var dst = new Rectangle(cursor, Point.Zero);
  213. byte pallet = 2;
  214. byte fadedpallet = 7;
  215. dst.Offset(size * offset.Value);
  216. Memory.Icons.Trim(Icons.ID.Finger_Right, pallet);
  217. if (blink)
  218. {
  219. Memory.Icons.Draw(Icons.ID.Finger_Right, fadedpallet, dst, scale, Fade);
  220. }
  221. Memory.Icons.Draw(Icons.ID.Finger_Right, pallet, dst, scale, blink ? Fade * Blink_Amount : Fade);
  222. }
  223. }
  224. public static void FadeIn()
  225. {
  226. if (FadeSlider.Reversed)
  227. FadeSlider.ReverseRestart();
  228. else
  229. FadeSlider.Restart();
  230. //Fade = _fadedout;
  231. //_fadeout = false;
  232. }
  233. public static void FadeOut()
  234. {
  235. if (!FadeSlider.Reversed)
  236. FadeSlider.ReverseRestart();
  237. else
  238. FadeSlider.Restart();
  239. //Fade = _fadedin;
  240. //_fadeout = true;
  241. }
  242. public static void InitStaticMembers()
  243. {
  244. Memory.Log.WriteLine($"{nameof(Menu)} :: {nameof(InitStaticMembers)}");
  245. lock (_igm_lock)
  246. {
  247. if (_igm_lobby == null)
  248. _igm_lobby = IGM_Lobby.Create();
  249. if (_igm == null)
  250. _igm = IGM.Create();
  251. if (_junction == null)
  252. _junction = Junction.Create();
  253. if (_igm_items == null)
  254. _igm_items = IGMItems.Create();
  255. if (_battlemenus == null)
  256. _battlemenus = BattleMenus.Create();
  257. if (_igmLoadSaveGame == null)
  258. _igmLoadSaveGame = IGMLoadSaveGame.Create();
  259. if (_debug_menu == null)
  260. _debug_menu = Debug_Menu.Create();
  261. if (_module == null)
  262. _module = MenuModule.Create();
  263. FadeIn();
  264. }
  265. }
  266. public static void UpdateOnce() => UpdateFade(null);
  267. public override void Draw()
  268. {
  269. StartDraw();
  270. DrawData();
  271. EndDraw();
  272. }
  273. public virtual void DrawData()
  274. {
  275. if (!skipdata && Enabled && Data != null)
  276. foreach (var i in Data.Where(x => x.Value != null && x.Value.Enabled).OrderBy(x => x.Key).Select(x => x.Value))
  277. i?.Draw();
  278. }
  279. public virtual void EndDraw()
  280. {
  281. if (Enabled)
  282. Memory.SpriteBatchEnd();
  283. }
  284. public virtual Enum GetMode() => _mode;
  285. public override bool Inputs() => false;
  286. public override void Refresh()
  287. {
  288. Backup();
  289. base.Refresh();
  290. }
  291. public void Refresh(bool backup) => Refresh(Damageable, backup);
  292. public override void Refresh(Damageable damageable) => Refresh(damageable, false);
  293. public virtual void Refresh(Damageable damageable, bool backup)
  294. {
  295. _backup = backup;
  296. base.Refresh(damageable);
  297. }
  298. public override void Reset()
  299. {
  300. if (!skipdata)
  301. foreach (var i in Data)
  302. {
  303. i.Value?.Reset();
  304. }
  305. base.Reset();
  306. }
  307. public virtual bool SetMode(Enum mode)
  308. {
  309. if (!mode.Equals(_mode))
  310. {
  311. _mode = mode;
  312. ModeChangeHandler?.Invoke(this, mode);
  313. return true;
  314. }
  315. return false;
  316. }
  317. public virtual void StartDraw()
  318. {
  319. if (Enabled)
  320. Memory.SpriteBatchStartAlpha(ss: SamplerState.PointClamp, tm: Focus);
  321. }
  322. public override bool Update()
  323. {
  324. var ret = false;
  325. if (!SkipFocus)
  326. GenerateFocus();
  327. if(Size != Vector2.Zero)
  328. StaticSize = Size;
  329. if (Enabled)
  330. {
  331. //todo detect when there is no saves detected.
  332. //check for null
  333. //if (!skipdata && Data != null)
  334. //{
  335. // ret = ((from i in Data
  336. // where i.Value != null && i.Value.Update()
  337. // select new { isTrue = true }).FirstOrDefault()?.isTrue ?? false) || ret;
  338. //}
  339. foreach (var i in Data.Where(x => x.Value != null))
  340. {
  341. ret = (i.Value.Update()) || ret;
  342. }
  343. }
  344. if (!NoInputOnUpdate)
  345. return Inputs() || ret;
  346. else return ret;
  347. }
  348. protected void GenerateFocus(Vector2? inputsize = null, Box_Options options = Box_Options.Default)
  349. {
  350. var size = inputsize ?? Size;
  351. var Zoom = Memory.Scale(size.X, size.Y, ScaleMode.FitBoth);
  352. var OffsetScreen = size/2f;
  353. var CenterOfScreen = new Vector2(vp.X, vp.Y ) / 2f;
  354. if ((options & Box_Options.Top) != 0)
  355. {
  356. CenterOfScreen.Y = 0;
  357. OffsetScreen.Y = 0;
  358. }
  359. else if ((options & Box_Options.Buttom) != 0)
  360. {
  361. CenterOfScreen.Y = vp.Y - (OffsetScreen.Y * 2 * Zoom.Y);
  362. OffsetScreen.Y = 0;
  363. }
  364. Focus = Matrix.CreateTranslation(-OffsetScreen.X, -OffsetScreen.Y, 0) *
  365. Matrix.CreateScale(new Vector3(Zoom.X, Zoom.Y, 1)) *
  366. Matrix.CreateTranslation(CenterOfScreen.X, CenterOfScreen.Y, 0);
  367. }
  368. protected override void Init()
  369. {
  370. }
  371. protected override void RefreshChild()
  372. {
  373. if (!skipdata)
  374. foreach (var i in Data.Where(x=>x.Value !=null))
  375. // children might have a damageable set.
  376. // if parents may not always have one set.
  377. {
  378. if (ForceNullDamageable)
  379. i.Value.ForceNullDamageable = ForceNullDamageable;
  380. i.Value.Refresh(Damageable);
  381. }
  382. ForceNullDamageable = false;
  383. }
  384. private static void UpdateFade(object sender = null)
  385. {
  386. if (!BlinkSlider.Done)
  387. {
  388. Blink_Amount = BlinkSlider.Update();
  389. if (BlinkSlider.Done)
  390. {
  391. BlinkSlider.Reverse();
  392. BlinkSlider.Restart();
  393. }
  394. }
  395. if (!FadeSlider.Done)
  396. {
  397. Fade = FadeSlider.Update();
  398. if (FadeSlider.Reversed)
  399. {
  400. if (FadeSlider.Done)
  401. {
  402. FadedOutHandler?.Invoke(sender, null);
  403. FadeSlider.ReverseRestart();
  404. }
  405. }
  406. else
  407. {
  408. Fade = FadeSlider.Update();
  409. if (FadeSlider.Done)
  410. {
  411. FadedInHandler?.Invoke(sender, null);
  412. }
  413. }
  414. }
  415. }
  416. private void Backup()
  417. {
  418. //backup memory
  419. if (_backup)
  420. Memory.PrevState = Memory.State.Clone();
  421. _backup = false;
  422. }
  423. private void InitConstructor()
  424. {
  425. //WaitForInit();
  426. if (!cancel)
  427. {
  428. Data = new ConcurrentDictionary<Enum, Menu_Base>();
  429. Init();
  430. skipdata = true;
  431. Refresh();
  432. skipdata = false;
  433. }
  434. }
  435. #endregion Methods
  436. #region Structs
  437. public struct BoxReturn
  438. {
  439. #region Fields
  440. public Point Cursor;
  441. public Rectangle Font;
  442. public Rectangle HotSpot;
  443. #endregion Fields
  444. #region Constructors
  445. public BoxReturn(Rectangle hotSpot, Point cursor, Rectangle font)
  446. {
  447. HotSpot = hotSpot;
  448. Cursor = cursor;
  449. Font = font;
  450. }
  451. #endregion Constructors
  452. }
  453. #endregion Structs
  454. }
  455. }