ModuleBattleDebug.cs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  1. using ImGuiNET;
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Graphics;
  4. using Microsoft.Xna.Framework.Input;
  5. using OpenVIII.Battle;
  6. using OpenVIII.Battle.Dat;
  7. using OpenVIII.IGMDataItem;
  8. using System;
  9. using System.Collections.Concurrent;
  10. using System.Collections.Generic;
  11. using System.Diagnostics.CodeAnalysis;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Text.RegularExpressions;
  15. namespace OpenVIII
  16. {
  17. public static class ModuleBattleDebug
  18. {
  19. #region Fields
  20. public const int YOffset = 0;
  21. public static AlphaTestEffect Ate;
  22. public static bool PauseATB = false;
  23. //parses battle stage and all monsters
  24. //draw geometry also supports updateCamera
  25. private static readonly TimeSpan FPS = TimeSpan.FromMilliseconds(1000.0d / 15d);
  26. private static readonly Vector3 PyramidOffset = new Vector3(0, 3f, 0);
  27. private static bool _bUseFPSCamera;
  28. private static Vector3 _camPosition, _camTarget;
  29. //private static List<EnemyInstanceInformation> EnemyInstances;
  30. private static List<CharacterInstanceInformation> _characterInstances;
  31. private static Icon _crossHair;
  32. private static DeadTime _deadTime;
  33. private static float _degrees = 90;
  34. private static bool _forceReload;
  35. private static FPS_Camera _fpsCamera;
  36. private static TimeSpan _frameTime = TimeSpan.Zero;
  37. private static MonsterDatFile[] _monstersData;
  38. private static sbyte? _partyPos;
  39. private static RegularPyramid _regularPyramid;
  40. private static byte _sid;
  41. private static readonly ConcurrentDictionary<Characters, IReadOnlyList<byte>> SWeapons = new ConcurrentDictionary<Characters, IReadOnlyList<byte>>();
  42. #endregion Fields
  43. #region Enums
  44. /// <summary>
  45. /// Main Animations IDs
  46. /// </summary>
  47. /// <remarks>more beyond this maybe part of attacking and such.</remarks>
  48. /// <see cref="http://forums.qhimm.com/index.php?topic=19362.msg269777#msg269777"/>
  49. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  50. [SuppressMessage("ReSharper", "UnusedMember.Local")]
  51. private enum AnimID
  52. {
  53. Idle = 0,
  54. Critical = 1,
  55. Dead = 2
  56. //These seem to be referring to something else.
  57. //Like he says there's another section with 30 sequences.
  58. //where it tells how to chain the animations together.
  59. //Damage_Taken_low_hp,
  60. //Damage_Taken_normal,
  61. //Damage_Taken_critical,
  62. //Nothing,
  63. //Appearance,
  64. //Ready_to_Attack,
  65. //Fail_Draw,
  66. //Magic,
  67. //Standing,
  68. //Attack_normal,
  69. //SummonGF_hide,
  70. //Item_Use,
  71. //Escape,
  72. //Escaped_vanish,
  73. //Victory,
  74. //Becoming_Ready_to_Attack,
  75. //SummonGF_show,
  76. //Limit_break_normal,
  77. //Draw_Defend_Phase_again,
  78. //Becoming_Defend_Draw_Phase,
  79. //Kamikaze_Command,
  80. //Attack_Darkside,
  81. //Escape_2,
  82. //Defend_Draw_stock,
  83. //Limit_break_Special,
  84. //Defend_command_standing_again,
  85. //Draw_Stock_Magic
  86. }
  87. #endregion Enums
  88. #region Properties
  89. public static BattleModule BattleModule { get; set; }
  90. public static Camera Camera { get; private set; }
  91. public static ConcurrentDictionary<Characters, IReadOnlyList<byte>> Costumes { get; } = new ConcurrentDictionary<Characters, IReadOnlyList<byte>>();
  92. public static int DebugFrame { get; private set; }
  93. public static BasicEffect Effect { get; private set; }
  94. public static Matrix ProjectionMatrix { get; private set; }
  95. public static Stage Stage { get; private set; }
  96. public static Matrix ViewMatrix { get; private set; }
  97. public static ConcurrentDictionary<Characters, IReadOnlyList<byte>> Weapons
  98. {
  99. get
  100. {
  101. FillWeapons();
  102. lock (WeaponLock)
  103. {
  104. return SWeapons;
  105. }
  106. }
  107. }
  108. public static Matrix WorldMatrix { get; private set; }
  109. private static Vector3 CenterOfScreen => Memory.Graphics.GraphicsDevice.Viewport.Unproject(
  110. new Vector3(Memory.Graphics.GraphicsDevice.Viewport.Width / 2f,
  111. Memory.Graphics.GraphicsDevice.Viewport.Height / 2f, 0f), ProjectionMatrix, ViewMatrix, WorldMatrix);
  112. #endregion Properties
  113. #region Methods
  114. public static void AddAnimationToQueue(EntityType entityType, int nIndex, int newAnimId)
  115. {
  116. switch (entityType)
  117. {
  118. case EntityType.Monster:
  119. if (Enemy.Party[nIndex].EII.Data.Animations.Count > newAnimId)
  120. Enemy.Party[nIndex].EII.AnimationSystem.AnimationQueue.Enqueue(newAnimId);
  121. return;
  122. case EntityType.Character:
  123. case EntityType.Weapon:
  124. if (Math.Max(_characterInstances[nIndex].Data.Character.Animations.Count,
  125. _characterInstances[nIndex].Data.Weapon.Animations.Count) > newAnimId)
  126. _characterInstances[nIndex].AnimationSystem.AnimationQueue.Enqueue(newAnimId);
  127. return;
  128. default:
  129. return;
  130. }
  131. }
  132. public static void AddSequenceToQueue(EntityType entityType, int nIndex, AnimationSequence section5)
  133. {
  134. foreach (var newAnimId in section5)
  135. {
  136. AddAnimationToQueue(entityType, nIndex, newAnimId);
  137. }
  138. }
  139. public static Matrix CreateBillboard(Vector3 pos) => Matrix.CreateBillboard(pos, CenterOfScreen, Vector3.Up, null);
  140. /*
  141. public static Matrix CreateConstrainedBillboard(Vector3 pos, Vector3 rotateAxis) => Matrix.CreateConstrainedBillboard(pos, CenterOfScreen, rotateAxis, null, null);
  142. */
  143. public static void Draw()
  144. {
  145. Memory.SpriteBatch.GraphicsDevice.Clear(Color.Black);
  146. switch (BattleModule)
  147. {
  148. case BattleModule.DrawGeometry:
  149. //DrawGeometry();
  150. DrawMonsters();
  151. DrawCharactersWeapons();
  152. _regularPyramid.Draw(WorldMatrix, ViewMatrix, ProjectionMatrix);
  153. Stage?.Draw();
  154. var v = GetIndicatorPoint(-1);
  155. v.Y -= 5f;
  156. if (!_bUseFPSCamera)
  157. Menu.BattleMenus.Draw();
  158. break;
  159. case BattleModule.Active:
  160. break;
  161. case BattleModule.Init:
  162. break;
  163. case BattleModule.ReadData:
  164. break;
  165. default:
  166. throw new ArgumentOutOfRangeException();
  167. }
  168. if(Memory.GameTime!=null)
  169. Memory.ImGui.BeforeLayout(Memory.GameTime);
  170. ImGui.Begin("BATTLE DEBUG INFO");
  171. ImGui.Text($"Encounter ready at: {Memory.Encounters.ID} - {Memory.Encounters.Filename}");
  172. ImGui.Text($"Debug variable: {DebugFrame} ({DebugFrame >> 4},{DebugFrame & 0b1111})\n");
  173. ImGui.Text($"1000/deltaTime milliseconds: {(Memory.ElapsedGameTime.TotalSeconds > 0 ? 1d / Memory.ElapsedGameTime.TotalSeconds : 0d)}\n");
  174. ImGui.Text($"Average FrameRate: {FPSCounter.AverageFramesPerSecond}\n");
  175. ImGui.Text($"camera frame: {Camera.Cam.CurrentTime}/{Camera.Cam.TotalTime}\n");
  176. ImGui.Text($"Camera.World.Position: {Extended.RemoveBrackets(_camPosition.ToString())}\n");
  177. ImGui.Text($"Camera.World.Target: {Extended.RemoveBrackets(_camTarget.ToString())}\n");
  178. ImGui.Text($"Camera.FOV: {MathHelper.Lerp(Camera.Cam.StartingFov, Camera.Cam.EndingFov, Camera.Cam.CurrentTime.Ticks / (float)Camera.Cam.TotalTime.Ticks)}\n");
  179. ImGui.Text($"Camera.Mode: {Camera.Cam.ControlWord & 1}\n");
  180. ImGui.Text($"DEBUG: Press 0 to switch between FPSCamera/Camera anim: {_bUseFPSCamera}\n");
  181. ImGui.Text($"Sequence ID: {_sid}, press F10 to activate sequence, F11 SID--, F12 SID++");
  182. ImGui.End();
  183. Memory.ImGui.AfterLayout();
  184. }
  185. public static void DrawCrossHair(Enemy enemy)
  186. {
  187. var shot = Menu.BattleMenus.GetCurrentBattleMenu()?.Shot;
  188. if (shot == null || !shot.Enabled) return;
  189. var targets = shot.Targets;
  190. if (targets == null) return;
  191. foreach (var d in targets)
  192. {
  193. if (!d.GetEnemy(out var e) || !e.Equals(enemy)) continue;
  194. var posIn3DSpace = e.EII.Data.IndicatorPoint(e.EII.Location);
  195. posIn3DSpace.Y -= 1f;
  196. var screenPos = Memory.Graphics.GraphicsDevice.Viewport.Project(posIn3DSpace, ProjectionMatrix, ViewMatrix, WorldMatrix);
  197. Memory.SpriteBatchStartAlpha();
  198. _crossHair.Pos = new Rectangle(new Vector2(screenPos.X, screenPos.Y).ToPoint(), Point.Zero);
  199. var icons = Memory.Icons[_crossHair.Data];
  200. var texture = Memory.Icons.GetTexture(Icons.ID.Cross_Hair1);
  201. var s = texture.ScaleFactor;
  202. _crossHair.Pos.Offset(-icons.Width * s.X / 2f, -icons.Height * s.Y / 2f);
  203. _crossHair.Draw();
  204. Memory.SpriteBatchEnd();
  205. break;
  206. }
  207. }
  208. public static Vector3 GetIndicatorPoint(int n)
  209. {
  210. if (n >= 0)
  211. {
  212. if ((_characterInstances == null))
  213. return Vector3.Zero;
  214. return _characterInstances[n].Data.Character.IndicatorPoint(_characterInstances[n].Data.Location) +
  215. PyramidOffset;
  216. }
  217. if (Enemy.Party == null) return Vector3.Zero;
  218. var enemyInstanceInformation = Enemy.Party.FirstOrDefault(x => x.EII.PartyPos == n)?.EII;
  219. if (enemyInstanceInformation == null) return Vector3.Zero;
  220. return enemyInstanceInformation.Data.IndicatorPoint(enemyInstanceInformation.Location) + PyramidOffset;
  221. }
  222. public static void Inputs()
  223. {
  224. if (Input2.Button(Keys.D0))
  225. _bUseFPSCamera = !_bUseFPSCamera;
  226. else if (Input2.Button(Keys.D1))
  227. {
  228. if ((DebugFrame & 0b1111) >= 7)
  229. {
  230. DebugFrame += 0b00010000;
  231. DebugFrame -= 7;
  232. }
  233. else DebugFrame += 1;
  234. Camera.ChangeAnimation((byte)Math.Abs(DebugFrame));
  235. }
  236. else if (Input2.Button(Keys.D2))
  237. {
  238. if ((DebugFrame & 0b1111) == 0)
  239. {
  240. DebugFrame -= 0b00010000;
  241. DebugFrame += 7;
  242. }
  243. else DebugFrame--;
  244. Camera.ChangeAnimation((byte)Math.Abs(DebugFrame));
  245. }
  246. else if (Input2.Button(Keys.F5))
  247. {
  248. ResetState();
  249. }
  250. else if (Input2.Button(Keys.D3))
  251. {
  252. BattleModule = BattleModule.Init;
  253. Memory.Encounters.Previous();
  254. Memory.SuppressDraw = true;
  255. }
  256. else if (Input2.Button(Keys.D4))
  257. {
  258. BattleModule = BattleModule.Init;
  259. Memory.Encounters.Next();
  260. Memory.SuppressDraw = true;
  261. }
  262. else if (Input2.Button(Keys.D5))
  263. {
  264. AddAnimationToQueue(EntityType.Monster, 0, 3);
  265. AddAnimationToQueue(EntityType.Monster, 0, 0);
  266. }
  267. else if (Input2.Button(Keys.F12))
  268. {
  269. if (_sid < 255)
  270. _sid++;
  271. else _sid = 0;
  272. }
  273. else if (Input2.Button(Keys.F11))
  274. {
  275. if (_sid <= 0)
  276. _sid = 255;
  277. else _sid--;
  278. }
  279. else if (Input2.Button(Keys.F10))
  280. {
  281. AddSequenceToAllQueues(_sid);
  282. }
  283. else if (Input2.Button(Keys.F9))
  284. {
  285. //AddSequenceToAllQueues(new AnimationSequence
  286. //{
  287. // AnimationQueue = new List<byte> {
  288. // //0x2,
  289. // //0x5,
  290. // //0xf,
  291. // //0x10,
  292. // //0xb,
  293. // //0x3,
  294. // //0x6,
  295. // 0xe,
  296. // //0x1,
  297. // 0xf,
  298. // 0x0
  299. //}
  300. //});
  301. }
  302. else if (Input2.Button(Keys.F8))
  303. {
  304. StopAnimations();
  305. }
  306. else if (Input2.Button(Keys.F7))
  307. {
  308. StartAnimations();
  309. }
  310. }
  311. /// <summary>
  312. /// Plays requested animation for given entity immediately (without waiting for current
  313. /// animation to stop if have any queued animations)
  314. /// </summary>
  315. /// <param name="entityType">Provide either Monster or Character/weapon</param>
  316. /// <param name="nIndex">
  317. /// Index of entityTypeInstance. Monster is monsterInstances, character is CharacterInstances
  318. /// </param>
  319. /// <param name="newAnimId">self explanatory</param>
  320. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  321. public static void PlayAnimationImmediately(EntityType entityType, int nIndex, int newAnimId)
  322. {
  323. switch (entityType)
  324. {
  325. case EntityType.Monster:
  326. var mInstanceInformationProvider = Enemy.Party[nIndex].EII;
  327. mInstanceInformationProvider.AnimationSystem.AnimationId = newAnimId;
  328. Enemy.Party[nIndex].EII = mInstanceInformationProvider;
  329. return;
  330. case EntityType.Character:
  331. case EntityType.Weapon:
  332. var cInstanceInformationProvider = _characterInstances[nIndex];
  333. cInstanceInformationProvider.AnimationSystem.AnimationId = newAnimId;
  334. _characterInstances[nIndex] = cInstanceInformationProvider;
  335. return;
  336. default:
  337. return;
  338. }
  339. }
  340. public static void ResetState()
  341. {
  342. //reload
  343. BattleModule = BattleModule.Init;
  344. _forceReload = true;
  345. Memory.SuppressDraw = true;
  346. }
  347. public static void Update()
  348. {
  349. if (_characterInstances != null)
  350. foreach (var cii in _characterInstances)
  351. {
  352. var c = Memory.State?[cii.VisibleCharacter];
  353. if (c == null) continue;
  354. c.Update(); //updates ATB for Character.
  355. if (cii.AnimationSystem.AnimationId < 0 ||
  356. cii.AnimationSystem.AnimationId > 2) continue;
  357. // this would probably interfere with other animations. I am hoping the limits above will keep it good.
  358. if (c.IsDead)
  359. cii.SetAnimationID((int)AnimID.Dead);
  360. else if (c.IsCritical)
  361. cii.SetAnimationID((int)AnimID.Critical);
  362. }
  363. if (Enemy.Party != null)
  364. foreach (var e in Enemy.Party)
  365. e.Update(); //updates ATB for enemy.
  366. var ret = false;
  367. switch (BattleModule)
  368. {
  369. case BattleModule.Init:
  370. Memory.SuppressDraw = true;
  371. InitBattle();
  372. break;
  373. case BattleModule.ReadData:
  374. Memory.SuppressDraw = true;
  375. ReadData();
  376. Menu.BattleMenus.Refresh();
  377. Menu.FadeIn();
  378. _forceReload = false;
  379. break;
  380. case BattleModule.DrawGeometry:
  381. Stage?.Update();
  382. _deadTime?.Update();
  383. Menu.BattleMenus.Update();
  384. var partyPos = Menu.BattleMenus.PartyPos;
  385. _regularPyramid.Set(GetIndicatorPoint(partyPos ?? 0));
  386. if (partyPos != _partyPos)
  387. {
  388. if (partyPos == null)
  389. _regularPyramid.FadeOut();
  390. else
  391. _regularPyramid.FadeIn();
  392. _partyPos = partyPos;
  393. }
  394. if (_bUseFPSCamera)
  395. {
  396. ViewMatrix = _fpsCamera.Update(ref _camPosition, ref _camTarget, ref _degrees);
  397. ProjectionMatrix = Camera.ProjectionMatrix;
  398. }
  399. else
  400. {
  401. if (Camera != null)
  402. {
  403. Camera.Update();
  404. ViewMatrix = Camera.ViewMatrix;
  405. ProjectionMatrix = Camera.ProjectionMatrix;
  406. }
  407. ret = Menu.BattleMenus.Inputs();
  408. }
  409. break;
  410. case BattleModule.Active:
  411. break;
  412. default:
  413. throw new ArgumentOutOfRangeException();
  414. }
  415. if (!ret) Inputs();
  416. _regularPyramid.Update();
  417. UpdateFrames();
  418. }
  419. [SuppressMessage("ReSharper", "UnusedMember.Local")]
  420. private static void AddSequenceToAllQueues(AnimationSequence section5)
  421. {
  422. for (var i = 0; i < Enemy.Party.Count; i++)
  423. {
  424. AddSequenceToQueue(EntityType.Monster, i, section5);
  425. }
  426. for (var i = 0; i < _characterInstances.Count; i++)
  427. {
  428. AddSequenceToQueue(EntityType.Character, i, section5);
  429. }
  430. }
  431. private static void AddSequenceToAllQueues(byte sid)
  432. {
  433. AnimationSequence section5;
  434. for (var i = 0; i < Enemy.Party.Count; i++)
  435. {
  436. if (Enemy.Party[i].EII.Data.Sequences.Count <= sid) continue;
  437. section5 = Enemy.Party[i].EII.Data.Sequences.FirstOrDefault(x => x.ID == sid);
  438. AddSequenceToQueue(EntityType.Monster, i, section5);
  439. //AddAnimationToQueue(Debug_battleDat.EntityType.Monster, i, 0);
  440. }
  441. for (var i = 0; i < _characterInstances.Count; i++)
  442. {
  443. var weapon = _characterInstances[i].Data.Weapon;
  444. var character = _characterInstances[i].Data.Character;
  445. var sequences =
  446. (weapon?.Sequences.Count ?? 0) == 0 ? character.Sequences : weapon.Sequences;
  447. if (sequences.Count <= sid) continue;
  448. section5 = sequences.FirstOrDefault(x => x.ID == sid);
  449. AddSequenceToQueue(EntityType.Character, i, section5);
  450. //AddAnimationToQueue(Debug_battleDat.EntityType.Character, i, 0);
  451. }
  452. }
  453. private static bool CharacterInstanceAnimationStopped(int n) =>
  454. _characterInstances[n].AnimationSystem.AnimationStopped ||
  455. ((Memory.State?[(Characters)_characterInstances[n].CharacterId]?.IsPetrify ?? false) &&
  456. _characterInstances[n].AnimationSystem.StopAnimation());
  457. private static double CharacterInstanceGenerateStep(int n) => GenerateStep(CharacterInstanceAnimationStopped(n));
  458. /// <summary>
  459. /// This function is responsible for deleting the queue of animation if passed correctly
  460. /// </summary>
  461. /// <param name="type"></param>
  462. /// <param name="n"></param>
  463. private static void CheckAnimationFrame(EntityType type, int n)
  464. {
  465. Animation animationSystem;
  466. switch (type)
  467. {
  468. case EntityType.Monster:
  469. animationSystem = Enemy.Party[n].EII.Data.Animations[Enemy.Party[n].EII.AnimationSystem.AnimationId];
  470. if (Enemy.Party[n].EII.AnimationSystem.AnimationFrame < animationSystem.Count) return;
  471. var eInstanceInformationProvider = Enemy.Party[n].EII;
  472. if (Enemy.Party[n].EII.AnimationSystem.AnimationQueue.TryDequeue(out var animationID) &&
  473. animationID < eInstanceInformationProvider.Data.Animations.Count &&
  474. animationID >= 0
  475. )
  476. {
  477. eInstanceInformationProvider.AnimationSystem.AnimationId = animationID;
  478. }
  479. Enemy.Party[n].EII = eInstanceInformationProvider;
  480. return;
  481. case EntityType.Character:
  482. case EntityType.Weapon:
  483. animationSystem = _characterInstances[n].Data.Character.Animations[_characterInstances[n].AnimationSystem.AnimationId];
  484. if (_characterInstances[n].AnimationSystem.AnimationFrame < animationSystem.Count) return;
  485. var cInstanceInformationProvider = _characterInstances[n];
  486. if (_characterInstances[n].AnimationSystem.AnimationQueue.TryDequeue(out animationID) &&
  487. (animationID < cInstanceInformationProvider.Data.Character.Animations.Count ||
  488. animationID < (cInstanceInformationProvider.Data.Weapon?.Animations.Count ?? 0)) &&
  489. animationID >= 0)
  490. {
  491. cInstanceInformationProvider.AnimationSystem.AnimationId = animationID;
  492. }
  493. _characterInstances[n] = cInstanceInformationProvider;
  494. return;
  495. default:
  496. return;
  497. }
  498. }
  499. private static void DrawBattleDat(DatFile battleDatFile, double step, ref AnimationSystem animationSystem,
  500. ref Vector3 position, Quaternion? nullRotation = null)
  501. {
  502. if (battleDatFile.Geometry == null) return;
  503. for (var i = 0; /*i<1 &&*/ i < battleDatFile.Geometry.CObjects; i++)
  504. {
  505. var rotation = nullRotation ?? Quaternion.CreateFromYawPitchRoll(MathHelper.Pi, 0, 0);
  506. var vertexPositionTexturePointersGRP = battleDatFile.GetVertexPositions(
  507. i,
  508. ref position,
  509. rotation,
  510. ref animationSystem,
  511. step); //DEBUG
  512. if (vertexPositionTexturePointersGRP.IsNotSet())
  513. return;
  514. for (var k = 0; k < vertexPositionTexturePointersGRP.VPT.Length / 3; k++)
  515. {
  516. Ate.Texture = (Texture2D)battleDatFile.Textures[vertexPositionTexturePointersGRP.TexturePointers[k]];
  517. foreach (var pass in Ate.CurrentTechnique.Passes)
  518. {
  519. pass.Apply();
  520. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList,
  521. vertexPositionTexturePointersGRP.VPT, k * 3, 1);
  522. }
  523. }
  524. }
  525. }
  526. /// <summary>
  527. /// Method to render characters and weapons for them
  528. /// </summary>
  529. private static void DrawCharactersWeapons()
  530. {
  531. Memory.Graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
  532. Memory.Graphics.GraphicsDevice.BlendState = BlendState.AlphaBlend;
  533. Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  534. Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  535. Ate.Projection = ProjectionMatrix; Ate.View = ViewMatrix; Ate.World = WorldMatrix;
  536. Effect.TextureEnabled = true;
  537. //CHARACTER
  538. if (_characterInstances == null)
  539. return;
  540. for (var n = 0; n < _characterInstances.Count; n++)
  541. {
  542. CheckAnimationFrame(EntityType.Character, n);
  543. var characterPosition = _characterInstances[n].Data.Location = GetCharPos(n);
  544. DrawBattleDat(_characterInstances[n].Data.Character, CharacterInstanceGenerateStep(n),
  545. ref _characterInstances[n].AnimationSystem, ref characterPosition);
  546. DrawShadow(characterPosition, Ate, .5f);
  547. //WEAPON
  548. if (_characterInstances[n].Data.Weapon == null) continue;
  549. CheckAnimationFrame(EntityType.Weapon, n);
  550. DrawBattleDat(_characterInstances[n].Data.Weapon, CharacterInstanceGenerateStep(n),
  551. ref _characterInstances[n].AnimationSystem, ref characterPosition);
  552. }
  553. }
  554. private static void DrawMonsters()
  555. {
  556. Memory.Graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
  557. Memory.Graphics.GraphicsDevice.BlendState = BlendState.AlphaBlend;
  558. Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  559. Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  560. Ate.Projection = ProjectionMatrix; Ate.View = ViewMatrix; Ate.World = WorldMatrix;
  561. Effect.TextureEnabled = true;
  562. if (Enemy.Party == null)
  563. return;
  564. for (var n = 0; n < Enemy.Party.Count; n++)
  565. {
  566. if (Enemy.Party[n].EII.Data.GetId == 127)
  567. {
  568. //TODO;
  569. continue;
  570. }
  571. CheckAnimationFrame(EntityType.Monster, n);
  572. var enemyPosition = GetEnemyPos(n);
  573. enemyPosition.Y += YOffset;
  574. DrawBattleDat(Enemy.Party[n].EII.Data, GenerateStep(EnemyInstanceAnimationStopped(n)), ref Enemy.Party[n].EII.AnimationSystem, ref enemyPosition, Quaternion.Identity);
  575. DrawShadow(enemyPosition, Ate, Enemy.Party[n].EII.Data.Skeleton.GetScale.X / 5);
  576. DrawCrossHair(Enemy.Party[n]);
  577. }
  578. }
  579. /// <summary>
  580. /// [BROKEN] See issue #46
  581. /// </summary>
  582. /// <param name="enemyPosition"></param>
  583. /// <param name="alphaTestEffect"></param>
  584. /// <param name="scale"></param>
  585. [SuppressMessage("ReSharper", "UnusedParameter.Local")]
  586. private static void DrawShadow(Vector3 enemyPosition, AlphaTestEffect alphaTestEffect, float scale)
  587. {
  588. /*
  589. VertexPositionTexture[] ptCopy = Memory.shadowGeometry.Clone() as VertexPositionTexture[];
  590. for (int i = 0; i < ptCopy.Length; i++)
  591. ptCopy[i].Position = Vector3.Transform(ptCopy[i].Position, Matrix.CreateScale(scale));
  592. for (int i = 0; i < ptCopy.Length; i++)
  593. {
  594. (float x, float y, float z) = enemyPosition;
  595. ptCopy[i].Position = Vector3.Add(ptCopy[i].Position, new Vector3(x, 0.1f, z));
  596. }
  597. alphaTestEffect.Texture = Memory.shadowTexture;
  598. foreach (EffectPass pass in alphaTestEffect.CurrentTechnique.Passes)
  599. {
  600. pass.Apply();
  601. Memory.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, ptCopy, 0, 8);
  602. }
  603. */
  604. }
  605. private static bool EnemyInstanceAnimationStopped(int n) =>
  606. Enemy.Party[n].EII.AnimationSystem.AnimationStopped ||
  607. (Enemy.Party[n].IsPetrify &&
  608. Enemy.Party[n].EII.AnimationSystem.StopAnimation());
  609. private static readonly object CostumeLock = new object();
  610. private static void FillCostumes()
  611. {
  612. lock (CostumeLock)
  613. {
  614. if (Costumes == null || Costumes.Count != 0) return;
  615. for (var i = 0; i <= (int)Characters.Ward_Zabac; i++)
  616. {
  617. //Debug.Assert(i != 10);
  618. var costumes = new SortedSet<byte>();
  619. var r = new Regex(@"d(" + i.ToString("X") + @")c(\d+)\.dat", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  620. var aw = ArchiveWorker.Load(Memory.Archives.A_BATTLE);
  621. foreach (var s in aw.GetListOfFiles().OrderBy(Path.GetFileName, StringComparer.InvariantCultureIgnoreCase).Where(x => x.IndexOf($"d{i:x}c", StringComparison.OrdinalIgnoreCase) >= 0))
  622. {
  623. var match = r.Match(s);
  624. if (!byte.TryParse(match.Groups[2].Value, out var a)) continue;
  625. costumes.Add(a);
  626. }
  627. Costumes.TryAdd((Characters)i, costumes.ToList().AsReadOnly());
  628. }
  629. }
  630. }
  631. private static readonly object WeaponLock = new object();
  632. private static void FillWeapons()
  633. {
  634. lock (WeaponLock)
  635. {
  636. if (SWeapons == null ||SWeapons.Count != 0) return;
  637. for (var i = 0; i <= (int)Characters.Ward_Zabac; i++)
  638. {
  639. //Debug.Assert(i != 10);
  640. var weapons = new SortedSet<byte>();
  641. var r = new Regex(@"d(" + i.ToString("X") + @")w(\d+)\.dat", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  642. var aw = ArchiveWorker.Load(Memory.Archives.A_BATTLE);
  643. foreach (var s in aw.GetListOfFiles().OrderBy(Path.GetFileName, StringComparer.InvariantCultureIgnoreCase).Where(x=>x.IndexOf($"d{i:x}w", StringComparison.OrdinalIgnoreCase) >=0))
  644. {
  645. var match = r.Match(s);
  646. if (!byte.TryParse(match.Groups[2].Value, out var a)) continue;
  647. weapons.Add(a);
  648. }
  649. SWeapons.TryAdd((Characters) i, weapons.ToList().AsReadOnly());
  650. }
  651. }
  652. }
  653. private static double GenerateStep(bool animationStopped)
  654. {
  655. if (animationStopped)
  656. return 1d;
  657. return (double)_frameTime.Ticks / FPS.Ticks;
  658. }
  659. private static Vector3 GetCharPos(int n) => new Vector3(-10 + n * 10, YOffset, -30);
  660. private static byte GetCostume(Characters c) =>
  661. Memory.State[c].AlternativeModel == 0 ? Costumes[c].First() : Costumes[c].Last();
  662. private static Vector3 GetEnemyPos(int n) => Enemy.Party[n].EII.Location;
  663. private static byte GetWeaponID(Characters c)
  664. {
  665. IReadOnlyList<byte> weapons;
  666. if (c >= Characters.Laguna_Loire)
  667. {
  668. if (Weapons.TryGetValue(c, out weapons) && weapons != null && weapons.Count > 0)
  669. return weapons[0];
  670. return 0;
  671. }
  672. var characterData = Memory.State[c];
  673. if (characterData.WeaponID >= Memory.KernelBin.WeaponsData.Count) return 0;
  674. var altID = Memory.KernelBin.WeaponsData[characterData.WeaponID].AltID;
  675. if (!Weapons.TryGetValue(c, out weapons) || weapons == null) return 0;
  676. if (weapons.Count > altID)
  677. return weapons[altID];
  678. return weapons.Count>0 ? weapons[weapons.Count - 1] : (byte) 0;
  679. }
  680. private static void InitBattle()
  681. {
  682. if (Stage == null || Stage.Scenario != Memory.Encounters.Scenario || _forceReload)
  683. using (var br = Stage.Open())
  684. {
  685. //Camera and stage are in the same file.
  686. Camera = Camera.Read(br);
  687. Stage = Stage.Read(Camera.EndOffset, br);
  688. }
  689. if (_crossHair == null || _forceReload)
  690. _crossHair = new Icon { Data = Icons.ID.Cross_Hair1 };
  691. //testQuad = Memory.Icons.Quad(Icons.ID.Cross_Hair1, 2);
  692. //MakiExtended.Debugger_Spawn();
  693. //MakiExtended.Debugger_Feed(typeof(Module_battle_debug), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
  694. //InputMouse.Mode = MouseLockMode.Center;
  695. if (_deadTime == null)
  696. {
  697. _deadTime = new DeadTime();
  698. }
  699. _deadTime.Restart();
  700. Console.WriteLine($"BS_DEBUG/ENC: Encounter: {Memory.Encounters.ID}\t cEnemies: {Memory.Encounters.EnabledEnemy}\t Enemies: {string.Join(",", Memory.Encounters.BEnemies.Where(x => x != 0x00).Select(x => $"{x}").ToArray())}");
  701. if (_fpsCamera == null)
  702. _fpsCamera = new FPS_Camera();
  703. if (_regularPyramid == null)
  704. {
  705. _regularPyramid = new RegularPyramid();
  706. _regularPyramid.Set(-2.5f, 2f, Color.Yellow);
  707. }
  708. _regularPyramid.Hide();
  709. //init renderer
  710. if (Effect == null)
  711. Effect = new BasicEffect(Memory.Graphics.GraphicsDevice);
  712. _camTarget = new Vector3(41.91198f, 33.59995f, 6.372305f);
  713. _camPosition = new Vector3(40.49409f, 39.70397f, -43.321299f);
  714. ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
  715. MathHelper.ToRadians(45f),
  716. Memory.Graphics.GraphicsDevice.Viewport.AspectRatio,
  717. 1f, 1000f);
  718. ViewMatrix = Matrix.CreateLookAt(_camPosition, _camTarget,
  719. new Vector3(0f, 1f, 0f));// Y up
  720. WorldMatrix = Matrix.CreateWorld(_camPosition, Vector3.
  721. Forward, Vector3.Up);
  722. BattleModule++;
  723. if (Ate == null)
  724. Ate = new AlphaTestEffect(Memory.Graphics.GraphicsDevice)
  725. {
  726. Projection = ProjectionMatrix,
  727. View = ViewMatrix,
  728. World = WorldMatrix
  729. };
  730. }
  731. private static CharacterInstanceInformation ReadCharacter(ref int cid, Characters c)
  732. {
  733. var cii = new CharacterInstanceInformation
  734. {
  735. Data = ReadCharacterData((int)c, GetCostume(c), GetWeaponID(c)),
  736. AnimationSystem = new AnimationSystem { AnimationQueue = new ConcurrentQueue<int>() },
  737. CharacterId = cid++
  738. };
  739. //cii.animationSystem.animationId = 4;
  740. Memory.State[c].BattleStart(cii);
  741. return cii;
  742. }
  743. private static CharacterData ReadCharacterData(int characterId, int alternativeCostumeId, int weaponId)
  744. {
  745. DatFile character = CharacterDatFile.CreateInstance(characterId, alternativeCostumeId);
  746. DatFile weapon;
  747. if (characterId == 1 || characterId == 9)
  748. weapon = WeaponDatFile.CreateInstance(characterId, weaponId, character);
  749. else if (weaponId != -1) weapon = WeaponDatFile.CreateInstance(characterId, weaponId);
  750. else weapon = null;
  751. return new CharacterData
  752. {
  753. Character = character,
  754. Weapon = weapon
  755. };
  756. }
  757. /// <summary>
  758. /// [WIP] Basic class responsible for creating character model into game. It should be
  759. /// changed to be parsed from current party
  760. /// </summary>
  761. private static void ReadCharacters()
  762. {
  763. if (Memory.State?.Characters != null)
  764. {
  765. FillCostumes();
  766. FillWeapons();
  767. int cid;
  768. if (_characterInstances != null && !_forceReload)
  769. {
  770. var test = _characterInstances.Select((x, i) => new
  771. {
  772. cii = x,
  773. index = i,
  774. id = (Characters)x.Data.Character.ID,
  775. alt = x.Data.Character.AltID,
  776. weapon = x.Data.Weapon.AltID
  777. }).ToArray();
  778. //where characters haven't changed
  779. foreach (var x in test.Where(x => Memory.State.Party[x.index] == x.id && (GetCostume(x.id) == x.alt) && x.weapon == GetWeaponID(x.id)))
  780. {
  781. Memory.State[x.id].BattleStart(x.cii);
  782. return;
  783. }
  784. //where character, weapon, or costume has changed
  785. foreach (var x in test.Where(x => !(Memory.State.Party[x.index] == x.id && (GetCostume(x.id) == x.alt) && x.weapon == GetWeaponID(x.id))))
  786. {
  787. cid = x.index;
  788. _characterInstances[x.index] = ReadCharacter(ref cid, x.id);
  789. return;
  790. }
  791. }
  792. _characterInstances = new List<CharacterInstanceInformation>(3);
  793. cid = 0;
  794. foreach (var c in Memory.State.Party)
  795. {
  796. if (c != Characters.Blank)
  797. {
  798. _characterInstances.Add(ReadCharacter(ref cid, c));
  799. }
  800. }
  801. }
  802. else
  803. _characterInstances = new List<CharacterInstanceInformation>
  804. {
  805. new CharacterInstanceInformation
  806. {
  807. Data = ReadCharacterData(0,0,0),
  808. AnimationSystem = new AnimationSystem { AnimationQueue = new ConcurrentQueue<int>()},
  809. CharacterId = 0
  810. },
  811. new CharacterInstanceInformation
  812. {
  813. Data = ReadCharacterData(1,3,8),
  814. AnimationSystem = new AnimationSystem { AnimationQueue = new ConcurrentQueue<int>()},
  815. CharacterId = 1
  816. },
  817. new CharacterInstanceInformation
  818. {
  819. Data = ReadCharacterData(2,6,13),
  820. AnimationSystem = new AnimationSystem { AnimationQueue = new ConcurrentQueue<int>()},
  821. CharacterId = 2
  822. }
  823. };
  824. }
  825. private static void ReadData()
  826. {
  827. ReadCharacters();
  828. ReadMonster();
  829. BattleModule++;
  830. }
  831. /// <summary>
  832. /// This method is responsible to read/parse the enemy data. It holds the result in
  833. /// monstersData[] This method was designed to read only one instance of enemy. A list called
  834. /// EnemyInstance holds data information for each enemy
  835. /// </summary>
  836. private static void ReadMonster()
  837. {
  838. int r(int i) => 7 - i;
  839. var encounter = Memory.Encounters.Current;
  840. if (encounter.EnabledEnemy.Cast<bool>().Any(x => x))
  841. {
  842. _monstersData = encounter.UniqueMonstersList?.Select(x => MonsterDatFile.CreateInstance(x)).ToArray();
  843. }
  844. else
  845. {
  846. _monstersData = null;
  847. return;
  848. }
  849. Enemy.Party = encounter.BEnemies.Select((x, i) => new { i, x }).Where(x => encounter.EnabledEnemy[r(x.i)]).Select(x =>
  850. {
  851. DatFile datFile = _monstersData.FirstOrDefault(mon => mon.GetId == x.x);
  852. var i = r(x.i);
  853. return datFile == default
  854. ? null
  855. : (Enemy)new EnemyInstanceInformation
  856. {
  857. Data = datFile,
  858. BIsHidden = encounter.HiddenEnemies[i],
  859. BIsActive = true,
  860. PartyPos = (sbyte)(-1 - x.i),
  861. BIsUntargetable = encounter.UntargetableEnemy[i],
  862. AnimationSystem = new AnimationSystem { AnimationQueue = new ConcurrentQueue<int>() },
  863. Location = encounter.EnemyCoordinates[i], //each instance needs own location.
  864. FixedLevel = encounter.BLevels[i]
  865. };
  866. }).Where(x => x != null).ToList();
  867. }
  868. private static void ResetTime() => _frameTime = TimeSpan.FromTicks(_frameTime.Ticks % FPS.Ticks);
  869. private static void StartAnimations()
  870. {
  871. foreach (var c in _characterInstances)
  872. c.AnimationSystem.StartAnimation();
  873. foreach (var e in Enemy.Party)
  874. e.EII.AnimationSystem.StartAnimation();
  875. }
  876. private static void StopAnimations()
  877. {
  878. foreach (var c in _characterInstances)
  879. c.AnimationSystem.StopAnimation();
  880. foreach (var e in Enemy.Party)
  881. e.EII.AnimationSystem.StopAnimation();
  882. }
  883. /// <summary>
  884. /// Increments animation frames by N, where N is equal to int(deltaTime/FPS). 15FPS is one
  885. /// frame per ~66 milliseconds. Therefore if deltaTime hits at least: below 33, then frame
  886. /// gets interpolated above 122, then frame gets skipped (by x/66)
  887. /// </summary>
  888. private static void UpdateFrames()
  889. {
  890. _frameTime += Memory.ElapsedGameTime;
  891. if (_frameTime <= FPS) return;
  892. if (Enemy.Party != null)
  893. foreach (var e in Enemy.Party.Where(e => !e.EII.AnimationSystem.AnimationStopped && !e.IsPetrify))
  894. {
  895. e.EII.AnimationSystem.NextFrame();
  896. }
  897. if (_characterInstances != null)
  898. foreach (var cii in _characterInstances.Where(cii =>
  899. !cii.AnimationSystem.AnimationStopped && (!Memory.State[cii.VisibleCharacter]?.IsPetrify ?? true)))
  900. {
  901. cii.AnimationSystem.NextFrame();
  902. }
  903. ResetTime();
  904. }
  905. #endregion Methods
  906. }
  907. }