MagicData.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. namespace OpenVIII
  6. {
  7. namespace Kernel
  8. {
  9. /// <summary>
  10. /// Magic Data
  11. /// </summary>
  12. /// <see cref="https://github.com/alexfilth/doomtrain/wiki/Magic-data"/>
  13. public class MagicData
  14. {
  15. #region Fields
  16. public const int Count = 57;
  17. public const int ID = 1;
  18. #endregion Fields
  19. #region Constructors
  20. private MagicData(BinaryReader br, int i)
  21. {
  22. Name = Memory.Strings.Read(Strings.FileID.Kernel, ID, i * 2);
  23. Description = Memory.Strings.Read(Strings.FileID.Kernel, ID, i * 2 + 1);
  24. MagicDataID = (byte)i;
  25. br.BaseStream.Seek(4, SeekOrigin.Current);
  26. MagicID = (MagicID)br.ReadUInt16();
  27. Unknown = br.ReadByte();
  28. AttackType = (AttackType)br.ReadByte();
  29. SpellPower = br.ReadByte();
  30. Unknown2 = br.ReadByte();
  31. Target = (Target)br.ReadByte();
  32. AttackFlags = (AttackFlags)br.ReadByte();
  33. DrawResist = br.ReadByte();
  34. HitCount = br.ReadByte();
  35. Element = (Element)br.ReadByte();
  36. Unknown3 = br.ReadByte();
  37. Statuses1 = (BattleOnlyStatuses)br.ReadUInt32();
  38. Statuses0 = (PersistentStatuses)br.ReadUInt16();
  39. StatusAttack = br.ReadByte();
  40. var jVal = new Dictionary<Stat, byte>()
  41. {
  42. {Stat.HP, br.ReadByte()},
  43. {Stat.STR, br.ReadByte()},
  44. {Stat.VIT, br.ReadByte()},
  45. {Stat.MAG, br.ReadByte()},
  46. {Stat.SPR, br.ReadByte()},
  47. {Stat.SPD, br.ReadByte()},
  48. {Stat.EVA, br.ReadByte()},
  49. {Stat.HIT, br.ReadByte()},
  50. {Stat.Luck, br.ReadByte()}
  51. };
  52. ElAtk = (Element)br.ReadByte();
  53. jVal.Add(Stat.ElAtk, br.ReadByte());
  54. ElDef = (Element)br.ReadByte();
  55. jVal.Add(Stat.ElDef1, br.ReadByte());
  56. jVal.Add(Stat.ElDef2, jVal[Stat.ElDef1]);
  57. jVal.Add(Stat.ElDef3, jVal[Stat.ElDef1]);
  58. jVal.Add(Stat.ElDef4, jVal[Stat.ElDef1]);
  59. jVal.Add(Stat.StAtk, br.ReadByte());
  60. jVal.Add(Stat.StDef1, br.ReadByte());
  61. jVal.Add(Stat.StDef2, jVal[Stat.StDef1]);
  62. jVal.Add(Stat.StDef3, jVal[Stat.StDef1]);
  63. jVal.Add(Stat.StDef4, jVal[Stat.StDef1]);
  64. JVal = jVal;
  65. StAtk = (JunctionStatuses)br.ReadUInt16();
  66. StDef = (JunctionStatuses)br.ReadUInt16();
  67. GFCompatibility = br.ReadBytes(16);
  68. Unknown4 = br.ReadBytes(2);
  69. }
  70. #endregion Constructors
  71. #region Properties
  72. ///0x000B 1 byte Attack Flags
  73. public AttackFlags AttackFlags { get; }
  74. ///0x0007 1 byte Attack type
  75. public AttackType AttackType { get; }
  76. ///0x0002 2 bytes Offset to spell description
  77. public FF8String Description { get; }
  78. ///0x000C 1 byte Draw resist(how hard is the magic to draw)
  79. public byte DrawResist { get; }
  80. ///0x0020 1 byte Characters J - Elem attack
  81. /// <para>//public byte J_Val[Kernel_bin.Stat.EL_Atk];//0x0021 1 byte Characters J - Elem attack value</para>
  82. public Element ElAtk { get; }
  83. ///0x0022 1 byte Characters J - Elem defense
  84. /// <para>//public byte J_Val[Kernel_bin.Stat.EL_Def_1];//0x0023 1 byte Characters J - Elem defense value</para>
  85. public Element ElDef { get; }
  86. ///0x000E 1 byte Element
  87. public Element Element { get; }
  88. /// <summary>
  89. ///<para>0x002A 1 byte Quezacotl compatibility</para>
  90. ///<para>0x002B 1 byte Shiva compatibility</para>
  91. ///<para>0x002C 1 byte Ifrit compatibility</para>
  92. ///<para>0x002D 1 byte Siren compatibility</para>
  93. ///<para>0x002E 1 byte Brothers compatibility</para>
  94. ///<para>0x002F 1 byte Diablos compatibility</para>
  95. ///<para>0x0030 1 byte Carbuncle compatibility</para>
  96. ///<para>0x0031 1 byte Leviathan compatibility</para>
  97. ///<para>0x0032 1 byte Pandemona compatibility</para>
  98. ///<para>0x0033 1 byte Cerberus compatibility</para>
  99. ///<para>0x0034 1 byte Alexander compatibility</para>
  100. ///<para>0x0035 1 byte Doomtrain compatibility</para>
  101. ///<para>0x0036 1 byte Bahamut compatibility</para>
  102. ///<para>0x0037 1 byte Cactuar compatibility</para>
  103. ///<para>0x0038 1 byte Tonberry compatibility</para>
  104. ///<para>0x0039 1 byte Eden compatibility</para>
  105. /// </summary>
  106. public byte[] GFCompatibility { get; }
  107. ///0x000D 1 byte Hit Count(works with meteor animation, not sure about others)
  108. public byte HitCount { get; }
  109. /// <summary>
  110. ///<para>public byte HP_J; 0x0017 1 byte Characters HP junction value</para>
  111. ///<para>public byte STR_J; 0x0018 1 byte Characters STR junction value</para>
  112. ///<para>public byte VIT_J; 0x0019 1 byte Characters VIT junction value</para>
  113. ///<para>public byte MAG_J; 0x001A 1 byte Characters MAG junction value</para>
  114. ///<para>public byte SPR_J; 0x001B 1 byte Characters SPR junction value</para>
  115. ///<para>public byte SPD_J; 0x001C 1 byte Characters SPD junction value</para>
  116. ///<para>public byte EVA_J; 0x001D 1 byte Characters EVA junction value</para>
  117. ///<para>public byte HIT_J; 0x001E 1 byte Characters HIT junction value</para>
  118. ///<para>public byte LUCK_J; 0x001F 1 byte Characters LUCK junction value</para>
  119. /// </summary>
  120. public IReadOnlyDictionary<Stat, byte> JVal { get; }
  121. public byte MagicDataID { get; }
  122. ///0x0004 2 bytes Magic ID
  123. public MagicID MagicID { get; }
  124. ///0x0000 2 bytes Offset to spell name
  125. public FF8String Name { get; }
  126. public bool PositiveMagic
  127. {
  128. get
  129. {
  130. switch (AttackType)
  131. {
  132. case AttackType.CurativeItem:
  133. case AttackType.CurativeMagic:
  134. case AttackType.GivePercentageHP:
  135. case AttackType.Revive:
  136. case AttackType.ReviveAtFullHP:
  137. case AttackType.WhiteWindQuistis:
  138. case AttackType.Scan: //scan is kinda both.
  139. return true;
  140. case AttackType.None:
  141. case AttackType.PhysicalAttack:
  142. case AttackType.MagicAttack:
  143. case AttackType.PhysicalDamage:
  144. case AttackType.MagicDamage:
  145. case AttackType.RenzokukenFinisher:
  146. case AttackType.SquallGunbladeAttack:
  147. case AttackType.GF:
  148. case AttackType.LvDown:
  149. case AttackType.SummonItem:
  150. case AttackType.GFIgnoreTargetSPR:
  151. case AttackType.LvUp:
  152. case AttackType.Card:
  153. case AttackType.Kamikaze:
  154. case AttackType.Devour:
  155. case AttackType.GFDamage:
  156. case AttackType.Unknown1:
  157. case AttackType.MagicAttackIgnoreTargetSPR:
  158. case AttackType.AngeloSearch:
  159. case AttackType.MoogleDance:
  160. case AttackType.LvAttack:
  161. case AttackType.FixedDamage:
  162. case AttackType.TargetCurrentHP1:
  163. case AttackType.FixedMagicDamageBasedOnGFLevel:
  164. case AttackType.Unknown2:
  165. case AttackType.Unknown3:
  166. case AttackType.Unknown4:
  167. case AttackType.EveryoneGrudge:
  168. case AttackType._1_HP_Damage:
  169. case AttackType.PhysicalAttackIgnoreTargetVIT:
  170. return false;
  171. default:
  172. throw new ArgumentOutOfRangeException();
  173. }
  174. }
  175. }
  176. ///0x0008 1 byte Spell power(used in damage formula)
  177. public byte SpellPower { get; }
  178. /// <summary>
  179. /// //0x0026 2 bytes Characters J - Statuses Attack
  180. /// <para>//public byte J_Val[Kernel_bin.Stat.ST_Atk];//0x0024 1 byte Characters J - Status attack value</para>
  181. /// </summary>
  182. public JunctionStatuses StAtk { get; }
  183. ///0x0016 1 byte Status attack enabler
  184. public byte StatusAttack { get; }
  185. ///0x0014 2 bytes Statuses 0
  186. public PersistentStatuses Statuses0 { get; }
  187. ///0x0010 4 bytes Statuses 1
  188. public BattleOnlyStatuses Statuses1 { get; }
  189. /// <summary>
  190. /// //0x0028 2 bytes Characters J - Statuses Defend
  191. /// <para>//public byte J_Val[Kernel_bin.Stat.ST_Def_1];//0x0025 1 byte Characters J - Status defense value</para>
  192. /// </summary>
  193. public JunctionStatuses StDef { get; }
  194. ///0x000A 1 byte Default_target
  195. public Target Target { get; }
  196. ///0x0006 1 byte Unknown
  197. public byte Unknown { get; }
  198. ///0x0009 1 byte Unknown
  199. public byte Unknown2 { get; }
  200. ///0x000F 1 byte Unknown
  201. public byte Unknown3 { get; }
  202. ///0x003A 2 bytes Unknown
  203. public byte[] Unknown4 { get; }
  204. #endregion Properties
  205. #region Methods
  206. public static IReadOnlyList<MagicData> Read(BinaryReader br)
  207. => Enumerable.Range(0, Count).Select(x => CreateInstance(br, x)).ToList();
  208. public override string ToString() => Name;
  209. public uint TotalStatVal(Stat stat)
  210. {
  211. switch (stat)
  212. {
  213. case Stat.HP:
  214. case Stat.STR:
  215. case Stat.VIT:
  216. case Stat.MAG:
  217. case Stat.SPR:
  218. case Stat.SPD:
  219. case Stat.EVA:
  220. case Stat.HIT:
  221. case Stat.Luck:
  222. return JVal[stat];
  223. case Stat.ElAtk:
  224. return JVal[stat] * ElAtk.Count();
  225. case Stat.StAtk:
  226. return JVal[stat] * StAtk.Count();
  227. case Stat.ElDef1:
  228. case Stat.ElDef2:
  229. case Stat.ElDef3:
  230. case Stat.ElDef4:
  231. return JVal[stat] * ElDef.Count();
  232. case Stat.StDef1:
  233. case Stat.StDef2:
  234. case Stat.StDef3:
  235. case Stat.StDef4:
  236. return JVal[stat] * StDef.Count();
  237. case Stat.None:
  238. return 0;
  239. default:
  240. throw new ArgumentOutOfRangeException(nameof(stat), stat, null);
  241. }
  242. }
  243. private static MagicData CreateInstance(BinaryReader br, int i) => new MagicData(br, i);
  244. #endregion Methods
  245. }
  246. }
  247. }