BattleMenus.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Microsoft.Xna.Framework;
  6. using Microsoft.Xna.Framework.Input;
  7. using OpenVIII.AV;
  8. using OpenVIII.Encoding.Tags;
  9. using OpenVIII.Kernel;
  10. namespace OpenVIII
  11. {
  12. /// <summary>
  13. /// Menu holds a menu for each character.
  14. /// </summary>
  15. public class BattleMenus : Menu
  16. {
  17. #region Fields
  18. private IReadOnlyDictionary<Mode, Action> _drawActions;
  19. private IReadOnlyDictionary<Mode, Func<bool>> _inputFunctions;
  20. private Module _lastGameState;
  21. private MenuModule.Mode _lastMenu;
  22. private ushort _lastMusic;
  23. private bool _lastMusicPlaying;
  24. private IReadOnlyDictionary<Mode, Action> _returnAction;
  25. private IReadOnlyDictionary<Mode, Func<bool>> _updateFunctions;
  26. #endregion Fields
  27. #region Enums
  28. public enum Mode : byte
  29. {
  30. /// <summary>
  31. /// Spawning character's and enemies and flying the camera around
  32. /// </summary>
  33. Starting,
  34. /// <summary>
  35. /// running atb and using battle menus.
  36. /// </summary>
  37. Battle,
  38. /// <summary>
  39. /// Fade out and goto victory menu.
  40. /// </summary>
  41. Victory,
  42. /// <summary>
  43. /// Fade out and goto game over screen.
  44. /// </summary>
  45. GameOver
  46. }
  47. public enum SectionName
  48. {
  49. /// <summary>
  50. /// Party Member Menu
  51. /// </summary>
  52. Party1,
  53. /// <summary>
  54. /// Party Member Menu
  55. /// </summary>
  56. Party2,
  57. /// <summary>
  58. /// Party Member Menu
  59. /// </summary>
  60. Party3,
  61. /// <summary>
  62. /// Debug Enemy Menu
  63. /// </summary>
  64. Enemy1,
  65. /// <summary>
  66. /// Debug Enemy Menu
  67. /// </summary>
  68. Enemy2,
  69. /// <summary>
  70. /// Debug Enemy Menu
  71. /// </summary>
  72. Enemy3,
  73. /// <summary>
  74. /// Debug Enemy Menu
  75. /// </summary>
  76. Enemy4,
  77. /// <summary>
  78. /// Debug Enemy Menu
  79. /// </summary>
  80. Enemy5,
  81. /// <summary>
  82. /// Debug Enemy Menu
  83. /// </summary>
  84. Enemy6,
  85. /// <summary>
  86. /// Debug Enemy Menu
  87. /// </summary>
  88. Enemy7,
  89. /// <summary>
  90. /// Debug Enemy Menu
  91. /// </summary>
  92. Enemy8,
  93. /// <summary>
  94. /// Victory Menu
  95. /// </summary>
  96. Victory
  97. }
  98. #endregion Enums
  99. #region Properties
  100. protected byte Player { get; set; }
  101. /// <summary>
  102. /// Get Damageable from active battle menu;
  103. /// </summary>
  104. /// <returns></returns>
  105. public Damageable GetDamageable() => GetCurrentBattleMenu()?.Damageable;
  106. public new sbyte? PartyPos
  107. {
  108. get
  109. {
  110. var bm = GetCurrentBattleMenu();
  111. if (bm?.Damageable?.GetBattleMode().Equals(Damageable.BattleMode.YourTurn) ?? false)
  112. {
  113. return bm.PartyPos;
  114. }
  115. return null;
  116. }
  117. }
  118. public VictoryMenu VictoryMenu { get => (VictoryMenu)(Data[SectionName.Victory]); protected set => Data[SectionName.Victory] = value; }
  119. #endregion Properties
  120. #region Methods
  121. public static BattleMenus Create() => Create<BattleMenus>();
  122. /// <summary>
  123. /// Save pre battle state.
  124. /// </summary>
  125. public void CameFrom()
  126. {
  127. _lastMenu = Module.State;
  128. _lastGameState = Memory.Module;
  129. _lastMusic = Memory.MusicIndex;
  130. _lastMusicPlaying = Music.Playing;
  131. }
  132. public override void Draw()
  133. {
  134. if (GetMode() == null || _drawActions == null) return;
  135. if (_drawActions.ContainsKey((Mode)GetMode()))
  136. _drawActions[(Mode)GetMode()]();
  137. }
  138. public IEnumerable<BattleMenu> GetBattleMenus() => Data?.Where(m => m.Value is BattleMenu && m.Value?.Damageable != null).Select(x => x.Value as BattleMenu);
  139. public BattleMenu GetCurrentBattleMenu() => (BattleMenu)Data?[PossibleValidPlayer()];
  140. public override bool Inputs()
  141. {
  142. var ret = false;
  143. InputMouse.Mode = MouseLockMode.Screen;
  144. Memory.IsMouseVisible = true;
  145. if (_inputFunctions?.ContainsKey((Mode)GetMode()) ?? false)
  146. ret = _inputFunctions[(Mode)GetMode()]();
  147. // press 6 to force victory
  148. if (Input2.DelayedButton(Keys.D6))
  149. {
  150. TriggerVictory();
  151. }
  152. // press 7 to force game over
  153. else if (Input2.DelayedButton(Keys.D7))
  154. {
  155. SetMode(Mode.GameOver);
  156. }
  157. // press 9 to go back to last state.
  158. else if (Input2.DelayedButton(Keys.D9))
  159. ReturnTo();
  160. return ret;
  161. }
  162. public override void Refresh()
  163. {
  164. SetParty();
  165. SetEnemyParty();
  166. SetMode(Mode.Battle);
  167. // exp, items and ap you are going to get after the battle is over.
  168. base.Refresh();
  169. }
  170. private void InitDictionaries()
  171. {
  172. if (_updateFunctions == null)
  173. _updateFunctions = new Dictionary<Mode, Func<bool>>
  174. {
  175. {Mode.Starting, UpdateStartingFunction},
  176. {Mode.Battle, UpdateBattleFunction},
  177. {Mode.Victory, UpdateVictoryFunction},
  178. {Mode.GameOver, UpdateGameOverFunction}
  179. };
  180. if (_drawActions == null)
  181. _drawActions = new Dictionary<Mode, Action>
  182. {
  183. {Mode.Starting, DrawStartingAction},
  184. {Mode.Battle, DrawBattleAction},
  185. {Mode.Victory, DrawVictoryAction},
  186. {Mode.GameOver, DrawGameOverAction}
  187. };
  188. if (_inputFunctions == null)
  189. _inputFunctions = new Dictionary<Mode, Func<bool>>
  190. {
  191. //{Mode.Starting, InputStartingFunction},
  192. {Mode.Battle, InputBattleFunction},
  193. {Mode.Victory, InputVictoryFunction}
  194. //{Mode.GameOver, InputGameOverFunction},
  195. };
  196. if (_returnAction == null)
  197. _returnAction = new Dictionary<Mode, Action>
  198. {
  199. {Mode.Starting, ReturnStartingFunction},
  200. {Mode.Battle, ReturnBattleFunction},
  201. {Mode.Victory, ReturnVictoryFunction},
  202. {Mode.GameOver, ReturnGameOverFunction}
  203. };
  204. }
  205. private void SetEnemyParty()
  206. {
  207. if (Enemy.Party == null || Enemy.Party.Count <= 0) return;
  208. byte i = 0;
  209. foreach (var e in Enemy.Party)
  210. {
  211. Data[SectionName.Enemy1 + i].SetDamageable(e);
  212. Data[SectionName.Enemy1 + i].Show();
  213. i++;
  214. }
  215. for (; i < 8; i++)
  216. {
  217. Data[SectionName.Enemy1 + i].SetDamageable(null, forcenull: true);
  218. Data[SectionName.Enemy1 + i].Hide();
  219. }
  220. }
  221. private void SetParty()
  222. {
  223. if (Memory.State == null || !Memory.State.Characters || Memory.State.CharactersCount <= 0 ||
  224. Memory.State.Party == null) return;
  225. var i = 0;
  226. var party = Memory.State.Party.Select((element, index) => new {element, index})
  227. .ToDictionary(m => m.index, m => m.element).Where(m => !m.Value.Equals(Characters.Blank)).ToList()
  228. .AsReadOnly();
  229. for (; i < party.Count; i++)
  230. {
  231. Data[SectionName.Party1 + i].SetDamageable(Memory.State[party[i].Value]);
  232. Data[SectionName.Party1 + i].Show();
  233. }
  234. for (; i <= (int)SectionName.Party3; i++)
  235. {
  236. Data[SectionName.Party1 + i].SetDamageable(null, forcenull: true);
  237. Data[SectionName.Party1 + i].Hide();
  238. }
  239. }
  240. /// <summary>
  241. /// Go back to pre battle state.
  242. /// </summary>
  243. public void ReturnTo()
  244. {
  245. Module.State = _lastMenu;
  246. Memory.Module = _lastGameState;
  247. IGM.Refresh(); // else the menu stats won't update.
  248. ModuleBattleDebug.ResetState();
  249. if (_lastMusicPlaying)
  250. Music.Play(_lastMusic);
  251. else
  252. Music.Stop();
  253. }
  254. public override bool SetMode(Enum mode)
  255. {
  256. if (!(base.GetMode()?.Equals(mode) ?? false))
  257. return base.SetMode(mode);
  258. return false;
  259. }
  260. public override bool Update()
  261. {
  262. var ret = false;
  263. if (GetMode() == null) return false;
  264. if (_updateFunctions != null && _updateFunctions.TryGetValue((Mode)GetMode(), out var u))
  265. {
  266. ret = u();
  267. }
  268. SkipFocus = true;
  269. skipdata = true;
  270. ret = base.Update() || ret;
  271. skipdata = false;
  272. return ret;
  273. }
  274. protected override void Init()
  275. {
  276. NoInputOnUpdate = true;
  277. Size = new Vector2 { X = 881, Y = 636 };
  278. base.Init();
  279. Data.TryAdd(SectionName.Party1, BattleMenu.Create(null));
  280. Data.TryAdd(SectionName.Party2, BattleMenu.Create(null));
  281. Data.TryAdd(SectionName.Party3, BattleMenu.Create(null));
  282. Data.TryAdd(SectionName.Enemy1, BattleMenu.Create(null));
  283. Data.TryAdd(SectionName.Enemy2, BattleMenu.Create(null));
  284. Data.TryAdd(SectionName.Enemy3, BattleMenu.Create(null));
  285. Data.TryAdd(SectionName.Enemy4, BattleMenu.Create(null));
  286. Data.TryAdd(SectionName.Enemy5, BattleMenu.Create(null));
  287. Data.TryAdd(SectionName.Enemy6, BattleMenu.Create(null));
  288. Data.TryAdd(SectionName.Enemy7, BattleMenu.Create(null));
  289. Data.TryAdd(SectionName.Enemy8, BattleMenu.Create(null));
  290. Data.TryAdd(SectionName.Victory, VictoryMenu.Create());
  291. Data.ForEach(x => x.Value.Hide());
  292. InitDictionaries();
  293. }
  294. private bool BoolBattleMenu() => Data?.Any(m => m.Value.GetType() == typeof(BattleMenu) && m.Value.Enabled) ?? false;
  295. private bool BoolRenzokuken() => GetBattleMenus()?.Any(m => m.Enabled && (m.Renzokuken?.Enabled ?? false)) ?? false;
  296. private bool BoolShot() => GetBattleMenus()?.Any(m => m.Enabled && (m.Shot?.Enabled ?? false)) ?? false;
  297. private void DrawBattleAction()
  298. {
  299. StartDraw();
  300. //Had to split up the HP and Commands drawing. So that Commands would draw over HP.
  301. if (BoolRenzokuken())
  302. GetOneRenzokuken().DrawData(BattleMenu.SectionName.Renzokuken);
  303. else if (BoolShot())
  304. GetOneShot().DrawData(BattleMenu.SectionName.Shot);
  305. else if (BoolBattleMenu())
  306. {
  307. GetBattleMenus().ForEach(m => m.DrawData(BattleMenu.SectionName.HP));
  308. GetBattleMenus().ForEach(m => m.DrawData(BattleMenu.SectionName.Commands));
  309. }
  310. //DrawData();
  311. EndDraw();
  312. }
  313. private static void DrawGameOverAction()
  314. {
  315. }
  316. private static void DrawStartingAction()
  317. {
  318. }
  319. private void DrawVictoryAction() => VictoryMenu.Draw();
  320. private BattleMenu GetOneRenzokuken() => GetBattleMenus()?.First(m => m.Enabled && m.Renzokuken.Enabled);
  321. private BattleMenu GetOneShot() => GetBattleMenus()?.First(m => m.Enabled && m.Shot.Enabled);
  322. private bool InputBattleFunction()
  323. {
  324. if (BoolRenzokuken())
  325. {
  326. return GetOneRenzokuken().Inputs();
  327. }
  328. if (GetBattleMenus().Where(m => m.Damageable.GetBattleMode().Equals(Damageable.BattleMode.YourTurn)).Select(m => m.Inputs()).Any(ret => ret))
  329. return true;
  330. if (!Input2.DelayedButton(FF8TextTagKey.Cancel)) return false;
  331. if (!(GetCurrentBattleMenu().Damageable?.Switch() ?? true)) return false;
  332. var cnt = 0;
  333. do
  334. {
  335. if (++Player > (int)SectionName.Enemy8) Player = 0;
  336. if (++cnt > (int)SectionName.Enemy8 * 2) return false;
  337. }
  338. while (Data.Count <= Player ||
  339. Data[PossibleValidPlayer()]?.Damageable == null ||
  340. Data[PossibleValidPlayer()].GetType() != typeof(BattleMenu) ||
  341. !GetCurrentBattleMenu().Damageable.StartTurn());
  342. NewTurnSND();
  343. return false;
  344. }
  345. private bool InputVictoryFunction() => VictoryMenu?.Inputs() ?? false;
  346. private void NewTurnSND()
  347. {
  348. Sound.Play(((BattleMenu) Data[PossibleValidPlayer()]).CrisisLevel > -1 ? 94 : 14);
  349. }
  350. private SectionName PossibleValidPlayer() => SectionName.Party1 + MathHelper.Clamp(Player, 0, (int)SectionName.Enemy8);
  351. private static void ReturnBattleFunction()
  352. {
  353. }
  354. private static void ReturnGameOverFunction()
  355. {
  356. }
  357. private static void ReturnStartingFunction()
  358. {
  359. }
  360. private static void ReturnVictoryFunction()
  361. {
  362. }
  363. private void TriggerVictory(ConcurrentDictionary<Characters, int> extraExp = null)
  364. {
  365. //if (extraExp == null) throw new ArgumentNullException(nameof(extraExp));
  366. var exp = 0;
  367. uint ap = 0;
  368. var items = new ConcurrentDictionary<byte, byte>();
  369. var cards = new ConcurrentDictionary<Cards.ID, byte>();
  370. foreach (var e in Enemy.Party)
  371. {
  372. exp += e.EXP;
  373. ap += e.AP;
  374. var drop = e.Drop(Memory.State.PartyHasAbility(Abilities.RareItem));
  375. var cardDrops = e.CardDrop();
  376. if (drop.QTY > 0 && drop.ID > 0)
  377. if (!items.TryAdd(drop.ID, drop.QTY))
  378. items[drop.ID] += drop.QTY;
  379. if (cardDrops == Cards.ID.Fail || cardDrops == Cards.ID.Immune) continue;
  380. if (!cards.TryAdd(cardDrops, 1))
  381. cards[cardDrops]++;
  382. }
  383. SetMode(Mode.Victory);
  384. VictoryMenu?.Refresh(exp, ap, extraExp, items, cards);
  385. VictoryMenu?.Show();
  386. }
  387. private bool UpdateBattleFunction()
  388. {
  389. var ret = false;
  390. var bml = GetBattleMenus().ToList();
  391. if (bml.Count == 0) return false;// issue here.
  392. foreach (var m in bml)
  393. {
  394. ret = m.Update() || ret;
  395. }
  396. if (!(GetCurrentBattleMenu()?.Damageable?.GetBattleMode().Equals(Damageable.BattleMode.YourTurn) ?? false))
  397. {
  398. var cnt = bml.Count;
  399. if (Player + 1 == cnt)
  400. Player = 0;
  401. for (var i = Player; cnt > 0; cnt--)
  402. {
  403. if (i < bml.Count && bml[i].Damageable.StartTurn())
  404. {
  405. Player = i;
  406. NewTurnSND();
  407. break;
  408. }
  409. i++;
  410. if (i >= bml.Count)
  411. i = 0;
  412. }
  413. }
  414. return ret;
  415. }
  416. private static bool UpdateGameOverFunction()
  417. {
  418. Memory.Module = OpenVIII.Module.FieldDebug;
  419. Memory.FieldHolder.FieldID = 75; //game over
  420. Music.Play(0);
  421. Module.State = MenuModule.Mode.MainLobby;
  422. return true;
  423. }
  424. private static bool UpdateStartingFunction() => throw new NotImplementedException();
  425. private bool UpdateVictoryFunction()
  426. {
  427. Music.Play(1);
  428. return VictoryMenu.Update();
  429. }
  430. #endregion Methods
  431. }
  432. }