using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace OpenVIII
{
///
/// Character/Enemy/GF that can be damaged or die.
///
public abstract class Damageable : IDamageable
{
#region Fields
protected ushort _CurrentHP;
private Enum _battlemode;
private Dictionary> _damageActions;
private Kernel.PersistentStatuses _statuses0;
private Kernel.BattleOnlyStatuses _statuses1;
private Dictionary> _statusesActions;
protected ATBTimer ATBTimer;
#endregion Fields
#region Constructors
protected Damageable()
{
}
#endregion Constructors
#region Events
public event EventHandler BattleModeChangeEventHandler;
#endregion Events
#region Enums
public enum BattleMode : byte
{
///
/// ATB bar filling
///
/// Orange Bar precent filled. ATB/Full ATB
ATB_Charging,
///
/// ATB bar charged, waiting for your turn
///
/// Yellow Bar
ATB_Charged,
///
/// Your turn
///
/// Yellow Bar/Name/HP Blinking
YourTurn,
///
/// GF Cast
///
/// Show GF name/hp and blueish bar.
GF_Charging,
EndTurn,
}
#endregion Enums
#region Properties
///
/// Max bar value
///
///
public virtual int ATBBarSize => (int)Memory.CurrentBattleSpeed * 4000;
public float ATBPercent => ATBTimer.Percent;
public bool Charging() => SetBattleMode(BattleMode.ATB_Charging);
public IReadOnlyDictionary> DamageActions
{
get
{
if (_damageActions == null)
_damageActions = new Dictionary>
{ { Kernel.AttackType.PhysicalAttack, Damage_Physical_Attack_Action},
{ Kernel.AttackType.MagicAttack, Damage_Magic_Attack_Action},
{ Kernel.AttackType.CurativeMagic, Damage_Curative_Magic_Action },
{ Kernel.AttackType.CurativeItem, Damage_Curative_Item_Action },
{ Kernel.AttackType.Revive, Damage_Revive_Action },
{ Kernel.AttackType.ReviveAtFullHP, Damage_Revive_At_Full_HP_Action },
{ Kernel.AttackType.PhysicalDamage, Damage_Physical_Damage_Action },
{ Kernel.AttackType.MagicDamage, Damage_Magic_Damage_Action },
{ Kernel.AttackType.RenzokukenFinisher, Damage_Renzokuken_Finisher_Action },
{ Kernel.AttackType.SquallGunbladeAttack, Damage_Squall_Gunblade_Attack_Action },
{ Kernel.AttackType.GF, Damage_GF_Action },
{ Kernel.AttackType.Scan, Damage_Scan_Action },
{ Kernel.AttackType.LvDown, Damage_LV_Down_Action },
{ Kernel.AttackType.SummonItem, Damage_Summon_Item_Action },
{ Kernel.AttackType.GFIgnoreTargetSPR, Damage_GF_Ignore_Target_SPR_Action },
{ Kernel.AttackType.LvUp, Damage_LV_Up_Action },
{ Kernel.AttackType.Card, Damage_Card_Action },
{ Kernel.AttackType.Kamikaze, Damage_Kamikaze_Action },
{ Kernel.AttackType.Devour, Damage_Devour_Action },
{ Kernel.AttackType.GFDamage, Damage_GF_Damage_Action },
{ Kernel.AttackType.Unknown1, Damage_Unknown_1_Action },
{ Kernel.AttackType.MagicAttackIgnoreTargetSPR, Damage_Magic_Attack_Ignore_Target_SPR_Action },
{ Kernel.AttackType.AngeloSearch, Damage_Angelo_Search_Action },
{ Kernel.AttackType.MoogleDance, Damage_Moogle_Dance_Action },
{ Kernel.AttackType.WhiteWindQuistis, Damage_White_WindQuistis_Action },
{ Kernel.AttackType.LvAttack, Damage_LV_Attack_Action },
{ Kernel.AttackType.FixedDamage, Damage_Fixed_Damage_Action },
{ Kernel.AttackType.TargetCurrentHP1, Damage_Target_Current_HP_1_Action },
{ Kernel.AttackType.FixedMagicDamageBasedOnGFLevel, Damage_Fixed_Magic_Damage_Based_on_GF_Level_Action },
{ Kernel.AttackType.Unknown2, Damage_Unknown_2_Action },
{ Kernel.AttackType.Unknown3, Damage_Unknown_3_Action },
{ Kernel.AttackType.GivePercentageHP, Damage_Give_Percentage_HP_Action },
{ Kernel.AttackType.Unknown4, Damage_Unknown_4_Action },
{ Kernel.AttackType.EveryoneGrudge, Damage_Everyones_Grudge_Action },
{ Kernel.AttackType._1_HP_Damage, Damage__1_HP_Damage_Action },
{ Kernel.AttackType.PhysicalAttackIgnoreTargetVIT, Damage_Physical_AttackIgnore_Target_VIT_Action },
};
return _damageActions;
int Damage__1_HP_Damage_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Angelo_Search_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Card_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Curative_Item_Action(int dmg, Kernel.AttackFlags flags)
{
//ChangeHP(-dmg);
var noheal = (Statuses0 & (Kernel.PersistentStatuses.Death | Kernel.PersistentStatuses.Petrify)) != 0 || (Statuses1 & (Kernel.BattleOnlyStatuses.SummonGF)) != 0 || _CurrentHP == 0;
var healisdmg = (Statuses0 & (Kernel.PersistentStatuses.Zombie)) != 0;
if (!noheal)
{
dmg = (healisdmg ? dmg : -dmg);
}
return ChangeHP(dmg) ? dmg : 0;
}
int Damage_Curative_Magic_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Devour_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Everyones_Grudge_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Fixed_Damage_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Fixed_Magic_Damage_Based_on_GF_Level_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_GF_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_GF_Damage_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_GF_Ignore_Target_SPR_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Give_Percentage_HP_Action(int dmg, Kernel.AttackFlags flags) => Damage_Curative_Item_Action(MaxHP() * dmg / 100, flags);
int Damage_Kamikaze_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_LV_Attack_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_LV_Down_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_LV_Up_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Magic_Attack_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Magic_Attack_Ignore_Target_SPR_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Magic_Damage_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Moogle_Dance_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Physical_Attack_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Physical_AttackIgnore_Target_VIT_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Physical_Damage_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Renzokuken_Finisher_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Revive_Action(int dmg, Kernel.AttackFlags flags)
{
var r = ReviveHP();
if ((Statuses0 & Kernel.PersistentStatuses.Zombie) != 0)
{
r = MaxHP();
//Debug.WriteLine($"{this}: Dealt {r}, previous hp: {_CurrentHP}, current hp: {_CurrentHP - r}");
return Damage_Curative_Item_Action(r, flags);
}
else if (_CurrentHP != r)
{
Debug.WriteLine($"{this}: Dealt {-r}, previous hp: {0}, current hp: {r}");
return _CurrentHP = r;
}
return 0;
}
int Damage_Revive_At_Full_HP_Action(int dmg, Kernel.AttackFlags flags)
{
var r = MaxHP();
if ((Statuses0 & Kernel.PersistentStatuses.Zombie) != 0)
{
//Debug.WriteLine($"{this}: Dealt {r}, previous hp: {_CurrentHP}, current hp: {_CurrentHP-r}");
return Damage_Curative_Item_Action(MaxHP(), flags);
}
if (_CurrentHP != r)
{
Debug.WriteLine($"{this}: Dealt {-r}, previous hp: {0}, current hp: {r}");
return _CurrentHP = r;
}
return 0;
}
int Damage_Scan_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Squall_Gunblade_Attack_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Summon_Item_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Target_Current_HP_1_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Unknown_1_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Unknown_2_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Unknown_3_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_Unknown_4_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Damage_White_WindQuistis_Action(int dmg, Kernel.AttackFlags flags) => throw new NotImplementedException();
}
}
public abstract byte EVA { get; }
public abstract int EXP { get; }
public abstract byte HIT { get; }
public ushort HP => CurrentHP();
public bool IsDead => CurrentHP() == 0 || Statuses0.HasFlag(Kernel.PersistentStatuses.Death);
///
/// If all partymemembers are in gameover trigger Phoenix Pinion if CanPhoenixPinion or
/// trigger Game over
///
public bool IsGameOver => IsDead || IsPetrify || (Statuses1 & (Kernel.BattleOnlyStatuses.Eject)) != 0;
///
/// ATB frozen
///
public bool IsInactive => IsGameOver ||
(Statuses1 & (Kernel.BattleOnlyStatuses.Stop)) != 0;
///
/// Menu disabled
///
public bool IsNonInteractive => IsInactive ||
(Statuses0 & Kernel.PersistentStatuses.Berserk) != 0;
public bool IsPetrify => (Statuses0 & (Kernel.PersistentStatuses.Petrify)) != 0;
public abstract byte Level { get; }
public abstract byte LUCK { get; }
public abstract byte MAG { get; }
///
/// Name
///
/// not saved to file
public virtual FF8String Name { get; set; }
public abstract byte SPD { get; }
public abstract byte SPR { get; }
///
/// Persistant_Statuses are saved and last between battles.
///
public virtual Kernel.PersistentStatuses Statuses0
{
get
{
if (!StatusImmune)
return _statuses0;
else
return Kernel.PersistentStatuses.None;
}
set
{
if (!StatusImmune)
_statuses0 = value;
}
}
///
/// Battle_Only_Statuses only exist in battle. Please set to None at end and/or start of battle.
///
public virtual Kernel.BattleOnlyStatuses Statuses1
{
get
{
if (!StatusImmune)
return _statuses1;
else
return Kernel.BattleOnlyStatuses.None;
}
set
{
if (!StatusImmune)
_statuses1 = value;
}
}
public IReadOnlyDictionary> StatusesActions
{
get
{
if (_statusesActions == null)
_statusesActions = new Dictionary>
{
{ Kernel.AttackType.PhysicalAttack, Statuses_Physical_Attack_Action },
{ Kernel.AttackType.MagicAttack, Statuses_Magic_Attack_Action },
{ Kernel.AttackType.CurativeMagic, Statuses_Curative_Magic_Action },
{ Kernel.AttackType.CurativeItem, Statuses_Curative_Item_Action },
{ Kernel.AttackType.Revive, Statuses_Revive_Action },
{ Kernel.AttackType.ReviveAtFullHP, Statuses_Revive_At_Full_HP_Action },
{ Kernel.AttackType.PhysicalDamage, Statuses_Physical_Statuses_Action },
{ Kernel.AttackType.MagicDamage, Statuses_Magic_Statuses_Action },
{ Kernel.AttackType.RenzokukenFinisher, Statuses_Renzokuken_Finisher_Action },
{ Kernel.AttackType.SquallGunbladeAttack, Statuses_Squall_Gunblade_Attack_Action },
{ Kernel.AttackType.GF, Statuses_GF_Action },
{ Kernel.AttackType.Scan, Statuses_Scan_Action },
{ Kernel.AttackType.LvDown, Statuses_LV_Down_Action },
{ Kernel.AttackType.SummonItem, Statuses_Summon_Item_Action },
{ Kernel.AttackType.GFIgnoreTargetSPR, Statuses_GF_Ignore_Target_SPR_Action },
{ Kernel.AttackType.LvUp, Statuses_LV_Up_Action },
{ Kernel.AttackType.Card, Statuses_Card_Action },
{ Kernel.AttackType.Kamikaze, Statuses_Kamikaze_Action },
{ Kernel.AttackType.Devour, Statuses_Devour_Action },
{ Kernel.AttackType.GFDamage, Statuses_GF_Statuses_Action },
{ Kernel.AttackType.Unknown1, Statuses_Unknown_1_Action },
{ Kernel.AttackType.MagicAttackIgnoreTargetSPR, Statuses_Magic_Attack_Ignore_Target_SPR_Action },
{ Kernel.AttackType.AngeloSearch, Statuses_Angelo_Search_Action },
{ Kernel.AttackType.MoogleDance, Statuses_Moogle_Dance_Action },
{ Kernel.AttackType.WhiteWindQuistis, Statuses_White_WindQuistis_Action },
{ Kernel.AttackType.LvAttack, Statuses_LV_Attack_Action },
{ Kernel.AttackType.FixedDamage, Statuses_Fixed_Statuses_Action },
{ Kernel.AttackType.TargetCurrentHP1, Statuses_Target_Current_HP_1_Action },
{ Kernel.AttackType.FixedMagicDamageBasedOnGFLevel, Statuses_Fixed_Magic_Statuses_Based_on_GF_Level_Action },
{ Kernel.AttackType.Unknown2, Statuses_Unknown_2_Action },
{ Kernel.AttackType.Unknown3, Statuses_Unknown_3_Action },
{ Kernel.AttackType.GivePercentageHP, Statuses_Give_Percentage_HP_Action },
{ Kernel.AttackType.Unknown4, Statuses_Unknown_4_Action },
{ Kernel.AttackType.EveryoneGrudge, Statuses_Everyones_Grudge_Action },
{ Kernel.AttackType._1_HP_Damage, Statuses__1_HP_Statuses_Action },
{ Kernel.AttackType.PhysicalAttackIgnoreTargetVIT, Statuses_Physical_AttackIgnore_Target_VIT_Action },
};
return _statusesActions;
int Statuses__1_HP_Statuses_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Angelo_Search_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Card_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Curative_Item_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags)
{
var bak0 = Statuses0;
var bak1 = Statuses1;
Statuses0 &= ~statuses0;
Statuses1 &= ~statuses1;
if (!bak0.Equals(Statuses0) || !bak1.Equals(Statuses1))
return 1;
return 0;
}
int Statuses_Curative_Magic_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Devour_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Everyones_Grudge_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Fixed_Magic_Statuses_Based_on_GF_Level_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Fixed_Statuses_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_GF_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_GF_Ignore_Target_SPR_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_GF_Statuses_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Give_Percentage_HP_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) =>
Statuses_Curative_Item_Action(statuses0, Statuses1, flags);
int Statuses_Kamikaze_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_LV_Attack_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_LV_Down_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_LV_Up_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Magic_Attack_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Magic_Attack_Ignore_Target_SPR_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Magic_Statuses_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Moogle_Dance_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Physical_Attack_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Physical_AttackIgnore_Target_VIT_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Physical_Statuses_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Renzokuken_Finisher_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Revive_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags)
{
if ((Statuses0 & Kernel.PersistentStatuses.Death) != 0 || _CurrentHP == 0)
{
Statuses0 = Kernel.PersistentStatuses.None;
Statuses1 = Kernel.BattleOnlyStatuses.None;
_CurrentHP = 0;
return 1;
}
return 0;
}
int Statuses_Revive_At_Full_HP_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) =>
Statuses_Revive_Action(statuses0, statuses1, flags);
int Statuses_Scan_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Squall_Gunblade_Attack_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Summon_Item_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Target_Current_HP_1_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Unknown_1_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Unknown_2_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Unknown_3_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_Unknown_4_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
int Statuses_White_WindQuistis_Action(Kernel.PersistentStatuses statuses0, Kernel.BattleOnlyStatuses statuses1, Kernel.AttackFlags flags) => throw new NotImplementedException();
}
}
///
/// If status immune statuses aren't set and return none.
///
public bool StatusImmune { get; protected set; } = false;
public abstract byte STR { get; }
public Saves.GFData SummonedGF { get; private set; }
public abstract byte VIT { get; }
#endregion Properties
#region Methods
///
/// Per tick increment
///
///
///
///
///
public static int BarIncrement(int spd, SpeedMod speedMod = SpeedMod.Normal) => (spd + 30) * ((byte)speedMod / 2);
public static T Load(BinaryReader br, Enum @enum, Saves.Data data) where T : Damageable, new()
{
var r = new T { Data = data };
r.ReadData(br, @enum);
r.Init();
return r;
}
protected Saves.Data Data { get; set; }
///
public static int TimeToFillBarGF(int spd) => 200 * (int)Memory.CurrentBattleSpeed / (3 * (spd + 30));
///
/// Starting value
///
///
///
///
public virtual int ATBBarStart(int spd)
{
var i = 0;
//this verison loops till it gets a value between 0 and ATBBarSize. Unsure which is best.
//do
//{
// i = ((spd / 4) + Memory.Random.Next(128) - 34) * (int)Memory.CurrentBattleSpeed * 40;
//}
//while (i <= 0 || i > ATBBarSize);
//return i;
//this version will return ATBBarSize if larger and 0 if less than 0.
i = ((spd / 4) + Memory.Random.Next(128) - 34) * (int)Memory.CurrentBattleSpeed * 40;
if (i > 0 && i < ATBBarSize)
return i;
else if (i < 0) return 0;
else return ATBBarSize;
}
public int ATBBarStart()
{
if (IsGameOver)
return 0;
return ATBBarStart(SPD);
}
public virtual bool ATBCharged()
{
if (GetBattleMode().Equals(BattleMode.ATB_Charging))
{
SetBattleMode(BattleMode.ATB_Charged);
return true;
}
return false;
}
public virtual int BarIncrement() => BarIncrement(SPD, GetSpeedMod());
public bool ChangeHP(int dmg)
{
if (dmg == 0 || (Statuses1 & Kernel.BattleOnlyStatuses.Invincible) != 0)
return false;
var hp = _CurrentHP - dmg;
var lasthp = _CurrentHP;
if (hp <= 0)
{
Statuses0 |= (Kernel.PersistentStatuses.Death);
_CurrentHP = 0;
}
else
{
_CurrentHP = (ushort)(hp > ushort.MaxValue ? ushort.MaxValue : hp);
//use a CurrentHP method or property to adjust this to correct maxhp.
//if team Laguna the max hp can be different and each gf has there own.
}
CurrentHP();
if (lasthp == _CurrentHP) return false;
Debug.WriteLine($"{this}: Dealt {dmg}, previous hp: {lasthp}, current hp: {_CurrentHP}");
return true;
}
public virtual bool ChargeGF()
{
if (!GetBattleMode().Equals(BattleMode.YourTurn) ||
(!(this is Saves.CharacterData))) return false;
SetBattleMode(BattleMode.ATB_Charged);
return true;
}
public abstract Damageable Clone();
public virtual bool IsCritical => CurrentHP() <= CriticalHP();
public virtual ushort CriticalHP() => (ushort)((MaxHP() / 4) - 1);
///
/// Override This: to check vs your max hp value.
///
///
public virtual ushort CurrentHP()
{
var max = MaxHP();
if (_CurrentHP > max) _CurrentHP = max;
return _CurrentHP;
}
public bool DealDamage(int dmg, Kernel.AttackType type, Kernel.AttackFlags? flags)
{
if (!DamageActions.ContainsKey(type)) return false;
//ChangeHP(-dmg);
// check max and min values
if (dmg > Kernel.KernelBin.MaxHPValue && type != Kernel.AttackType.GivePercentageHP) dmg = Kernel.KernelBin.MaxHPValue;
if (dmg < 0) return false;
// depending on damage type see if damage is correct
dmg = DamageActions[type](dmg, flags ?? Kernel.AttackFlags.None);
return dmg != 0;
}
public bool DealStatus(
Kernel.PersistentStatuses? statuses0,
Kernel.BattleOnlyStatuses? statuses1,
Kernel.AttackType type,
Kernel.AttackFlags? flags)
{
if (StatusesActions.ContainsKey(type))
{
var total = StatusesActions[type](
statuses0 ?? Kernel.PersistentStatuses.None,
statuses1 ?? Kernel.BattleOnlyStatuses.None,
flags ?? Kernel.AttackFlags.None);
return total != 0;
}
return false;
}
public abstract short ElementalResistance(Kernel.Element @in);
public virtual bool EndTurn(bool force = false)
{
if (force ||
GetBattleMode().Equals(BattleMode.YourTurn) ||
GetBattleMode().Equals(BattleMode.GF_Charging)
)
{
SetBattleMode(BattleMode.EndTurn); // trigger any end of turn clean up.
SetBattleMode(BattleMode.ATB_Charging); //start charging next turn.
ATBTimer.NewTurn();
Refresh();
return true;
}
return false;
}
public virtual Enum GetBattleMode() => _battlemode ?? BattleMode.ATB_Charging;
public bool GetCast(out T cast) where T : Damageable
{
if (GetType() == typeof(T))
{
cast = (T)this;
return true;
}
cast = null;
return false;
}
public bool GetCharacterData(out Saves.CharacterData character)
=> GetCast(out character);
public bool GetEnemy(out Enemy enemy)
=> GetCast(out enemy);
public bool GetGFData(out Saves.GFData gf)
=> GetCast(out gf);
public SpeedMod GetSpeedMod()
{
if ((Statuses1 & Kernel.BattleOnlyStatuses.Haste) != 0)
return SpeedMod.Haste;
else if ((Statuses1 & Kernel.BattleOnlyStatuses.Stop) != 0 || IsGameOver)
return SpeedMod.Stop;
else if ((Statuses1 & Kernel.BattleOnlyStatuses.Slow) != 0)
return SpeedMod.Slow;
return SpeedMod.Normal;
}
public virtual bool GFDiedWhileCharging()
{
if (GetBattleMode().Equals(BattleMode.GF_Charging))
{
SetBattleMode(BattleMode.YourTurn);
return true;
}
return false;
}
///
/// Overload this: To get correct MaxHP value
///
///
public virtual ushort MaxHP() => ushort.MaxValue;
public virtual float PercentFullHP() => (float)CurrentHP() / MaxHP();
public virtual void Refresh() => ATBTimer.Refresh(this);
public virtual void Reset()
{
ATBTimer.Reset();
Statuses1 = 0;
}
public virtual ushort ReviveHP() => (ushort)(MaxHP() / 8);
protected virtual bool SetBattleMode(Enum mode)
{
if (!(_battlemode?.Equals(mode) ?? false))
{
_battlemode = mode;
BattleModeChangeEventHandler?.Invoke(this, mode);
return true;
}
return false;
}
public void SetSummon(Saves.GFData gfdata)
{
SummonedGF = gfdata;
if (SummonedGF != null)
{
SetBattleMode(BattleMode.GF_Charging);
SummonedGF.EndTurn(true);
}
}
///
/// Summon GF
///
///
public void SetSummon(GFs gf)
{
Saves.GFData gfdata = null;
if (Memory.State?.GFs?.TryGetValue(gf, out gfdata) ?? false)
{
// found a gf.
}
SetSummon(gfdata);
}
public virtual bool StartTurn()
{
if (
GetBattleMode().Equals(BattleMode.ATB_Charged)
)
{
SetBattleMode(BattleMode.YourTurn); //it's your turn.
Refresh();
return true;
}
return false;
}
public abstract sbyte StatusResistance(Kernel.BattleOnlyStatuses s);
public abstract sbyte StatusResistance(Kernel.PersistentStatuses s);
public virtual bool Switch()
{
if (GetBattleMode().Equals(BattleMode.YourTurn))
{
SetBattleMode(BattleMode.ATB_Charged);
return true;
}
return false;
}
public float TicksToFillBar(int start, int spd, SpeedMod speedMod = SpeedMod.Normal)
{
var top = (ATBBarSize - start);
var bot = BarIncrement(spd, speedMod);
if (bot == 0)
return float.MinValue;
return (top / bot);
}
public float TicksToFillBar() => TicksToFillBar(ATBBarStart(), SPD, GetSpeedMod());
public float TimeToFillBar(int start, int spd, SpeedMod speedMod = SpeedMod.Normal)
{
var tickspersec = 60f;
return TicksToFillBar(start, spd, speedMod) / tickspersec;
}
public float TimeToFillBar() => TimeToFillBar(ATBBarStart(), SPD, GetSpeedMod());
public int TimeToFillBarGF() => TimeToFillBarGF(SPD);
public abstract ushort TotalStat(Kernel.Stat s);
public virtual bool Update(bool force = false)
{
if (GetBattleMode().Equals(BattleMode.ATB_Charging) || force)
{
if (!ModuleBattleDebug.PauseATB)
ATBTimer.Update();
if (ATBTimer.Done && ATBCharged())
{
//Your turn is ready.
}
return true;
}
else if (GetBattleMode().Equals(BattleMode.GF_Charging))
{
SummonedGF.Update();
if (SummonedGF.IsGameOver || (SummonedGF.ATBTimer.Done && EndTurn()))
{
//Summon GF end turn.
//SetSummon(null);
}
return true;
}
return false;
}
protected virtual void Init()
{
SetBattleMode(BattleMode.ATB_Charging);
ATBTimer = new ATBTimer(this);
}
protected abstract void ReadData(BinaryReader br, Enum @enum);
#endregion Methods
}
}