using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using OpenVIII.Encoding.Tags; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace OpenVIII { /// /// Menu holds a menu for each character. /// public partial class BattleMenus : Menu { #region Fields private Dictionary DrawActions; private Dictionary> InputFunctions; private MODULE lastgamestate; private MenuModule.Mode lastmenu; private ushort lastmusic; private bool lastmusicplaying; private Dictionary ReturnAction; private Dictionary> UpdateFunctions; #endregion Fields #region Enums public enum Mode : byte { /// /// Spawning character's and enemies and flying the camera around /// Starting, /// /// running atb and using battle menus. /// Battle, /// /// Fade out and goto victory menu. /// Victory, /// /// Fade out and goto game over screen. /// GameOver, } public enum SectionName { /// /// Party Member Menu /// Party1, /// /// Party Member Menu /// Party2, /// /// Party Member Menu /// Party3, /// /// Debug Enemy Menu /// Enemy1, /// /// Debug Enemy Menu /// Enemy2, /// /// Debug Enemy Menu /// Enemy3, /// /// Debug Enemy Menu /// Enemy4, /// /// Debug Enemy Menu /// Enemy5, /// /// Debug Enemy Menu /// Enemy6, /// /// Debug Enemy Menu /// Enemy7, /// /// Debug Enemy Menu /// Enemy8, /// /// Victory Menu /// Victory, } #endregion Enums #region Properties protected byte Player { get; set; } = 0; /// /// Get Damageable from active battle menu; /// /// public Damageable GetDamageable() => GetCurrentBattleMenu()?.Damageable; public new sbyte? PartyPos { get { BattleMenu bm = GetCurrentBattleMenu(); if (bm?.Damageable?.GetBattleMode().Equals(Damageable.BattleMode.YourTurn) ?? false) { return bm.PartyPos; } return null; } } public VictoryMenu Victory_Menu { get => (VictoryMenu)(Data[SectionName.Victory]); protected set => Data[SectionName.Victory] = value; } #endregion Properties #region Methods public static BattleMenus Create() => Create(); /// /// Save pre battle state. /// public void CameFrom() { lastmenu = Menu.Module.State; lastgamestate = Memory.Module; lastmusic = Memory.MusicIndex; lastmusicplaying = init_debugger_Audio.MusicPlaying; } public override void Draw() { if (GetMode() != null && DrawActions != null) { if (DrawActions.ContainsKey((Mode)GetMode())) DrawActions[(Mode)GetMode()](); } } public IEnumerable GetBattleMenus() => Data?.Where(m => m.Value.GetType().Equals(typeof(BattleMenu)) && m.Value.Damageable != null).Select(x => (BattleMenu)x.Value); public BattleMenu GetCurrentBattleMenu() => (BattleMenu)Data?[PossibleValidPlayer()]; public override bool Inputs() { bool ret = false; InputMouse.Mode = MouseLockMode.Screen; Memory.IsMouseVisible = true; if (InputFunctions?.ContainsKey((Mode)GetMode()) ?? false) ret = InputFunctions[(Mode)GetMode()]() && ret; // press 6 to force victory if (Input2.DelayedButton(Keys.D6)) { TriggerVictory(); } // press 7 to force game over else if (Input2.DelayedButton(Keys.D7)) { SetMode(Mode.GameOver); } // press 9 to go back to last state. else if (Input2.DelayedButton(Keys.D9)) ReturnTo(); return ret; } public override void Refresh() { SetParty(); SetEnemyParty(); SetMode(Mode.Battle); // exp, items and ap you are going to get after the battle is over. base.Refresh(); } private void InitDictionarys() { if (UpdateFunctions == null) UpdateFunctions = new Dictionary>() { {Mode.Starting, UpdateStartingFunction}, {Mode.Battle, UpdateBattleFunction}, {Mode.Victory, UpdateVictoryFunction}, {Mode.GameOver, UpdateGameOverFunction}, }; if (DrawActions == null) DrawActions = new Dictionary() { {Mode.Starting, DrawStartingAction}, {Mode.Battle, DrawBattleAction}, {Mode.Victory, DrawVictoryAction}, {Mode.GameOver, DrawGameOverAction}, }; if (InputFunctions == null) InputFunctions = new Dictionary>() { //{Mode.Starting, InputStartingFunction}, {Mode.Battle, InputBattleFunction}, {Mode.Victory, InputVictoryFunction}, //{Mode.GameOver, InputGameOverFunction}, }; if (ReturnAction == null) ReturnAction = new Dictionary() { {Mode.Starting, ReturnStartingFunction}, {Mode.Battle, ReturnBattleFunction}, {Mode.Victory, ReturnVictoryFunction}, {Mode.GameOver, ReturnGameOverFunction}, }; } private void SetEnemyParty() { if (Enemy.Party != null && Enemy.Party.Count > 0) { byte i = 0; foreach (Enemy e in Enemy.Party) { Data[SectionName.Enemy1 + i].SetDamageable(e); Data[SectionName.Enemy1 + i].Show(); i++; } for (; i < 8; i++) { Data[SectionName.Enemy1 + i].SetDamageable(null, forcenull: true); Data[SectionName.Enemy1 + i].Hide(); } } } private void SetParty() { if (Memory.State?.Characters != null && Memory.State.Characters.Count > 0 && Memory.State.Party != null) { byte i = 0; IEnumerable> party = Memory.State.Party.Select((element, index) => new { element, index }).ToDictionary(m => m.index, m => m.element).Where(m => !m.Value.Equals(Characters.Blank)); int count = party.Count(); foreach (KeyValuePair m in party) { Data[SectionName.Party1 + i].SetDamageable(Memory.State[Memory.State.PartyData[m.Key]]); Data[SectionName.Party1 + i].Show(); i++; } for (; i <= (int)SectionName.Party3; i++) { Data[SectionName.Party1 + i].SetDamageable(null, forcenull: true); Data[SectionName.Party1 + i].Hide(); } } } /// /// Go back to pre battle state. /// public void ReturnTo() { Menu.Module.State = lastmenu; Memory.Module = lastgamestate; Menu.IGM.Refresh(); // else the menu stats won't update. Module_battle_debug.ResetState(); if (lastmusicplaying) init_debugger_Audio.PlayMusic(lastmusic); else init_debugger_Audio.StopMusic(); } public override bool SetMode(Enum mode) { if (!(base.GetMode()?.Equals(mode) ?? false)) return base.SetMode(mode); return false; } public override bool Update() { bool ret = false; if (GetMode() != null) { if (UpdateFunctions != null && UpdateFunctions.TryGetValue((Mode)GetMode(), out Func u)) { ret = u(); } SkipFocus = true; skipdata = true; ret = base.Update() || ret; skipdata = false; } return ret; } protected override void Init() { NoInputOnUpdate = true; Size = new Vector2 { X = 881, Y = 636 }; base.Init(); Data.TryAdd(SectionName.Party1, BattleMenu.Create(null)); Data.TryAdd(SectionName.Party2, BattleMenu.Create(null)); Data.TryAdd(SectionName.Party3, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy1, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy2, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy3, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy4, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy5, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy6, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy7, BattleMenu.Create(null)); Data.TryAdd(SectionName.Enemy8, BattleMenu.Create(null)); Data.TryAdd(SectionName.Victory, VictoryMenu.Create()); Data.ForEach(x => x.Value.Hide()); InitDictionarys(); } private bool BoolBattleMenu() => Data?.Any(m => m.Value.GetType().Equals(typeof(BattleMenu)) && m.Value.Enabled) ?? false; private bool BoolRenzokeken() => GetBattleMenus()?.Any(m => m.Enabled && (m.Renzokeken?.Enabled ?? false)) ?? false; private bool BoolShot() => GetBattleMenus()?.Any(m => m.Enabled && (m.Shot?.Enabled ?? false)) ?? false; public override void StartDraw() { //if (BoolShot()) // GetOneShot().Shot.DrawCrosshair(); base.StartDraw(); } private void DrawBattleAction() { StartDraw(); //Had to split up the HP and Commands drawing. So that Commands would draw over HP. if (BoolRenzokeken()) GetOneRenzokeken().DrawData(BattleMenu.SectionName.Renzokeken); else if (BoolShot()) GetOneShot().DrawData(BattleMenu.SectionName.Shot); else if (BoolBattleMenu()) { GetBattleMenus().ForEach(m => m.DrawData(BattleMenu.SectionName.HP)); GetBattleMenus().ForEach(m => m.DrawData(BattleMenu.SectionName.Commands)); } //DrawData(); EndDraw(); } private void DrawGameOverAction() { } private void DrawStartingAction() { } private void DrawVictoryAction() => Victory_Menu.Draw(); private BattleMenu GetOneRenzokeken() => GetBattleMenus()?.First(m => m.Enabled && m.Renzokeken.Enabled); private BattleMenu GetOneShot() => GetBattleMenus()?.First(m => m.Enabled && m.Shot.Enabled); private bool InputBattleFunction() { bool ret = false; if (BoolRenzokeken()) { return GetOneRenzokeken().Inputs(); } foreach (BattleMenu m in GetBattleMenus().Where(m => m.Damageable.GetBattleMode().Equals(Damageable.BattleMode.YourTurn))) { ret = m.Inputs() || ret; if (ret) return ret; } if (Input2.DelayedButton(FF8TextTagKey.Cancel)) { if (GetCurrentBattleMenu().Damageable?.Switch() ?? true) { int cnt = 0; do { if (++Player > (int)SectionName.Enemy8) Player = 0; if (++cnt > (int)SectionName.Enemy8 * 2) return false; } while (Data.Count <= Player || Data[PossibleValidPlayer()]?.Damageable == null || Data[PossibleValidPlayer()].GetType() != typeof(BattleMenu) || !GetCurrentBattleMenu().Damageable.StartTurn()); NewTurnSND(); } } return ret; } private bool InputGameOverFunction() => false; private bool InputStartingFunction() => false; private bool InputVictoryFunction() => Victory_Menu?.Inputs() ?? false; private void NewTurnSND() { if (((BattleMenu)Data[PossibleValidPlayer()]).CrisisLevel > -1) init_debugger_Audio.PlaySound(94); else init_debugger_Audio.PlaySound(14); } private SectionName PossibleValidPlayer() => SectionName.Party1 + MathHelper.Clamp(Player, 0, (int)SectionName.Enemy8); private void ReturnBattleFunction() { } private void ReturnGameOverFunction() { } private void ReturnStartingFunction() { } private void ReturnVictoryFunction() { } private void TriggerVictory(ConcurrentDictionary expextra = null) { int exp = 0; uint ap = 0; ConcurrentDictionary items = new ConcurrentDictionary(); ConcurrentDictionary cards = new ConcurrentDictionary(); foreach (Enemy e in Enemy.Party) { exp += e.EXP; ap += e.AP; Saves.Item drop = e.Drop(Memory.State.PartyHasAbility(Kernel_bin.Abilities.RareItem)); Cards.ID carddrop = e.CardDrop(); if (drop.QTY > 0 && drop.ID > 0) if (!items.TryAdd(drop.ID, drop.QTY)) items[drop.ID] += drop.QTY; if (carddrop != Cards.ID.Fail && carddrop != Cards.ID.Immune) if (!cards.TryAdd(carddrop, 1)) cards[carddrop]++; } SetMode(Mode.Victory); Victory_Menu?.Refresh(exp, ap, expextra, items, cards); Victory_Menu?.Show(); } private bool UpdateBattleFunction() { bool ret = false; List bml = GetBattleMenus().ToList(); if (bml.Count == 0) return false;// issue here. foreach (BattleMenu m in bml) { ret = m.Update() || ret; } if (!(GetCurrentBattleMenu()?.Damageable?.GetBattleMode().Equals(Damageable.BattleMode.YourTurn) ?? false)) { int cnt = bml.Count; if (Player + 1 == cnt) Player = 0; for (byte i = Player; cnt > 0; cnt--) { if (i < bml.Count && bml[i].Damageable.StartTurn()) { Player = i; NewTurnSND(); break; } i++; if (i >= bml.Count) i = 0; } } return ret; } private bool UpdateGameOverFunction() { Memory.Module = MODULE.FIELD_DEBUG; Memory.FieldHolder.FieldID = 75; //gover init_debugger_Audio.PlayMusic(0); Menu.Module.State = MenuModule.Mode.MainLobby; return true; } private bool UpdateStartingFunction() => throw new NotImplementedException(); private bool UpdateVictoryFunction() { init_debugger_Audio.PlayMusic(1); return Victory_Menu.Update(); } #endregion Methods } }