Damageable.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. namespace OpenVIII
  6. {
  7. /// <summary>
  8. /// Character/Enemy/GF that can be damaged or die.
  9. /// </summary>
  10. public abstract class Damageable : IDamageable
  11. {
  12. #region Fields
  13. protected ushort _CurrentHP;
  14. private Enum _battlemode;
  15. private Dictionary<Kernel_bin.Attack_Type, Func<int, Kernel_bin.Attack_Flags, int>> _damageActions;
  16. private Kernel_bin.Persistent_Statuses _statuses0;
  17. private Kernel_bin.Battle_Only_Statuses _statuses1;
  18. private Dictionary<Kernel_bin.Attack_Type, Func<Kernel_bin.Persistent_Statuses, Kernel_bin.Battle_Only_Statuses, Kernel_bin.Attack_Flags, int>> _statusesActions;
  19. protected ATBTimer ATBTimer;
  20. #endregion Fields
  21. #region Constructors
  22. protected Damageable()
  23. {
  24. }
  25. #endregion Constructors
  26. #region Events
  27. public event EventHandler<Enum> BattleModeChangeEventHandler;
  28. #endregion Events
  29. #region Enums
  30. public enum BattleMode : byte
  31. {
  32. /// <summary>
  33. /// ATB bar filling
  34. /// </summary>
  35. /// <remarks>Orange Bar precent filled. ATB/Full ATB</remarks>
  36. ATB_Charging,
  37. /// <summary>
  38. /// ATB bar charged, waiting for your turn
  39. /// </summary>
  40. /// <remarks>Yellow Bar</remarks>
  41. ATB_Charged,
  42. /// <summary>
  43. /// Your turn
  44. /// </summary>
  45. /// <remarks>Yellow Bar/Name/HP Blinking</remarks>
  46. YourTurn,
  47. /// <summary>
  48. /// GF Cast
  49. /// </summary>
  50. /// <remarks>Show GF name/hp and blueish bar.</remarks>
  51. GF_Charging,
  52. EndTurn,
  53. }
  54. #endregion Enums
  55. #region Properties
  56. /// <summary>
  57. /// Max bar value
  58. /// </summary>
  59. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  60. public virtual int ATBBarSize => (int)Memory.CurrentBattleSpeed * 4000;
  61. public float ATBPercent => ATBTimer.Percent;
  62. public bool Charging() => SetBattleMode(BattleMode.ATB_Charging);
  63. public IReadOnlyDictionary<Kernel_bin.Attack_Type, Func<int, Kernel_bin.Attack_Flags, int>> DamageActions
  64. {
  65. get
  66. {
  67. if (_damageActions == null)
  68. _damageActions = new Dictionary<Kernel_bin.Attack_Type, Func<int, Kernel_bin.Attack_Flags, int>>
  69. { { Kernel_bin.Attack_Type.Physical_Attack, Damage_Physical_Attack_Action},
  70. { Kernel_bin.Attack_Type.Magic_Attack, Damage_Magic_Attack_Action},
  71. { Kernel_bin.Attack_Type.Curative_Magic, Damage_Curative_Magic_Action },
  72. { Kernel_bin.Attack_Type.Curative_Item, Damage_Curative_Item_Action },
  73. { Kernel_bin.Attack_Type.Revive, Damage_Revive_Action },
  74. { Kernel_bin.Attack_Type.Revive_At_Full_HP, Damage_Revive_At_Full_HP_Action },
  75. { Kernel_bin.Attack_Type.Physical_Damage, Damage_Physical_Damage_Action },
  76. { Kernel_bin.Attack_Type.Magic_Damage, Damage_Magic_Damage_Action },
  77. { Kernel_bin.Attack_Type.Renzokuken_Finisher, Damage_Renzokuken_Finisher_Action },
  78. { Kernel_bin.Attack_Type.Squall_Gunblade_Attack, Damage_Squall_Gunblade_Attack_Action },
  79. { Kernel_bin.Attack_Type.GF, Damage_GF_Action },
  80. { Kernel_bin.Attack_Type.Scan, Damage_Scan_Action },
  81. { Kernel_bin.Attack_Type.LV_Down, Damage_LV_Down_Action },
  82. { Kernel_bin.Attack_Type.Summon_Item, Damage_Summon_Item_Action },
  83. { Kernel_bin.Attack_Type.GF_Ignore_Target_SPR, Damage_GF_Ignore_Target_SPR_Action },
  84. { Kernel_bin.Attack_Type.LV_Up, Damage_LV_Up_Action },
  85. { Kernel_bin.Attack_Type.Card, Damage_Card_Action },
  86. { Kernel_bin.Attack_Type.Kamikaze, Damage_Kamikaze_Action },
  87. { Kernel_bin.Attack_Type.Devour, Damage_Devour_Action },
  88. { Kernel_bin.Attack_Type.GF_Damage, Damage_GF_Damage_Action },
  89. { Kernel_bin.Attack_Type.Unknown_1, Damage_Unknown_1_Action },
  90. { Kernel_bin.Attack_Type.Magic_Attack_Ignore_Target_SPR, Damage_Magic_Attack_Ignore_Target_SPR_Action },
  91. { Kernel_bin.Attack_Type.Angelo_Search, Damage_Angelo_Search_Action },
  92. { Kernel_bin.Attack_Type.Moogle_Dance, Damage_Moogle_Dance_Action },
  93. { Kernel_bin.Attack_Type.White_WindQuistis, Damage_White_WindQuistis_Action },
  94. { Kernel_bin.Attack_Type.LV_Attack, Damage_LV_Attack_Action },
  95. { Kernel_bin.Attack_Type.Fixed_Damage, Damage_Fixed_Damage_Action },
  96. { Kernel_bin.Attack_Type.Target_Current_HP_1, Damage_Target_Current_HP_1_Action },
  97. { Kernel_bin.Attack_Type.Fixed_Magic_Damage_Based_on_GF_Level, Damage_Fixed_Magic_Damage_Based_on_GF_Level_Action },
  98. { Kernel_bin.Attack_Type.Unknown_2, Damage_Unknown_2_Action },
  99. { Kernel_bin.Attack_Type.Unknown_3, Damage_Unknown_3_Action },
  100. { Kernel_bin.Attack_Type.Give_Percentage_HP, Damage_Give_Percentage_HP_Action },
  101. { Kernel_bin.Attack_Type.Unknown_4, Damage_Unknown_4_Action },
  102. { Kernel_bin.Attack_Type.Everyones_Grudge, Damage_Everyones_Grudge_Action },
  103. { Kernel_bin.Attack_Type._1_HP_Damage, Damage__1_HP_Damage_Action },
  104. { Kernel_bin.Attack_Type.Physical_AttackIgnore_Target_VIT, Damage_Physical_AttackIgnore_Target_VIT_Action },
  105. };
  106. return _damageActions;
  107. int Damage__1_HP_Damage_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  108. int Damage_Angelo_Search_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  109. int Damage_Card_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  110. int Damage_Curative_Item_Action(int dmg, Kernel_bin.Attack_Flags flags)
  111. {
  112. //ChangeHP(-dmg);
  113. bool noheal = (Statuses0 & (Kernel_bin.Persistent_Statuses.Death | Kernel_bin.Persistent_Statuses.Petrify)) != 0 || (Statuses1 & (Kernel_bin.Battle_Only_Statuses.Summon_GF)) != 0 || _CurrentHP == 0;
  114. bool healisdmg = (Statuses0 & (Kernel_bin.Persistent_Statuses.Zombie)) != 0;
  115. if (!noheal)
  116. {
  117. dmg = (healisdmg ? dmg : -dmg);
  118. }
  119. return ChangeHP(dmg) ? dmg : 0;
  120. }
  121. int Damage_Curative_Magic_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  122. int Damage_Devour_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  123. int Damage_Everyones_Grudge_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  124. int Damage_Fixed_Damage_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  125. int Damage_Fixed_Magic_Damage_Based_on_GF_Level_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  126. int Damage_GF_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  127. int Damage_GF_Damage_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  128. int Damage_GF_Ignore_Target_SPR_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  129. int Damage_Give_Percentage_HP_Action(int dmg, Kernel_bin.Attack_Flags flags) => Damage_Curative_Item_Action(MaxHP() * dmg / 100, flags);
  130. int Damage_Kamikaze_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  131. int Damage_LV_Attack_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  132. int Damage_LV_Down_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  133. int Damage_LV_Up_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  134. int Damage_Magic_Attack_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  135. int Damage_Magic_Attack_Ignore_Target_SPR_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  136. int Damage_Magic_Damage_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  137. int Damage_Moogle_Dance_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  138. int Damage_Physical_Attack_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  139. int Damage_Physical_AttackIgnore_Target_VIT_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  140. int Damage_Physical_Damage_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  141. int Damage_Renzokuken_Finisher_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  142. int Damage_Revive_Action(int dmg, Kernel_bin.Attack_Flags flags)
  143. {
  144. ushort r = ReviveHP();
  145. if ((Statuses0 & Kernel_bin.Persistent_Statuses.Zombie) != 0)
  146. {
  147. r = MaxHP();
  148. //Debug.WriteLine($"{this}: Dealt {r}, previous hp: {_CurrentHP}, current hp: {_CurrentHP - r}");
  149. return Damage_Curative_Item_Action(r, flags);
  150. }
  151. else if (_CurrentHP != r)
  152. {
  153. Debug.WriteLine($"{this}: Dealt {-r}, previous hp: {0}, current hp: {r}");
  154. return _CurrentHP = r;
  155. }
  156. return 0;
  157. }
  158. int Damage_Revive_At_Full_HP_Action(int dmg, Kernel_bin.Attack_Flags flags)
  159. {
  160. ushort r = MaxHP();
  161. if ((Statuses0 & Kernel_bin.Persistent_Statuses.Zombie) != 0)
  162. {
  163. //Debug.WriteLine($"{this}: Dealt {r}, previous hp: {_CurrentHP}, current hp: {_CurrentHP-r}");
  164. return Damage_Curative_Item_Action(MaxHP(), flags);
  165. }
  166. if (_CurrentHP != r)
  167. {
  168. Debug.WriteLine($"{this}: Dealt {-r}, previous hp: {0}, current hp: {r}");
  169. return _CurrentHP = r;
  170. }
  171. return 0;
  172. }
  173. int Damage_Scan_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  174. int Damage_Squall_Gunblade_Attack_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  175. int Damage_Summon_Item_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  176. int Damage_Target_Current_HP_1_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  177. int Damage_Unknown_1_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  178. int Damage_Unknown_2_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  179. int Damage_Unknown_3_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  180. int Damage_Unknown_4_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  181. int Damage_White_WindQuistis_Action(int dmg, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  182. }
  183. }
  184. public abstract byte EVA { get; }
  185. public abstract int EXP { get; }
  186. public abstract byte HIT { get; }
  187. public ushort HP => CurrentHP();
  188. public bool IsDead => CurrentHP() == 0 || (Statuses0 & Kernel_bin.Persistent_Statuses.Death) != 0;
  189. /// <summary>
  190. /// If all partymemembers are in gameover trigger Phoenix Pinion if CanPhoenixPinion or
  191. /// trigger Game over
  192. /// </summary>
  193. public bool IsGameOver => IsDead || IsPetrify || (Statuses1 & (Kernel_bin.Battle_Only_Statuses.Eject)) != 0;
  194. /// <summary>
  195. /// ATB frozen
  196. /// </summary>
  197. public bool IsInactive => IsGameOver ||
  198. (Statuses1 & (Kernel_bin.Battle_Only_Statuses.Stop)) != 0;
  199. /// <summary>
  200. /// Menu disabled
  201. /// </summary>
  202. public bool IsNonInteractive => IsInactive ||
  203. (Statuses0 & Kernel_bin.Persistent_Statuses.Berserk) != 0;
  204. public bool IsPetrify => (Statuses0 & (Kernel_bin.Persistent_Statuses.Petrify)) != 0;
  205. public abstract byte Level { get; }
  206. public abstract byte LUCK { get; }
  207. public abstract byte MAG { get; }
  208. /// <summary>
  209. /// Name
  210. /// </summary>
  211. /// <remarks>not saved to file</remarks>
  212. public virtual FF8String Name { get; set; }
  213. public abstract byte SPD { get; }
  214. public abstract byte SPR { get; }
  215. /// <summary>
  216. /// Persistant_Statuses are saved and last between battles.
  217. /// </summary>
  218. public virtual Kernel_bin.Persistent_Statuses Statuses0
  219. {
  220. get
  221. {
  222. if (!StatusImmune)
  223. return _statuses0;
  224. else
  225. return Kernel_bin.Persistent_Statuses.None;
  226. }
  227. set
  228. {
  229. if (!StatusImmune)
  230. _statuses0 = value;
  231. }
  232. }
  233. /// <summary>
  234. /// Battle_Only_Statuses only exist in battle. Please set to None at end and/or start of battle.
  235. /// </summary>
  236. public virtual Kernel_bin.Battle_Only_Statuses Statuses1
  237. {
  238. get
  239. {
  240. if (!StatusImmune)
  241. return _statuses1;
  242. else
  243. return Kernel_bin.Battle_Only_Statuses.None;
  244. }
  245. set
  246. {
  247. if (!StatusImmune)
  248. _statuses1 = value;
  249. }
  250. }
  251. public IReadOnlyDictionary<Kernel_bin.Attack_Type, Func<Kernel_bin.Persistent_Statuses, Kernel_bin.Battle_Only_Statuses, Kernel_bin.Attack_Flags, int>> StatusesActions
  252. {
  253. get
  254. {
  255. if (_statusesActions == null)
  256. _statusesActions = new Dictionary<Kernel_bin.Attack_Type, Func<Kernel_bin.Persistent_Statuses, Kernel_bin.Battle_Only_Statuses, Kernel_bin.Attack_Flags, int>>
  257. {
  258. { Kernel_bin.Attack_Type.Physical_Attack, Statuses_Physical_Attack_Action },
  259. { Kernel_bin.Attack_Type.Magic_Attack, Statuses_Magic_Attack_Action },
  260. { Kernel_bin.Attack_Type.Curative_Magic, Statuses_Curative_Magic_Action },
  261. { Kernel_bin.Attack_Type.Curative_Item, Statuses_Curative_Item_Action },
  262. { Kernel_bin.Attack_Type.Revive, Statuses_Revive_Action },
  263. { Kernel_bin.Attack_Type.Revive_At_Full_HP, Statuses_Revive_At_Full_HP_Action },
  264. { Kernel_bin.Attack_Type.Physical_Damage, Statuses_Physical_Statuses_Action },
  265. { Kernel_bin.Attack_Type.Magic_Damage, Statuses_Magic_Statuses_Action },
  266. { Kernel_bin.Attack_Type.Renzokuken_Finisher, Statuses_Renzokuken_Finisher_Action },
  267. { Kernel_bin.Attack_Type.Squall_Gunblade_Attack, Statuses_Squall_Gunblade_Attack_Action },
  268. { Kernel_bin.Attack_Type.GF, Statuses_GF_Action },
  269. { Kernel_bin.Attack_Type.Scan, Statuses_Scan_Action },
  270. { Kernel_bin.Attack_Type.LV_Down, Statuses_LV_Down_Action },
  271. { Kernel_bin.Attack_Type.Summon_Item, Statuses_Summon_Item_Action },
  272. { Kernel_bin.Attack_Type.GF_Ignore_Target_SPR, Statuses_GF_Ignore_Target_SPR_Action },
  273. { Kernel_bin.Attack_Type.LV_Up, Statuses_LV_Up_Action },
  274. { Kernel_bin.Attack_Type.Card, Statuses_Card_Action },
  275. { Kernel_bin.Attack_Type.Kamikaze, Statuses_Kamikaze_Action },
  276. { Kernel_bin.Attack_Type.Devour, Statuses_Devour_Action },
  277. { Kernel_bin.Attack_Type.GF_Damage, Statuses_GF_Statuses_Action },
  278. { Kernel_bin.Attack_Type.Unknown_1, Statuses_Unknown_1_Action },
  279. { Kernel_bin.Attack_Type.Magic_Attack_Ignore_Target_SPR, Statuses_Magic_Attack_Ignore_Target_SPR_Action },
  280. { Kernel_bin.Attack_Type.Angelo_Search, Statuses_Angelo_Search_Action },
  281. { Kernel_bin.Attack_Type.Moogle_Dance, Statuses_Moogle_Dance_Action },
  282. { Kernel_bin.Attack_Type.White_WindQuistis, Statuses_White_WindQuistis_Action },
  283. { Kernel_bin.Attack_Type.LV_Attack, Statuses_LV_Attack_Action },
  284. { Kernel_bin.Attack_Type.Fixed_Damage, Statuses_Fixed_Statuses_Action },
  285. { Kernel_bin.Attack_Type.Target_Current_HP_1, Statuses_Target_Current_HP_1_Action },
  286. { Kernel_bin.Attack_Type.Fixed_Magic_Damage_Based_on_GF_Level, Statuses_Fixed_Magic_Statuses_Based_on_GF_Level_Action },
  287. { Kernel_bin.Attack_Type.Unknown_2, Statuses_Unknown_2_Action },
  288. { Kernel_bin.Attack_Type.Unknown_3, Statuses_Unknown_3_Action },
  289. { Kernel_bin.Attack_Type.Give_Percentage_HP, Statuses_Give_Percentage_HP_Action },
  290. { Kernel_bin.Attack_Type.Unknown_4, Statuses_Unknown_4_Action },
  291. { Kernel_bin.Attack_Type.Everyones_Grudge, Statuses_Everyones_Grudge_Action },
  292. { Kernel_bin.Attack_Type._1_HP_Damage, Statuses__1_HP_Statuses_Action },
  293. { Kernel_bin.Attack_Type.Physical_AttackIgnore_Target_VIT, Statuses_Physical_AttackIgnore_Target_VIT_Action },
  294. };
  295. return _statusesActions;
  296. int Statuses__1_HP_Statuses_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  297. int Statuses_Angelo_Search_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  298. int Statuses_Card_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  299. int Statuses_Curative_Item_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags)
  300. {
  301. Kernel_bin.Persistent_Statuses bak0 = Statuses0;
  302. Kernel_bin.Battle_Only_Statuses bak1 = Statuses1;
  303. Statuses0 &= ~statuses0;
  304. Statuses1 &= ~statuses1;
  305. if (!bak0.Equals(Statuses0) || !bak1.Equals(Statuses1))
  306. return 1;
  307. return 0;
  308. }
  309. int Statuses_Curative_Magic_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  310. int Statuses_Devour_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  311. int Statuses_Everyones_Grudge_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  312. int Statuses_Fixed_Magic_Statuses_Based_on_GF_Level_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  313. int Statuses_Fixed_Statuses_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  314. int Statuses_GF_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  315. int Statuses_GF_Ignore_Target_SPR_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  316. int Statuses_GF_Statuses_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  317. int Statuses_Give_Percentage_HP_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) =>
  318. Statuses_Curative_Item_Action(statuses0, Statuses1, flags);
  319. int Statuses_Kamikaze_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  320. int Statuses_LV_Attack_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  321. int Statuses_LV_Down_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  322. int Statuses_LV_Up_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  323. int Statuses_Magic_Attack_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  324. int Statuses_Magic_Attack_Ignore_Target_SPR_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  325. int Statuses_Magic_Statuses_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  326. int Statuses_Moogle_Dance_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  327. int Statuses_Physical_Attack_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  328. int Statuses_Physical_AttackIgnore_Target_VIT_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  329. int Statuses_Physical_Statuses_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  330. int Statuses_Renzokuken_Finisher_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  331. int Statuses_Revive_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags)
  332. {
  333. if ((Statuses0 & Kernel_bin.Persistent_Statuses.Death) != 0 || _CurrentHP == 0)
  334. {
  335. Statuses0 = Kernel_bin.Persistent_Statuses.None;
  336. Statuses1 = Kernel_bin.Battle_Only_Statuses.None;
  337. _CurrentHP = 0;
  338. return 1;
  339. }
  340. return 0;
  341. }
  342. int Statuses_Revive_At_Full_HP_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) =>
  343. Statuses_Revive_Action(statuses0, statuses1, flags);
  344. int Statuses_Scan_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  345. int Statuses_Squall_Gunblade_Attack_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  346. int Statuses_Summon_Item_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  347. int Statuses_Target_Current_HP_1_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  348. int Statuses_Unknown_1_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  349. int Statuses_Unknown_2_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  350. int Statuses_Unknown_3_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  351. int Statuses_Unknown_4_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  352. int Statuses_White_WindQuistis_Action(Kernel_bin.Persistent_Statuses statuses0, Kernel_bin.Battle_Only_Statuses statuses1, Kernel_bin.Attack_Flags flags) => throw new NotImplementedException();
  353. }
  354. }
  355. /// <summary>
  356. /// If status immune statuses aren't set and return none.
  357. /// </summary>
  358. public bool StatusImmune { get; protected set; } = false;
  359. public abstract byte STR { get; }
  360. public Saves.GFData SummonedGF { get; private set; }
  361. public abstract byte VIT { get; }
  362. #endregion Properties
  363. #region Methods
  364. /// <summary>
  365. /// Per tick increment
  366. /// </summary>
  367. /// <param name="spd"></param>
  368. /// <param name="speedMod"></param>
  369. /// <returns></returns>
  370. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  371. public static int BarIncrement(int spd, SpeedMod speedMod = SpeedMod.Normal) => (spd + 30) * ((byte)speedMod / 2);
  372. public static T Load<T>(BinaryReader br, Enum @enum, Saves.Data data) where T : Damageable, new()
  373. {
  374. T r = new T { Data = data };
  375. r.ReadData(br, @enum);
  376. r.Init();
  377. return r;
  378. }
  379. protected Saves.Data Data { get; set; }
  380. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  381. public static int TimeToFillBarGF(int spd) => 200 * (int)Memory.CurrentBattleSpeed / (3 * (spd + 30));
  382. /// <summary>
  383. /// Starting value
  384. /// </summary>
  385. /// <param name="spd"></param>
  386. /// <returns></returns>
  387. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  388. public virtual int ATBBarStart(int spd)
  389. {
  390. int i = 0;
  391. //this verison loops till it gets a value between 0 and ATBBarSize. Unsure which is best.
  392. //do
  393. //{
  394. // i = ((spd / 4) + Memory.Random.Next(128) - 34) * (int)Memory.CurrentBattleSpeed * 40;
  395. //}
  396. //while (i <= 0 || i > ATBBarSize);
  397. //return i;
  398. //this version will return ATBBarSize if larger and 0 if less than 0.
  399. i = ((spd / 4) + Memory.Random.Next(128) - 34) * (int)Memory.CurrentBattleSpeed * 40;
  400. if (i > 0 && i < ATBBarSize)
  401. return i;
  402. else if (i < 0) return 0;
  403. else return ATBBarSize;
  404. }
  405. public int ATBBarStart()
  406. {
  407. if (IsGameOver)
  408. return 0;
  409. return ATBBarStart(SPD);
  410. }
  411. public virtual bool ATBCharged()
  412. {
  413. if (GetBattleMode().Equals(BattleMode.ATB_Charging))
  414. {
  415. SetBattleMode(BattleMode.ATB_Charged);
  416. return true;
  417. }
  418. return false;
  419. }
  420. public virtual int BarIncrement() => BarIncrement(SPD, GetSpeedMod());
  421. public bool ChangeHP(int dmg)
  422. {
  423. if (dmg == 0 || (Statuses1 & Kernel_bin.Battle_Only_Statuses.Invincible) != 0)
  424. return false;
  425. int hp = _CurrentHP - dmg;
  426. ushort lasthp = _CurrentHP;
  427. if (hp <= 0)
  428. {
  429. Statuses0 |= (Kernel_bin.Persistent_Statuses.Death);
  430. _CurrentHP = 0;
  431. }
  432. else
  433. {
  434. _CurrentHP = (ushort)(hp > ushort.MaxValue ? ushort.MaxValue : hp);
  435. //use a CurrentHP method or property to adjust this to correct maxhp.
  436. //if teamlaguna the max hp can be different and each gf has there own.
  437. }
  438. CurrentHP();
  439. if (lasthp == _CurrentHP) return false;
  440. Debug.WriteLine($"{this}: Dealt {dmg}, previous hp: {lasthp}, current hp: {_CurrentHP}");
  441. return true;
  442. }
  443. public virtual bool ChargeGF()
  444. {
  445. if (GetBattleMode().Equals(BattleMode.YourTurn) && (this.GetType().Equals(typeof(Saves.CharacterData))))
  446. {
  447. SetBattleMode(BattleMode.ATB_Charged);
  448. return true;
  449. }
  450. return false;
  451. }
  452. public abstract Damageable Clone();
  453. public virtual bool IsCritical => CurrentHP() <= CriticalHP();
  454. public virtual ushort CriticalHP() => (ushort)((MaxHP() / 4) - 1);
  455. /// <summary>
  456. /// Override This: to check vs your max hp value.
  457. /// </summary>
  458. /// <returns></returns>
  459. public virtual ushort CurrentHP()
  460. {
  461. ushort max = MaxHP();
  462. if (_CurrentHP > max) _CurrentHP = max;
  463. return _CurrentHP;
  464. }
  465. public bool DealDamage(int dmg, Kernel_bin.Attack_Type type, Kernel_bin.Attack_Flags? flags)
  466. {
  467. if (DamageActions.ContainsKey(type))
  468. {
  469. //ChangeHP(-dmg);
  470. // check max and min values
  471. if (dmg > Kernel_bin.MAX_HP_VALUE && type != Kernel_bin.Attack_Type.Give_Percentage_HP) dmg = Kernel_bin.MAX_HP_VALUE;
  472. if (dmg < 0) return false;
  473. // depending on damage type see if damage is correct
  474. dmg = DamageActions[type](dmg, flags ?? Kernel_bin.Attack_Flags.None);
  475. return dmg != 0;
  476. }
  477. return false;
  478. }
  479. public bool DealStatus(
  480. Kernel_bin.Persistent_Statuses? statuses0,
  481. Kernel_bin.Battle_Only_Statuses? statuses1,
  482. Kernel_bin.Attack_Type type,
  483. Kernel_bin.Attack_Flags? flags)
  484. {
  485. if (StatusesActions.ContainsKey(type))
  486. {
  487. int total = StatusesActions[type](
  488. statuses0 ?? Kernel_bin.Persistent_Statuses.None,
  489. statuses1 ?? Kernel_bin.Battle_Only_Statuses.None,
  490. flags ?? Kernel_bin.Attack_Flags.None);
  491. return total != 0;
  492. }
  493. return false;
  494. }
  495. public abstract short ElementalResistance(Kernel_bin.Element @in);
  496. public virtual bool EndTurn(bool force = false)
  497. {
  498. if (force ||
  499. GetBattleMode().Equals(BattleMode.YourTurn) ||
  500. GetBattleMode().Equals(BattleMode.GF_Charging)
  501. )
  502. {
  503. SetBattleMode(BattleMode.EndTurn); // trigger any end of turn clean up.
  504. SetBattleMode(BattleMode.ATB_Charging); //start charging next turn.
  505. ATBTimer.NewTurn();
  506. Refresh();
  507. return true;
  508. }
  509. return false;
  510. }
  511. public virtual Enum GetBattleMode() => _battlemode ?? BattleMode.ATB_Charging;
  512. public bool GetCast<T>(out T cast) where T : Damageable
  513. {
  514. if (GetType() == typeof(T))
  515. {
  516. cast = (T)this;
  517. return true;
  518. }
  519. cast = null;
  520. return false;
  521. }
  522. public bool GetCharacterData(out Saves.CharacterData character)
  523. => GetCast(out character);
  524. public bool GetEnemy(out Enemy enemy)
  525. => GetCast(out enemy);
  526. public bool GetGFData(out Saves.GFData gf)
  527. => GetCast(out gf);
  528. public SpeedMod GetSpeedMod()
  529. {
  530. if ((Statuses1 & Kernel_bin.Battle_Only_Statuses.Haste) != 0)
  531. return SpeedMod.Haste;
  532. else if ((Statuses1 & Kernel_bin.Battle_Only_Statuses.Stop) != 0 || IsGameOver)
  533. return SpeedMod.Stop;
  534. else if ((Statuses1 & Kernel_bin.Battle_Only_Statuses.Slow) != 0)
  535. return SpeedMod.Slow;
  536. return SpeedMod.Normal;
  537. }
  538. public virtual bool GFDiedWhileCharging()
  539. {
  540. if (GetBattleMode().Equals(BattleMode.GF_Charging))
  541. {
  542. SetBattleMode(BattleMode.YourTurn);
  543. return true;
  544. }
  545. return false;
  546. }
  547. /// <summary>
  548. /// Overload this: To get correct MaxHP value
  549. /// </summary>
  550. /// <returns></returns>
  551. public virtual ushort MaxHP() => ushort.MaxValue;
  552. public virtual float PercentFullHP() => (float)CurrentHP() / MaxHP();
  553. public virtual void Refresh() => ATBTimer.Refresh(this);
  554. public virtual void Reset()
  555. {
  556. ATBTimer.Reset();
  557. Statuses1 = 0;
  558. }
  559. public virtual ushort ReviveHP() => (ushort)(MaxHP() / 8);
  560. protected virtual bool SetBattleMode(Enum mode)
  561. {
  562. if (!(_battlemode?.Equals(mode) ?? false))
  563. {
  564. _battlemode = mode;
  565. BattleModeChangeEventHandler?.Invoke(this, mode);
  566. return true;
  567. }
  568. return false;
  569. }
  570. public void SetSummon(Saves.GFData gfdata)
  571. {
  572. SummonedGF = gfdata;
  573. if (SummonedGF != null)
  574. {
  575. SetBattleMode(BattleMode.GF_Charging);
  576. SummonedGF.EndTurn(true);
  577. }
  578. }
  579. /// <summary>
  580. /// Summon GF
  581. /// </summary>
  582. /// <param name="gf"></param>
  583. public void SetSummon(GFs gf)
  584. {
  585. Saves.GFData gfdata = null;
  586. if (Memory.State?.GFs?.TryGetValue(gf, out gfdata) ?? false)
  587. {
  588. // found a gf.
  589. }
  590. SetSummon(gfdata);
  591. }
  592. public virtual bool StartTurn()
  593. {
  594. if (
  595. GetBattleMode().Equals(BattleMode.ATB_Charged)
  596. )
  597. {
  598. SetBattleMode(BattleMode.YourTurn); //it's your turn.
  599. Refresh();
  600. return true;
  601. }
  602. return false;
  603. }
  604. public abstract sbyte StatusResistance(Kernel_bin.Battle_Only_Statuses s);
  605. public abstract sbyte StatusResistance(Kernel_bin.Persistent_Statuses s);
  606. public virtual bool Switch()
  607. {
  608. if (GetBattleMode().Equals(BattleMode.YourTurn))
  609. {
  610. SetBattleMode(BattleMode.ATB_Charged);
  611. return true;
  612. }
  613. return false;
  614. }
  615. public float TicksToFillBar(int start, int spd, SpeedMod speedMod = SpeedMod.Normal)
  616. {
  617. int top = (ATBBarSize - start);
  618. int bot = BarIncrement(spd, speedMod);
  619. if (bot == 0)
  620. return float.MinValue;
  621. return (top / bot);
  622. }
  623. public float TicksToFillBar() => TicksToFillBar(ATBBarStart(), SPD, GetSpeedMod());
  624. public float TimeToFillBar(int start, int spd, SpeedMod speedMod = SpeedMod.Normal)
  625. {
  626. float tickspersec = 60f;
  627. return TicksToFillBar(start, spd, speedMod) / tickspersec;
  628. }
  629. public float TimeToFillBar() => TimeToFillBar(ATBBarStart(), SPD, GetSpeedMod());
  630. public int TimeToFillBarGF() => TimeToFillBarGF(SPD);
  631. public abstract ushort TotalStat(Kernel_bin.Stat s);
  632. public virtual bool Update(bool force = false)
  633. {
  634. if (GetBattleMode().Equals(BattleMode.ATB_Charging) || force)
  635. {
  636. if (!Module_battle_debug.PauseATB)
  637. ATBTimer.Update();
  638. if (ATBTimer.Done && ATBCharged())
  639. {
  640. //Your turn is ready.
  641. }
  642. return true;
  643. }
  644. else if (GetBattleMode().Equals(BattleMode.GF_Charging))
  645. {
  646. SummonedGF.Update();
  647. if (SummonedGF.IsGameOver || (SummonedGF.ATBTimer.Done && EndTurn()))
  648. {
  649. //Summon GF end turn.
  650. //SetSummon(null);
  651. }
  652. return true;
  653. }
  654. return false;
  655. }
  656. protected virtual void Init()
  657. {
  658. SetBattleMode(BattleMode.ATB_Charging);
  659. ATBTimer = new ATBTimer(this);
  660. }
  661. protected abstract void ReadData(BinaryReader br, Enum @enum);
  662. #endregion Methods
  663. }
  664. }