Saves.CharacterData.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. using Microsoft.Xna.Framework;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. namespace OpenVIII
  8. {
  9. public static partial class Saves
  10. {
  11. #region Enums
  12. /// <summary>
  13. /// </summary>
  14. /// <remarks>Hyne has switchlocked0 and 1</remarks>
  15. [Flags]
  16. public enum Exists : byte
  17. {
  18. Unavailable = 0x0,
  19. /// <summary>
  20. /// Shows in menu.
  21. /// </summary>
  22. Available = 0x1,
  23. /// <summary>
  24. /// Party members that cannot leave or be added
  25. /// </summary>
  26. SwitchLocked0 = 0x2,
  27. /// <summary>
  28. /// Party members that cannot leave or be added
  29. /// </summary>
  30. SwitchLocked1 = 0x4,
  31. /// <summary>
  32. /// Many have this set. I donno what it does.
  33. /// </summary>
  34. Unk8 = 0x8,
  35. /// <summary>
  36. /// Many have this set. I donno what it does.
  37. /// </summary>
  38. Unk10 = 0x10,
  39. /// <summary>
  40. /// Many have this set. I donno what it does.
  41. /// </summary>
  42. Unk20 = 0x20,
  43. /// <summary>
  44. /// Many have this set. I donno what it does.
  45. /// </summary>
  46. Unk30 = 0x40,
  47. /// <summary>
  48. /// Many have this set. I donno what it does.
  49. /// </summary>
  50. Unk40 = 0x80,
  51. }
  52. #endregion Enums
  53. #region Classes
  54. /// <summary>
  55. /// Data for each Character
  56. /// </summary>
  57. /// <see cref="http://wiki.ffrtt.ru/index.php/FF8/GameSaveFormat#Characters"/>
  58. public partial class CharacterData : Damageable, ICharacterData
  59. {
  60. #region Fields
  61. /// <summary>
  62. /// Total amount of spells the will be loaded/saved.
  63. /// </summary>
  64. private const int MagicCapacity = 32;
  65. /// <summary>
  66. /// Total Exp
  67. /// </summary>
  68. private uint experience;
  69. #endregion Fields
  70. #region Methods
  71. private void Auto(IReadOnlyList<Kernel_bin.Stat> list)
  72. {
  73. RemoveMagic();
  74. List<Kernel_bin.Abilities> unlockedlist = UnlockedGFAbilities;
  75. foreach (Kernel_bin.Stat stat in list)
  76. {
  77. if (Unlocked(unlockedlist, stat))
  78. foreach (Kernel_bin.Magic_Data spell in SortedMagic(stat))
  79. {
  80. if (!Stat_J.ContainsValue(spell.ID))
  81. {
  82. //TODO make smarter.
  83. //example if you can get max stat with a weaker spell use that first.
  84. // if stat is max with out spell skip
  85. if (stat != Kernel_bin.Stat.HP && TotalStat(stat) == Kernel_bin.MAX_STAT_VALUE) break;
  86. // if hp is max without spell skip
  87. else if (stat == Kernel_bin.Stat.HP && TotalStat(stat) == Kernel_bin.MAX_HP_VALUE) break;
  88. // junction spell
  89. else Stat_J[stat] = spell.ID;
  90. break;
  91. }
  92. }
  93. }
  94. }
  95. public OrderedDictionary<byte, byte> CloneMagic() => Magics.Clone();
  96. public Dictionary<Kernel_bin.Stat, byte> CloneMagicJunction() => new Dictionary<Kernel_bin.Stat, byte>(Stat_J);
  97. #endregion Methods
  98. /// <summary>
  99. /// Raw HP buff from items.
  100. /// </summary>
  101. public ushort _HP;
  102. /// <summary>
  103. /// Junctioned Abilities
  104. /// </summary>
  105. public List<Kernel_bin.Abilities> Abilities;
  106. /// <summary>
  107. /// <para>Alt Models/different costumes</para>
  108. /// <para>(Normal, SeeD, Soldier...)</para>
  109. /// </summary>
  110. public byte Alternativemodel;
  111. /// <summary>
  112. /// Junctioned Commands
  113. /// </summary>
  114. public List<Kernel_bin.Abilities> Commands;
  115. /// <summary>
  116. /// <para>Compatibility With GFs</para>
  117. /// <para>Effects Summon speed and such.</para>
  118. /// </summary>
  119. public Dictionary<GFs, CompatibilitywithGF> CompatibilitywithGFs;
  120. /// <summary>
  121. /// Value determines if a character shows in menu and can be added to party.
  122. /// <para>
  123. /// 15,9,7,4,1 shows on menu, 0 locked, 6 hidden // I think I wonder if this is a flags value.
  124. /// </para>
  125. /// </summary>
  126. public Exists Exists;
  127. /// <summary>
  128. /// Juctioned GFs - raw value
  129. /// </summary>
  130. private GFflags rawJunctionedGFs;
  131. /// <summary>
  132. /// Juctioned GFs
  133. /// </summary>
  134. public IEnumerable<GFs> JunctionedGFs => Enum.GetValues(rawJunctionedGFs.GetType()).Cast<GFflags>().Where(x => rawJunctionedGFs.HasFlag(x) && ConvertGFEnum.ContainsKey(x)).Distinct().Select(x=> ConvertGFEnum[x]);
  135. public void JunctionGF(GFs gf) =>
  136. rawJunctionedGFs |= Saves.ConvertGFEnum.FirstOrDefault(x => x.Value == gf).Key;
  137. public void RemoveJunctionedGF(GFs gf) =>
  138. rawJunctionedGFs ^= Saves.ConvertGFEnum.FirstOrDefault(x => x.Value == gf).Key;
  139. //public byte _STR; //0x09
  140. //public byte _VIT; //0x0A
  141. //public byte _MAG; //0x0B
  142. //public byte _SPR; //0x0C
  143. //public byte _SPD; //0x0D
  144. //public byte _LCK; //0x0E
  145. public OrderedDictionary<byte, byte> Magics;
  146. /// <summary>
  147. /// Character Model
  148. /// </summary>
  149. public byte ModelID;
  150. /// <summary>
  151. /// Number of Kills
  152. /// </summary>
  153. public ushort Numberofkills;
  154. /// <summary>
  155. /// Number of KOs
  156. /// </summary>
  157. public ushort NumberofKOs;
  158. public byte Paddingorunusedcommand;
  159. /// <summary>
  160. /// Stats that can be incrased via items. Except for HP because it's a ushort not a byte.
  161. /// </summary>
  162. public Dictionary<Kernel_bin.Stat, byte> RawStats;
  163. /// <summary>
  164. /// Junctioned Magic per stat.
  165. /// </summary>
  166. public Dictionary<Kernel_bin.Stat, byte> Stat_J;
  167. public byte Unknown1;
  168. public byte Unknown2;
  169. public byte Unknown3;
  170. public byte Unknown4;
  171. /// <summary>
  172. /// Weapon
  173. /// </summary>
  174. public byte WeaponID;
  175. //public CharacterData(BinaryReader br, Characters c) => Read(br, c);
  176. public CharacterData() { }
  177. /// <summary>
  178. /// 25.4% chance to cast automaticly on gameover, if used once in battle
  179. /// </summary>
  180. /// <remarks>
  181. /// Memory.State.Fieldvars. has a value that tracks if PhoenixPinion is used just need to
  182. /// find it
  183. /// </remarks>
  184. public bool CanPhoenixPinion => IsDead && !(IsPetrify || (Statuses1 & (Kernel_bin.Battle_Only_Statuses.Eject)) != 0) && Memory.State.Items.Where(m => m.ID == 31 && m.QTY >= 1).Count() > 0;
  185. public Module_battle_debug.CharacterInstanceInformation CII { get; private set; }
  186. /// <summary>
  187. /// Set by GenerateCrisisLevel(), -1 means no limit break. &gt;=0 has a limit break.
  188. /// </summary>
  189. /// <returns>-1 - 4</returns>
  190. /// <remarks>https://finalfantasy.fandom.com/wiki/Crisis_Level</remarks>
  191. public sbyte CurrentCrisisLevel { get; private set; }
  192. public override int EXP => checked((int)Experience);
  193. public uint Experience
  194. {
  195. get => experience; set
  196. {
  197. if (experience == 0)
  198. experience = value;
  199. else if (!IsGameOver && experience != value)
  200. experience = value; //trying to give my self a good break point
  201. }
  202. }
  203. public ushort ExperienceToNextLevel => (ushort)(Level == 100 ? 0 : MathHelper.Clamp(CharacterStats.EXP((byte)(Level + 1)) - Experience, 0, CharacterStats.EXP(2)));
  204. private Characters id;
  205. /// <summary>
  206. /// If TeamLaguna the id will change to a Luguna Party member
  207. /// </summary>
  208. public Characters ID
  209. {
  210. get
  211. {
  212. int ind = 0;
  213. if (Memory.State != null && Memory.State.TeamLaguna && (ind = Memory.State.PartyData.FindIndex(x => x.Equals(id))) >= 0 && !Memory.State.Party.Contains(id))
  214. return Memory.State.Party[ind];
  215. return id;
  216. }
  217. }
  218. public override bool IsCritical => CurrentHP() <= CriticalHP();
  219. public override byte Level
  220. {
  221. get
  222. {
  223. if (CharacterStats != null)
  224. return CharacterStats.LEVEL(Experience);
  225. return 0;
  226. }
  227. }
  228. /// <summary>
  229. /// If TeamLaguna the name will change to a Luguna Party member
  230. /// </summary>
  231. public override FF8String Name
  232. {
  233. get
  234. {
  235. if (Memory.State != null && Memory.State.TeamLaguna)
  236. {
  237. return Memory.Strings.GetName(ID);
  238. }
  239. return base.Name;
  240. }
  241. set => base.Name = value;
  242. }
  243. public List<Kernel_bin.Abilities> UnlockedGFAbilities
  244. {
  245. get
  246. {
  247. BitArray total = new BitArray(16 * 8);
  248. List<Kernel_bin.Abilities> abilities = new List<Kernel_bin.Abilities>();
  249. foreach (GFs gf in JunctionedGFs)
  250. {
  251. total.Or(Memory.State.GFs[gf].Complete);
  252. }
  253. for (int i = 1; i < total.Length; i++)//0 is none so skipping it.
  254. {
  255. if (total[i])
  256. abilities.Add((Kernel_bin.Abilities)i);
  257. }
  258. return abilities;
  259. }
  260. }
  261. //0x6B (padding?)
  262. /// <summary>
  263. /// Visible
  264. /// </summary>
  265. public bool Available => (Exists & Exists.Available) != 0;
  266. /// <summary>
  267. /// Kernel Stats
  268. /// </summary>
  269. public Kernel_bin.Character_Stats CharacterStats
  270. {
  271. get
  272. {
  273. if (Kernel_bin.CharacterStats != null && Kernel_bin.CharacterStats.TryGetValue(id, out Kernel_bin.Character_Stats value))
  274. return value;
  275. return null;
  276. }
  277. }
  278. public override byte EVA => checked((byte)TotalStat(Kernel_bin.Stat.EVA));
  279. public override byte HIT => checked((byte)TotalStat(Kernel_bin.Stat.HIT));
  280. public override byte LUCK => checked((byte)TotalStat(Kernel_bin.Stat.LUCK));
  281. public override byte MAG => checked((byte)TotalStat(Kernel_bin.Stat.MAG));
  282. public override byte SPD => checked((byte)TotalStat(Kernel_bin.Stat.SPD));
  283. public override byte SPR => checked((byte)TotalStat(Kernel_bin.Stat.SPR));
  284. public override byte STR => checked((byte)TotalStat(Kernel_bin.Stat.STR));
  285. /// <summary>
  286. /// Cannot remove from party or add to party.
  287. /// </summary>
  288. public bool SwitchLocked => (Exists & (Exists.SwitchLocked0 | Exists.SwitchLocked1)) != 0;
  289. public override byte VIT => checked((byte)TotalStat(Kernel_bin.Stat.VIT));
  290. public void AutoATK() => Auto(Kernel_bin.AutoATK);
  291. public void AutoDEF() => Auto(Kernel_bin.AutoDEF);
  292. public void AutoMAG() => Auto(Kernel_bin.AutoMAG);
  293. public void BattleStart(Module_battle_debug.CharacterInstanceInformation cii)
  294. {
  295. CII = cii;
  296. Statuses1 = Kernel_bin.Battle_Only_Statuses.None;
  297. if (Abilities.Contains(Kernel_bin.Abilities.Auto_Haste))
  298. Statuses1 |= Kernel_bin.Battle_Only_Statuses.Haste;
  299. if (Abilities.Contains(Kernel_bin.Abilities.Auto_Protect))
  300. Statuses1 |= Kernel_bin.Battle_Only_Statuses.Protect;
  301. if (Abilities.Contains(Kernel_bin.Abilities.Auto_Reflect))
  302. Statuses1 |= Kernel_bin.Battle_Only_Statuses.Reflect;
  303. if (Abilities.Contains(Kernel_bin.Abilities.Auto_Shell))
  304. Statuses1 |= Kernel_bin.Battle_Only_Statuses.Shell;
  305. //reset the ATB timer.
  306. ATBTimer.FirstTurn();
  307. }
  308. public override Damageable Clone()
  309. {
  310. //Shadowcopy
  311. CharacterData c = (CharacterData)MemberwiseClone();
  312. //Deepcopy
  313. c.Name = Name.Clone();
  314. c.CompatibilitywithGFs = CompatibilitywithGFs.ToDictionary(e => e.Key, e => e.Value);
  315. c.Stat_J = Stat_J.ToDictionary(e => e.Key, e => e.Value);
  316. c.Magics = new OrderedDictionary<byte, byte>(Magics.Count);
  317. foreach (KeyValuePair<byte, byte> magic in Magics)
  318. c.Magics.Add(magic.Key, magic.Value);
  319. c.RawStats = RawStats.ToDictionary(e => e.Key, e => e.Value);
  320. c.Commands = Commands.ConvertAll(Item => Item);
  321. c.Abilities = Abilities.ConvertAll(Item => Item);
  322. return c;
  323. }
  324. public int CriticalHP(Characters value) => MaxHP(value) / 4 - 1;
  325. public override ushort CurrentHP() => CurrentHP(ID);
  326. public ushort CurrentHP(Characters c)
  327. {
  328. ushort max = MaxHP(c);
  329. if (max < _CurrentHP) _CurrentHP = max;
  330. return _CurrentHP;
  331. }
  332. public override short ElementalResistance(Kernel_bin.Element @in) => throw new NotImplementedException();
  333. /// <summary>
  334. /// <para>
  335. /// This Generates a Crisis Level. Run this each turn in battle. Though in real game it
  336. /// runs when the menu pops up.
  337. /// </para>
  338. /// <para>-1 means no limit break. &gt;=0 has a limit break.</para>
  339. /// </summary>
  340. /// <returns>-1 - 4</returns>
  341. /// <remarks>https://finalfantasy.fandom.com/wiki/Crisis_Level</remarks>
  342. /// <remarks>TODO: Need to confirm the formula is correct via reverse</remarks>
  343. public sbyte GenerateCrisisLevel()
  344. {
  345. ushort current = CurrentHP();
  346. ushort max = MaxHP();
  347. //if ((id == Characters.Seifer_Almasy && CurrentHP() < (max * 84 / 100)))
  348. //{
  349. int HPMod = CharacterStats.Crisis * 10 * current / max;
  350. int DeathBonus = Memory.State.DeadPartyMembers() * 200 + 1600;
  351. int StatusBonus = (int)(Statuses0.Count() * 10); // I think this is status of all party members
  352. int RandomMod = Memory.Random.Next(byte.MaxValue + 1) + 160;
  353. int crisislevel = (StatusBonus + DeathBonus - HPMod) / RandomMod; // better random number?
  354. if (crisislevel == 5)
  355. {
  356. CurrentCrisisLevel = 0;
  357. return CurrentCrisisLevel;
  358. }
  359. else if (crisislevel == 6)
  360. {
  361. CurrentCrisisLevel = 1;
  362. return CurrentCrisisLevel;
  363. }
  364. else if (crisislevel == 7)
  365. {
  366. CurrentCrisisLevel = 2;
  367. return CurrentCrisisLevel;
  368. }
  369. else if (crisislevel >= 8)
  370. {
  371. CurrentCrisisLevel = 3;
  372. return CurrentCrisisLevel;
  373. }
  374. //}
  375. return CurrentCrisisLevel = -1;
  376. }
  377. public void JunctionSpell(Kernel_bin.Stat stat, byte spell)
  378. {
  379. //see if magic is in use, if so remove it
  380. if (Stat_J.ContainsValue(spell))
  381. {
  382. Kernel_bin.Stat key = Stat_J.FirstOrDefault(x => x.Value == spell).Key;
  383. Stat_J[key] = 0;
  384. }
  385. //junction magic
  386. Stat_J[stat] = spell;
  387. }
  388. /// <summary>
  389. /// Max HP
  390. /// </summary>
  391. /// <param name="c">Force another character's HP calculation</param>
  392. /// <returns></returns>
  393. public ushort MaxHP(Characters c) => TotalStat(Kernel_bin.Stat.HP, c);
  394. public override ushort MaxHP() => MaxHP(ID);
  395. public override float PercentFullHP() => PercentFullHP(ID);
  396. public float PercentFullHP(Characters c) => (float)CurrentHP(c) / MaxHP(c);
  397. public static CharacterData Load(BinaryReader br, Characters @enum, Data data) => Load<CharacterData>(br, @enum,data);
  398. protected override void ReadData(BinaryReader br, Enum c)
  399. {
  400. if (!c.GetType().Equals(typeof(Characters))) throw new ArgumentException($"Enum {c} is not Characters");
  401. id = (Characters)c;
  402. Name = Memory.Strings.GetName(id, Data ?? Memory.State);
  403. _CurrentHP = br.ReadUInt16();//0x00
  404. _HP = br.ReadUInt16();//0x02
  405. Experience = br.ReadUInt32();//0x04
  406. ModelID = br.ReadByte();//0x08
  407. WeaponID = br.ReadByte();//0x09
  408. RawStats = new Dictionary<Kernel_bin.Stat, byte>(6)
  409. {
  410. [Kernel_bin.Stat.STR] = br.ReadByte(),//0x0A
  411. [Kernel_bin.Stat.VIT] = br.ReadByte(),//0x0B
  412. [Kernel_bin.Stat.MAG] = br.ReadByte(),//0x0C
  413. [Kernel_bin.Stat.SPR] = br.ReadByte(),//0x0D
  414. [Kernel_bin.Stat.SPD] = br.ReadByte(),//0x0E
  415. [Kernel_bin.Stat.LUCK] = br.ReadByte()//0x0F
  416. };
  417. Magics = new OrderedDictionary<byte, byte>(MagicCapacity);
  418. for (int i = 0; i < MagicCapacity; i++)
  419. {
  420. byte key = br.ReadByte();
  421. byte val = br.ReadByte();
  422. if (key >= 0 && !Magics.ContainsKey(key))
  423. Magics.Add(key, val);//0x10
  424. }
  425. Commands = Array.ConvertAll(br.ReadBytes(3), Item => (Kernel_bin.Abilities)Item).ToList();//0x50
  426. Paddingorunusedcommand = br.ReadByte();//0x53
  427. Abilities = Array.ConvertAll(br.ReadBytes(4), Item => (Kernel_bin.Abilities)Item).ToList();//0x54
  428. rawJunctionedGFs = (GFflags)br.ReadUInt16();//0x58 each bit is one gf.
  429. Unknown1 = br.ReadByte();//0x5A
  430. Alternativemodel = br.ReadByte();//0x5B (Normal, SeeD, Soldier...)
  431. Stat_J = new Dictionary<Kernel_bin.Stat, byte>(9);
  432. for (int i = 0; i < 19; i++)
  433. {
  434. Kernel_bin.Stat key = (Kernel_bin.Stat)i;
  435. byte val = br.ReadByte();
  436. if (!Stat_J.ContainsKey(key))
  437. Stat_J.Add(key, val);
  438. }
  439. Unknown2 = br.ReadByte();//0x6F (padding?)
  440. CompatibilitywithGFs = new Dictionary<GFs, CompatibilitywithGF>(16);
  441. for (int i = 0; i < 16; i++)
  442. CompatibilitywithGFs.Add((GFs)i, br.ReadUInt16());//0x70
  443. Numberofkills = br.ReadUInt16();//0x90
  444. NumberofKOs = br.ReadUInt16();//0x92
  445. Exists = (Exists)br.ReadByte();//0x94
  446. Unknown3 = br.ReadByte();//0x95
  447. Statuses0 = (Kernel_bin.Persistent_Statuses)br.ReadByte();//0x96
  448. Unknown4 = br.ReadByte();//0x97
  449. }
  450. public void RemoveAll()
  451. {
  452. Stat_J = Stat_J.ToDictionary(e => e.Key, e => (byte)0);
  453. Commands = Commands.ConvertAll(Item => Kernel_bin.Abilities.None);
  454. Abilities = Abilities.ConvertAll(Item => Kernel_bin.Abilities.None);
  455. rawJunctionedGFs = GFflags.None;
  456. }
  457. public void RemoveMagic() => Stat_J = Stat_J.ToDictionary(e => e.Key, e => (byte)0);
  458. /// <summary>
  459. /// Sorted Enumerable based on best to worst for Stat. Uses character's total magic and
  460. /// kernel bin's stat value.
  461. /// </summary>
  462. /// <param name="Stat">Stat sorting by.</param>
  463. /// <returns>Ordered Enumberable</returns>
  464. public IOrderedEnumerable<Kernel_bin.Magic_Data> SortedMagic(Kernel_bin.Stat Stat) => Kernel_bin.MagicData.OrderBy(x => (-x.totalStatVal(Stat) * (Magics.ContainsKey(x.ID) ? Magics[x.ID] : 0)) / 100);
  465. public override sbyte StatusResistance(Kernel_bin.Battle_Only_Statuses s) => throw new NotImplementedException();
  466. public override sbyte StatusResistance(Kernel_bin.Persistent_Statuses s) => throw new NotImplementedException();
  467. public override string ToString() => Name.Length > 0 ? Name.ToString() : base.ToString();
  468. public ushort TotalStat(Kernel_bin.Stat s, Characters c = Characters.Blank)
  469. {
  470. if (c == Characters.Blank)
  471. c = id;
  472. if (c != id && c < Characters.Laguna_Loire)
  473. throw new ArgumentException($"{this}::Wrong visible character value({c}). Must match ({id}) unless Laguna Kiros or Ward!");
  474. int total = 0;
  475. if (Kernel_bin.Statpercentabilities != null)
  476. foreach (Kernel_bin.Abilities i in Abilities)
  477. {
  478. if (Kernel_bin.Statpercentabilities.TryGetValue(i, out Kernel_bin.Stat_percent_abilities ability) && ability.Stat == s)
  479. total += ability.Value;
  480. }
  481. if (CharacterStats != null)
  482. switch (s)
  483. {
  484. case Kernel_bin.Stat.HP:
  485. return CharacterStats.HP((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], _HP, total);
  486. case Kernel_bin.Stat.EVA:
  487. //TODO confirm if there is no flat stat buff for eva. If there isn't then remove from function.
  488. return CharacterStats.EVA((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], 0, TotalStat(Kernel_bin.Stat.SPD, c), total);
  489. case Kernel_bin.Stat.SPD:
  490. return CharacterStats.SPD((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], RawStats[s], total);
  491. case Kernel_bin.Stat.HIT:
  492. return CharacterStats.HIT(Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], WeaponID);
  493. case Kernel_bin.Stat.LUCK:
  494. return CharacterStats.LUCK((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], RawStats[s], total);
  495. case Kernel_bin.Stat.MAG:
  496. return CharacterStats.MAG((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], RawStats[s], total);
  497. case Kernel_bin.Stat.SPR:
  498. return CharacterStats.SPR((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], RawStats[s], total);
  499. case Kernel_bin.Stat.STR:
  500. return CharacterStats.STR((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], RawStats[s], total, WeaponID);
  501. case Kernel_bin.Stat.VIT:
  502. return CharacterStats.VIT((sbyte)Level, Stat_J[s], Stat_J[s] == 0 ? 0 : Magics[Stat_J[s]], RawStats[s], total);
  503. }
  504. return 0;
  505. }
  506. public override ushort TotalStat(Kernel_bin.Stat s) => TotalStat(s, ID);
  507. public bool Unlocked(Kernel_bin.Stat stat) => Unlocked(UnlockedGFAbilities, stat);
  508. public bool Unlocked(List<Kernel_bin.Abilities> unlocked, Kernel_bin.Stat stat)
  509. {
  510. switch (stat)
  511. {
  512. default:
  513. return unlocked.Contains(Kernel_bin.Stat2Ability[stat]);
  514. case Kernel_bin.Stat.EL_Atk:
  515. return unlocked.Contains(Kernel_bin.Abilities.EL_Atk_J);
  516. case Kernel_bin.Stat.EL_Def_1:
  517. return unlocked.Contains(Kernel_bin.Abilities.EL_Def_Jx1) ||
  518. unlocked.Contains(Kernel_bin.Abilities.EL_Def_Jx2) ||
  519. unlocked.Contains(Kernel_bin.Abilities.EL_Def_Jx4);
  520. case Kernel_bin.Stat.EL_Def_2:
  521. return unlocked.Contains(Kernel_bin.Abilities.EL_Def_Jx2) ||
  522. unlocked.Contains(Kernel_bin.Abilities.EL_Def_Jx4);
  523. case Kernel_bin.Stat.EL_Def_3:
  524. case Kernel_bin.Stat.EL_Def_4:
  525. return unlocked.Contains(Kernel_bin.Abilities.EL_Def_Jx4);
  526. case Kernel_bin.Stat.ST_Atk:
  527. return unlocked.Contains(Kernel_bin.Abilities.ST_Atk_J);
  528. case Kernel_bin.Stat.ST_Def_1:
  529. return unlocked.Contains(Kernel_bin.Abilities.ST_Def_Jx1) ||
  530. unlocked.Contains(Kernel_bin.Abilities.ST_Def_Jx2) ||
  531. unlocked.Contains(Kernel_bin.Abilities.ST_Def_Jx4);
  532. case Kernel_bin.Stat.ST_Def_2:
  533. return unlocked.Contains(Kernel_bin.Abilities.ST_Def_Jx2) ||
  534. unlocked.Contains(Kernel_bin.Abilities.ST_Def_Jx4);
  535. case Kernel_bin.Stat.ST_Def_3:
  536. case Kernel_bin.Stat.ST_Def_4:
  537. return unlocked.Contains(Kernel_bin.Abilities.ST_Def_Jx4);
  538. }
  539. }
  540. }
  541. #endregion Classes
  542. }
  543. }