Saves.GFData.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. namespace OpenVIII
  7. {
  8. public static partial class Saves
  9. {
  10. #region Classes
  11. /// <summary>
  12. /// Data for each GF
  13. /// </summary>
  14. /// <see cref="http://wiki.ffrtt.ru/index.php/FF8/GameSaveFormat#Guardian_Forces"/>
  15. public class GFData : Damageable
  16. {
  17. #region Fields
  18. /// <summary>
  19. /// <para>0x14 (1 byte = 1 ability of the GF, 2 bytes unused)</para>
  20. /// <para>AP of the abilties the GF can learn. See Kernel_Bin for list and prerequirements.</para>
  21. /// </summary>
  22. public byte[] APs;
  23. /// <summary>
  24. /// <para>0x12 abilities (1 bit = 1 ability completed, 9 bits unused)</para>
  25. /// </summary>
  26. public BitArray Complete;
  27. /// <summary>
  28. /// 0x10 Seems to be 0 for false and 1 for true.
  29. /// </summary>
  30. public bool Exists;
  31. /// <summary>
  32. /// <para>0x00 Total exp for GF</para>
  33. /// <para>Exp per level is in Kernel_bin</para>
  34. /// </summary>
  35. public uint Experience;
  36. /// <summary>
  37. /// <para>0x41 abilities (1 bit = 1 ability of the GF forgotten, 2 bits unused)</para>
  38. /// <para>I think: Corrisponds to APs. Basically disables learnable abilities.</para>
  39. /// </summary>
  40. public BitArray Forgotten;
  41. /// <summary>
  42. /// <para>0x24 Number of kills</para>
  43. /// </summary>
  44. public ushort NumberKills;
  45. /// <summary>
  46. /// <para>0x3C Number of KOs</para>
  47. /// </summary>
  48. public ushort NumberKOs;
  49. /// <summary>
  50. /// 0x10 unknown value
  51. /// </summary>
  52. public byte Unknown;
  53. #endregion Fields
  54. #region Constructors
  55. public GFData()
  56. {
  57. }
  58. #endregion Constructors
  59. //public GFData(BinaryReader br, GFs g) => Read(br, g);
  60. #region Properties
  61. /// <summary>
  62. /// GF version is compatibility duration.
  63. /// </summary>
  64. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  65. public override int ATBBarSize
  66. {
  67. get
  68. {
  69. int Compability = ShownCompability;
  70. return (int)(Compability * (int)Memory.CurrentBattleSpeed * 0.9143 / 32);
  71. }
  72. }
  73. public override int BarIncrement() => (int)GetSpeedMod();
  74. public override int ATBBarStart(int spd) => 0;
  75. public override byte EVA => 0;
  76. public override int EXP => checked((int)Experience);
  77. /// <summary>
  78. /// Total exp to the next level up
  79. /// </summary>
  80. public ushort EXPtoNextLevel => Level >= 100 ? (ushort)0 :
  81. (ushort)Math.Abs((Level * JunctionableGFsData.EXPperLevel) - Experience);
  82. /// <summary>
  83. /// Gf ability data
  84. /// </summary>
  85. private IReadOnlyDictionary<Kernel_bin.Abilities, Kernel_bin.GF_abilities> GFabilities => Kernel_bin.GFabilities;
  86. public override byte HIT => 0;
  87. /// <summary>
  88. /// Enum ID for this GF
  89. /// </summary>
  90. public GFs ID { get; private set; }
  91. /// <summary>
  92. /// Kernel bin data on this GF
  93. /// </summary>
  94. private Kernel_bin.Junctionable_GFs_Data JunctionableGFsData
  95. {
  96. get
  97. {
  98. if (
  99. Kernel_bin.JunctionableGFsData != null &&
  100. Kernel_bin.JunctionableGFsData.TryGetValue(ID, out Kernel_bin.Junctionable_GFs_Data value))
  101. {
  102. return value;
  103. }
  104. return null;
  105. }
  106. }
  107. /// <summary>
  108. /// This is the ability that will gain AP from battles.
  109. /// </summary>
  110. public Kernel_bin.Abilities Learning { get; private set; }
  111. /// <summary>
  112. /// Current Level for this GF
  113. /// </summary>
  114. public override byte Level
  115. {
  116. get
  117. {
  118. Kernel_bin.Junctionable_GFs_Data junctionableGFsData = JunctionableGFsData;
  119. if (junctionableGFsData != null)
  120. {
  121. uint ret = (Experience / junctionableGFsData.EXPperLevel) + 1;
  122. return ret > 100 ? (byte)100 : (byte)ret;
  123. }
  124. return 0;
  125. }
  126. }
  127. public override byte LUCK => 0;
  128. public override byte MAG => 0;
  129. /// <summary>
  130. /// True if at max.
  131. /// </summary>
  132. public bool MaxGFAbilities => (from bool m in Complete
  133. where m
  134. select m).Count() >= 22;
  135. /// <summary>
  136. /// Total amount of percent based hp buff
  137. /// </summary>
  138. public int Percent
  139. {
  140. get
  141. {
  142. int p = 0;
  143. List<Kernel_bin.Abilities> unlocked = UnlockedAbilities;
  144. if (unlocked.Contains(Kernel_bin.Abilities.GFHP_10))
  145. p += 10;
  146. if (unlocked.Contains(Kernel_bin.Abilities.GFHP_20))
  147. p += 20;
  148. if (unlocked.Contains(Kernel_bin.Abilities.GFHP_30))
  149. p += 30;
  150. if (unlocked.Contains(Kernel_bin.Abilities.GFHP_40))
  151. p += 40;
  152. return p;
  153. }
  154. }
  155. public CompatibilitywithGF ShownCompability
  156. {
  157. get
  158. {
  159. if (Memory.State.JunctionedGFs().TryGetValue(ID, out Characters c) &&
  160. Memory.State[c].CompatibilitywithGFs.TryGetValue(ID, out Saves.CompatibilitywithGF value))
  161. {
  162. return value;
  163. }
  164. return 0;
  165. }
  166. }
  167. public override byte SPD => 0;
  168. public override byte SPR => 0;
  169. public override byte STR => 0;
  170. /// <summary>
  171. /// Unlocked abilities
  172. /// </summary>
  173. public List<Kernel_bin.Abilities> UnlockedAbilities
  174. {
  175. get
  176. {
  177. List<Kernel_bin.Abilities> abilities = new List<Kernel_bin.Abilities>();
  178. for (int i = 1; Complete != null && i < Complete.Length; i++)//0 is none so skipping it.
  179. {
  180. if (Complete[i])
  181. abilities.Add((Kernel_bin.Abilities)i);
  182. }
  183. return abilities;
  184. }
  185. }
  186. public override byte VIT => 0;
  187. #endregion Properties
  188. #region Methods
  189. public static GFData Load(BinaryReader br, GFs @enum, Saves.Data data) => Load<GFData>(br, @enum,data);
  190. /// <summary>
  191. /// Create a copy of this gfdata object
  192. /// </summary>
  193. public override Damageable Clone()
  194. {
  195. //Shadowcopy
  196. GFData c = (GFData)MemberwiseClone();
  197. //Deepcopy
  198. c.Name = Name.Clone();
  199. c.Complete = (BitArray)(Complete.Clone());
  200. c.Forgotten = (BitArray)(Forgotten.Clone());
  201. c.APs = (byte[])(APs.Clone());
  202. return c;
  203. }
  204. public bool EarnExp(uint ap, out Kernel_bin.Abilities ability)
  205. {
  206. bool ret = false;
  207. ability = Kernel_bin.Abilities.None;
  208. if (!IsGameOver)
  209. {
  210. if (EXPtoNextLevel <= ap && Level < 100)
  211. ret = true;
  212. Experience += ap;
  213. if (!Learning.Equals(Kernel_bin.Abilities.None) && Kernel_bin.AllAbilities.ContainsKey(Learning))
  214. {
  215. byte ap_tolearn = Kernel_bin.AllAbilities[Learning].AP;
  216. if (JunctionableGFsData.Ability.TryGetIndexByKey(Learning, out int ind) && TestGFCanLearn(Learning, false))
  217. {
  218. if (ap_tolearn < APs[ind] + ap)
  219. {
  220. APs[ind] = ap_tolearn;
  221. ability = Learning;
  222. Learn(Learning);
  223. }
  224. else
  225. {
  226. APs[ind] += (byte)ap;
  227. }
  228. }
  229. }
  230. }
  231. return ret;
  232. }
  233. public override short ElementalResistance(Kernel_bin.Element @in) => throw new System.NotImplementedException();
  234. /// <summary>
  235. /// Learn this ability
  236. /// </summary>
  237. /// <param name="ability"></param>
  238. /// <returns></returns>
  239. public bool Learn(Kernel_bin.Abilities ability)
  240. {
  241. if (!MaxGFAbilities)
  242. {
  243. if (!Complete[(int)ability])
  244. {
  245. Complete[(int)ability] = true;
  246. if (Learning.Equals(ability))
  247. {
  248. SetLearning();
  249. }
  250. return true;
  251. }
  252. }
  253. return false;
  254. }
  255. /// <summary>
  256. /// Max HP for GF
  257. /// </summary>
  258. public override ushort MaxHP()
  259. {
  260. Kernel_bin.Junctionable_GFs_Data junctionableGFsData = JunctionableGFsData;
  261. if (junctionableGFsData != null)
  262. {
  263. int max = ((Level * Level / 25) + 250 + junctionableGFsData.HP_MOD * Level) * (Percent + 100) / 100;
  264. return (ushort)(max > Kernel_bin.MAX_HP_VALUE ? Kernel_bin.MAX_HP_VALUE : max);
  265. }
  266. return 0;
  267. }
  268. /// <summary>
  269. /// <para>Set which Ability the GF is learning.</para>
  270. /// <para>Could actually be a byte to corrispond to APs and Forgotten.</para>
  271. /// </summary>
  272. /// <param name="ability">If null sets to first in learnable list</param>
  273. public void SetLearning(Kernel_bin.Abilities? _ability = null)
  274. {
  275. Kernel_bin.Abilities a = _ability ?? Kernel_bin.Abilities.None;
  276. bool _set = false;
  277. if (a == Kernel_bin.Abilities.None)
  278. {
  279. foreach (KeyValuePair<Kernel_bin.Abilities, Kernel_bin.Unlocker> kvp in JunctionableGFsData.Ability)
  280. {
  281. if (TestGFCanLearn(kvp.Key, false))
  282. {
  283. Learning = kvp.Key;
  284. _set = true;
  285. break;
  286. }
  287. }
  288. }
  289. else if (TestGFCanLearn(a, false))
  290. {
  291. Learning = a;
  292. _set = true;
  293. }
  294. if (!_set)
  295. Learning = Kernel_bin.Abilities.None;
  296. }
  297. public override sbyte StatusResistance(Kernel_bin.Battle_Only_Statuses s) => sbyte.MaxValue;
  298. public override sbyte StatusResistance(Kernel_bin.Persistent_Statuses s) => sbyte.MaxValue;
  299. /// <summary>
  300. /// False if gf knows ability, True if can learn it.
  301. /// </summary>
  302. /// <param name="ability">Ability you want to learn.</param>
  303. /// <param name="item">
  304. /// If using an Item you don't need the prereq, set to false if need prereq
  305. /// </param>
  306. public bool TestGFCanLearn(Kernel_bin.Abilities ability, bool item = true) => !Complete[(int)ability] && ((item) || UnlockerTest(ability));
  307. /// <summary>
  308. /// If converting to string display GF's Name.
  309. /// </summary>
  310. public override string ToString() => Name.ToString();
  311. public override ushort TotalStat(Kernel_bin.Stat s) => 0;
  312. public bool UnlockerTest(Kernel_bin.Abilities a)
  313. {
  314. if (JunctionableGFsData.Ability.TryGetByKey(a, out Kernel_bin.Unlocker u))
  315. {
  316. return UnlockerTest(u);
  317. }
  318. //return true if there is no prereq.
  319. return true;
  320. }
  321. public bool UnlockerTest(Kernel_bin.Unlocker u)
  322. {
  323. if (u == Kernel_bin.Unlocker.None)
  324. {
  325. return true;
  326. }
  327. else if (u < Kernel_bin.Unlocker.GFLevel100)
  328. {
  329. return ((byte)u <= Level);
  330. }
  331. else
  332. {
  333. int ind = (u - Kernel_bin.Unlocker.GFLevel100);
  334. if (JunctionableGFsData.Ability.TryGetKeyByIndex(ind, out Kernel_bin.Abilities key))
  335. return Complete[(int)key];
  336. else
  337. return false;
  338. }
  339. }
  340. /// <summary>
  341. /// Read in values from safe data.
  342. /// </summary>
  343. /// <param name="br">Binary Reader to raw save data</param>
  344. /// <param name="g">Which GF we are reading in</param>
  345. protected override void ReadData(BinaryReader br, Enum @enum)
  346. {
  347. if ([email protected]().Equals(typeof(GFs))) throw new ArgumentException($"Enum {@enum} is not GFs");
  348. StatusImmune = true;
  349. ID = (GFs)@enum;
  350. Name = br.ReadBytes(12);//0x00 (0x00 terminated)
  351. if (string.IsNullOrWhiteSpace(Name)) Name = Memory.Strings.GetName((GFs)@enum);
  352. Experience = br.ReadUInt32();//0x0C
  353. Unknown = br.ReadByte();//0x10
  354. Exists = br.ReadByte() == 1 ? true : false;//0x11 //1 unlocked //0 locked
  355. _CurrentHP = br.ReadUInt16();//0x12
  356. Complete = new BitArray(br.ReadBytes(16));//0x14 abilities (1 bit = 1 ability completed, 9 bits unused)
  357. APs = br.ReadBytes(24);//0x24 (1 byte = 1 ability of the GF, 2 bytes unused)
  358. NumberKills = br.ReadUInt16();//0x3C of kills
  359. NumberKOs = br.ReadUInt16();//0x3E of KOs
  360. Learning = (Kernel_bin.Abilities)br.ReadByte();//0x41 ability
  361. Forgotten = new BitArray(br.ReadBytes(3));//0x42 abilities (1 bit = 1 ability of the GF forgotten, 2 bits unused)
  362. }
  363. #endregion Methods
  364. }
  365. #endregion Classes
  366. }
  367. }