using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using OpenVIII.AV;
using OpenVIII.Encoding.Tags;
using OpenVIII.Kernel;
namespace OpenVIII
{
///
/// Menu holds a menu for each character.
///
public class BattleMenus : Menu
{
#region Fields
private IReadOnlyDictionary _drawActions;
private IReadOnlyDictionary> _inputFunctions;
private Module _lastGameState;
private MenuModule.Mode _lastMenu;
private ushort _lastMusic;
private bool _lastMusicPlaying;
private IReadOnlyDictionary _returnAction;
private IReadOnlyDictionary> _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; }
///
/// Get Damageable from active battle menu;
///
///
public Damageable GetDamageable() => GetCurrentBattleMenu()?.Damageable;
public new sbyte? PartyPos
{
get
{
var bm = GetCurrentBattleMenu();
if (bm?.Damageable?.GetBattleMode().Equals(Damageable.BattleMode.YourTurn) ?? false)
{
return bm.PartyPos;
}
return null;
}
}
public VictoryMenu VictoryMenu { 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 = Module.State;
_lastGameState = Memory.Module;
_lastMusic = Memory.MusicIndex;
_lastMusicPlaying = Music.Playing;
}
public override void Draw()
{
if (GetMode() == null || _drawActions == null) return;
if (_drawActions.ContainsKey((Mode)GetMode()))
_drawActions[(Mode)GetMode()]();
}
public IEnumerable GetBattleMenus() => Data?.Where(m => m.Value is BattleMenu && m.Value?.Damageable != null).Select(x => x.Value as BattleMenu);
public BattleMenu GetCurrentBattleMenu() => (BattleMenu)Data?[PossibleValidPlayer()];
public override bool Inputs()
{
var ret = false;
InputMouse.Mode = MouseLockMode.Screen;
Memory.IsMouseVisible = true;
if (_inputFunctions?.ContainsKey((Mode)GetMode()) ?? false)
ret = _inputFunctions[(Mode)GetMode()]();
// 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 InitDictionaries()
{
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) return;
byte i = 0;
foreach (var 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 == null || !Memory.State.Characters || Memory.State.CharactersCount <= 0 ||
Memory.State.Party == null) return;
var i = 0;
var party = Memory.State.Party.Select((element, index) => new {element, index})
.ToDictionary(m => m.index, m => m.element).Where(m => !m.Value.Equals(Characters.Blank)).ToList()
.AsReadOnly();
for (; i < party.Count; i++)
{
Data[SectionName.Party1 + i].SetDamageable(Memory.State[party[i].Value]);
Data[SectionName.Party1 + i].Show();
}
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()
{
Module.State = _lastMenu;
Memory.Module = _lastGameState;
IGM.Refresh(); // else the menu stats won't update.
ModuleBattleDebug.ResetState();
if (_lastMusicPlaying)
Music.Play(_lastMusic);
else
Music.Stop();
}
public override bool SetMode(Enum mode)
{
if (!(base.GetMode()?.Equals(mode) ?? false))
return base.SetMode(mode);
return false;
}
public override bool Update()
{
var ret = false;
if (GetMode() == null) return false;
if (_updateFunctions != null && _updateFunctions.TryGetValue((Mode)GetMode(), out var 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());
InitDictionaries();
}
private bool BoolBattleMenu() => Data?.Any(m => m.Value.GetType() == typeof(BattleMenu) && m.Value.Enabled) ?? false;
private bool BoolRenzokuken() => GetBattleMenus()?.Any(m => m.Enabled && (m.Renzokuken?.Enabled ?? false)) ?? false;
private bool BoolShot() => GetBattleMenus()?.Any(m => m.Enabled && (m.Shot?.Enabled ?? false)) ?? false;
private void DrawBattleAction()
{
StartDraw();
//Had to split up the HP and Commands drawing. So that Commands would draw over HP.
if (BoolRenzokuken())
GetOneRenzokuken().DrawData(BattleMenu.SectionName.Renzokuken);
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 static void DrawGameOverAction()
{
}
private static void DrawStartingAction()
{
}
private void DrawVictoryAction() => VictoryMenu.Draw();
private BattleMenu GetOneRenzokuken() => GetBattleMenus()?.First(m => m.Enabled && m.Renzokuken.Enabled);
private BattleMenu GetOneShot() => GetBattleMenus()?.First(m => m.Enabled && m.Shot.Enabled);
private bool InputBattleFunction()
{
if (BoolRenzokuken())
{
return GetOneRenzokuken().Inputs();
}
if (GetBattleMenus().Where(m => m.Damageable.GetBattleMode().Equals(Damageable.BattleMode.YourTurn)).Select(m => m.Inputs()).Any(ret => ret))
return true;
if (!Input2.DelayedButton(FF8TextTagKey.Cancel)) return false;
if (!(GetCurrentBattleMenu().Damageable?.Switch() ?? true)) return false;
var 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 false;
}
private bool InputVictoryFunction() => VictoryMenu?.Inputs() ?? false;
private void NewTurnSND()
{
Sound.Play(((BattleMenu) Data[PossibleValidPlayer()]).CrisisLevel > -1 ? 94 : 14);
}
private SectionName PossibleValidPlayer() => SectionName.Party1 + MathHelper.Clamp(Player, 0, (int)SectionName.Enemy8);
private static void ReturnBattleFunction()
{
}
private static void ReturnGameOverFunction()
{
}
private static void ReturnStartingFunction()
{
}
private static void ReturnVictoryFunction()
{
}
private void TriggerVictory(ConcurrentDictionary extraExp = null)
{
if (extraExp == null) throw new ArgumentNullException(nameof(extraExp));
var exp = 0;
uint ap = 0;
var items = new ConcurrentDictionary();
var cards = new ConcurrentDictionary();
foreach (var e in Enemy.Party)
{
exp += e.EXP;
ap += e.AP;
var drop = e.Drop(Memory.State.PartyHasAbility(Abilities.RareItem));
var cardDrops = e.CardDrop();
if (drop.QTY > 0 && drop.ID > 0)
if (!items.TryAdd(drop.ID, drop.QTY))
items[drop.ID] += drop.QTY;
if (cardDrops == Cards.ID.Fail || cardDrops == Cards.ID.Immune) continue;
if (!cards.TryAdd(cardDrops, 1))
cards[cardDrops]++;
}
SetMode(Mode.Victory);
VictoryMenu?.Refresh(exp, ap, extraExp, items, cards);
VictoryMenu?.Show();
}
private bool UpdateBattleFunction()
{
var ret = false;
var bml = GetBattleMenus().ToList();
if (bml.Count == 0) return false;// issue here.
foreach (var m in bml)
{
ret = m.Update() || ret;
}
if (!(GetCurrentBattleMenu()?.Damageable?.GetBattleMode().Equals(Damageable.BattleMode.YourTurn) ?? false))
{
var cnt = bml.Count;
if (Player + 1 == cnt)
Player = 0;
for (var 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 static bool UpdateGameOverFunction()
{
Memory.Module = OpenVIII.Module.FieldDebug;
Memory.FieldHolder.FieldID = 75; //game over
Music.Play(0);
Module.State = MenuModule.Mode.MainLobby;
return true;
}
private static bool UpdateStartingFunction() => throw new NotImplementedException();
private bool UpdateVictoryFunction()
{
Music.Play(1);
return VictoryMenu.Update();
}
#endregion Methods
}
}