module_world_debug.cs 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using Microsoft.Xna.Framework.Input;
  4. using OpenVIII.World;
  5. using OpenVIII.Encoding.Tags;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Runtime.InteropServices;
  11. namespace OpenVIII
  12. {
  13. public class Module_world_debug
  14. {
  15. private static bool bUseCustomShaderTest = true; //enable for testing the shader- mostly learning stuff
  16. private static FPS_Camera fps_camera;
  17. public static Matrix projectionMatrix, viewMatrix, worldMatrix;
  18. public static float degrees;
  19. private static float camDistance = 120.0f;
  20. private static float camHeight = 160.0f;
  21. private static float cameraFOV = 60;
  22. public static float renderCamDistance = 1200f;
  23. public static Vector3 camPosition, camTarget;
  24. public static Vector3 playerPosition = new Vector3(-9105f, 30f, -4466);
  25. private static Vector3 lastPlayerPosition = playerPosition;
  26. public static BasicEffect effect;
  27. public static AlphaTestEffect ate;
  28. public static Effect worldShaderModel;
  29. private const float BEND_VALUE = 1.4f;
  30. private const float BEND_DISTANCE = 350.0f;
  31. private static readonly Vector3 BEND_VECTOR = new Vector3(0, -0.01f, 0);
  32. private static readonly Vector4 FOG_COLOR = new Vector4(2f, 2f, 2f, 0f);
  33. private static Vector3 skyColor = Vector3.One;
  34. public static float debugVar = 1f;
  35. private enum _worldState
  36. {
  37. _0init,
  38. _1active,
  39. _9debugFly
  40. }
  41. private enum MiniMapState
  42. {
  43. noMinimap,
  44. planet,
  45. rectangle,
  46. fullscreen
  47. }
  48. //DEBUG
  49. private const float WORLD_SCALE_MODEL = 16f;
  50. private static readonly float FOV = 60;
  51. public static Vector2 segmentPosition;
  52. private static CharaOne chara;
  53. private static texl texl;
  54. public static Wmset wmset;
  55. private static wm2field wm2field;
  56. private static rail rail;
  57. private static byte[] wmx;
  58. private static int debugEncounter = -1;
  59. private const int WM_SEG_SIZE = 0x9000; //World map segment size in file
  60. private const int WM_SEGMENTS_COUNT = 835;
  61. #region structures
  62. private static Segment[] segments;
  63. private struct Segment
  64. {
  65. public int segmentId;
  66. public SegHeader headerData;
  67. public Block[] block;
  68. /// <summary>
  69. /// parsedTriangle is a struct containing pre-calculated values for world map so the
  70. /// calculations are one-time operation
  71. /// </summary>
  72. public ParsedTriangleData[] parsedTriangle;
  73. }
  74. public struct ParsedTriangleData
  75. {
  76. public Vector3 A;
  77. public Vector3 B;
  78. public Vector3 C;
  79. public Vector2 uvA;
  80. public Vector2 uvB;
  81. public Vector2 uvC;
  82. public Polygon parentPolygon;
  83. public BoundingBox boundingBox;
  84. }
  85. private struct Block
  86. {
  87. public byte polyCount;
  88. public byte vertCount;
  89. public byte normalCount;
  90. public byte unkPadd;
  91. public Polygon[] polygons;
  92. public Vertex[] vertices;
  93. public Normal[] normals;
  94. public int unkPadd2;
  95. }
  96. [StructLayout(LayoutKind.Sequential, Size = 68, Pack = 1)]
  97. private struct SegHeader
  98. {
  99. public uint groupId;
  100. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
  101. public uint[] blockOffsets;
  102. }
  103. #pragma warning disable 0649 //Yes, we know- it's expected here
  104. public struct Polygon
  105. {
  106. public byte F1, F2, F3, N1, N2, N3, U1, V1, U2, V2, U3, V3, TPage_clut, groundtype;
  107. //private byte texSwitch, flags;
  108. public Texflags texFlags;
  109. public byte vertFlags;
  110. public byte TPage => (byte)((TPage_clut >> 4) & 0x0F);
  111. public byte Clut => (byte)(TPage_clut & 0x0F);
  112. private Texflags TexFlags { get => Texflags.TEXFLAGS_ISENTERABLE | Texflags.TEXFLAGS_MISC | Texflags.TEXFLAGS_ROAD | Texflags.TEXFLAGS_SHADOW | Texflags.TEXFLAGS_UNK | Texflags.TEXFLAGS_TRANSPARENT | Texflags.TEXFLAGS_WATER; set => texFlags = value; }
  113. //public byte TPage_clut1 { set => TPage_clut = value; }
  114. public override string ToString() => $"GP={groundtype.ToString()} TP={TPage.ToString()} Clut={Clut.ToString()} TexFlags={Convert.ToString((byte)texFlags, 2).PadLeft(8, '0')} vertFlags={Convert.ToString(vertFlags, 2).PadLeft(8, '0')} UV={U1.ToString()} {V1.ToString()} {U2.ToString()} {V2.ToString()} {U3.ToString()} {V3.ToString()}";
  115. }
  116. private struct Vertex
  117. {
  118. public short X;
  119. private short Z;
  120. public short Y;
  121. private short W;
  122. public short Z1 { get => (short)(Z * -1); set => Z = value; }
  123. }
  124. private struct Normal /*: Vertex we can't inherit struct in C#*/
  125. {
  126. public short X;
  127. private short Z;
  128. private short Y;
  129. private short W;
  130. public short Z1 { get => (short)(Z * -1); set => Z = value; }
  131. }
  132. #pragma warning restore 169
  133. #pragma warning restore 0649
  134. #endregion structures
  135. /// <summary>
  136. /// This is index to characters in chara.one file of worldmap
  137. /// </summary>
  138. public enum worldCharacters
  139. {
  140. SquallCasual,
  141. Ragnarok,
  142. Chocobo,
  143. BokoChocobo,
  144. SquallSeed,
  145. ZellCasual,
  146. SelphieCasual
  147. }
  148. public static worldCharacterInstance[] worldCharacterInstances = new worldCharacterInstance[8];
  149. public struct worldCharacterInstance
  150. {
  151. public worldCharacters activeCharacter;
  152. public Vector3 worldPosition;
  153. public float localRotation;
  154. public int currentAnimationId;
  155. public int currentAnimFrame;
  156. public TimeSpan animationDeltaTime;
  157. public bool bDraw;
  158. public float localvRotation;
  159. public Quaternion localquaternion;
  160. }
  161. private static _worldState worldState;
  162. private static MiniMapState MapState = MiniMapState.rectangle;
  163. [Flags]
  164. public enum Texflags : byte
  165. {
  166. TEXFLAGS_SHADOW = 0b11,
  167. TEXFLAGS_UNK = 0b100,
  168. TEXFLAGS_ISENTERABLE = 0b00001000,
  169. TEXFLAGS_TRANSPARENT = 0b00010000,
  170. TEXFLAGS_ROAD = 0b00100000,
  171. TEXFLAGS_WATER = 0b01000000,
  172. TEXFLAGS_MISC = 0b10000000
  173. }
  174. [Flags]
  175. private enum VertFlags
  176. {
  177. /// <summary>
  178. /// Player can walk on selected face (and only player)
  179. /// </summary>
  180. bWalkable = 0b10000000,
  181. /// <summary>
  182. /// Available exclusive to forests and tunnels
  183. /// </summary>
  184. bTreeZone = 0b01000000,
  185. /// <summary>
  186. /// Marked on faces that are NOT walkable by Player, but are by Chocobo- a thin water for example
  187. /// </summary>
  188. bWalkableByChocobo = 0b00010000
  189. }
  190. public const byte TRIFLAGS_COLLIDE = 0b10000000;
  191. public const byte TRIFLAGS_FORESTTEST = 0b01000000;
  192. private static int GetSegment(int segID) => segID * WM_SEG_SIZE;
  193. private static void InitWorld()
  194. {
  195. fps_camera = new FPS_Camera();
  196. //init renderer
  197. effect = new BasicEffect(Memory.Graphics.GraphicsDevice);
  198. effect.EnableDefaultLighting();
  199. effect.TextureEnabled = true;
  200. effect.DirectionalLight0.Enabled = true;
  201. effect.DirectionalLight1.Enabled = false;
  202. effect.DirectionalLight2.Enabled = false;
  203. effect.DirectionalLight0.Direction = new Vector3(
  204. -0.349999f,
  205. 0.499999f,
  206. -0.650000f
  207. );
  208. effect.DirectionalLight0.SpecularColor = new Vector3(0.8500003f, 0.8500003f, 0.8500003f);
  209. effect.DirectionalLight0.DiffuseColor = new Vector3(1.54999f, 1.54999f, 1.54999f);
  210. camTarget = new Vector3(0, 0f, 0f);
  211. camPosition = new Vector3(-9100.781f, 108.0096f, -4438.435f);
  212. projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
  213. MathHelper.ToRadians(cameraFOV),
  214. Memory.Graphics.GraphicsDevice.Viewport.AspectRatio,
  215. 1f, 10000f);
  216. viewMatrix = Matrix.CreateLookAt(camPosition, camTarget,
  217. new Vector3(0f, 1f, 0f));// Y up
  218. //worldMatrix = Matrix.CreateWorld(camTarget, Vector3.
  219. // Forward, Vector3.Up);
  220. worldMatrix = Matrix.CreateTranslation(0, 0, 0);
  221. if (bUseCustomShaderTest)
  222. {
  223. worldShaderModel = Memory.Content.Load<Effect>("testShader");
  224. worldShaderModel.Parameters["World"].SetValue(worldMatrix);
  225. worldShaderModel.Parameters["View"].SetValue(viewMatrix);
  226. worldShaderModel.Parameters["Projection"].SetValue(projectionMatrix);
  227. worldShaderModel.Parameters["bendValue"].SetValue(BEND_VALUE);
  228. worldShaderModel.Parameters["bendDistance"].SetValue(BEND_DISTANCE);
  229. worldShaderModel.Parameters["bendVector"].SetValue(BEND_VECTOR);
  230. worldShaderModel.Parameters["Projection"].SetValue(projectionMatrix);
  231. worldShaderModel.Parameters["fogColor"].SetValue(FOG_COLOR);
  232. worldShaderModel.Parameters["Transparency"].SetValue(1f);
  233. }
  234. //temporarily disabling this, because I'm getting more and more tired of this music playing over and over when debugging
  235. //Memory.musicIndex = 30;
  236. //AV.Music.Play();
  237. ate = new AlphaTestEffect(Memory.Graphics.GraphicsDevice)
  238. {
  239. Projection = projectionMatrix,
  240. View = viewMatrix,
  241. World = worldMatrix,
  242. FogEnabled = true,
  243. FogColor = Color.CornflowerBlue.ToVector3(),
  244. FogStart = 9.75f,
  245. FogEnd = renderCamDistance
  246. };
  247. ReadWorldMapFiles();
  248. worldCharacterInstances[currentControllableEntity] = new worldCharacterInstance() { activeCharacter = worldCharacters.SquallSeed, worldPosition = playerPosition, localRotation = -90f, currentAnimationId = 0, currentAnimFrame = 0 };
  249. worldState++;
  250. return;
  251. }
  252. private static void ReadWorldMapFiles()
  253. {
  254. var aw = ArchiveWorker.Load(Memory.Archives.A_WORLD);
  255. var awMain = ArchiveWorker.Load(Memory.Archives.A_MAIN);
  256. var wmxPath = aw.GetListOfFiles().Where(x => x.ToLower().Contains("wmx.obj")).Select(x => x).First();
  257. var texlPath = aw.GetListOfFiles().Where(x => x.ToLower().Contains("texl.obj")).Select(x => x).First();
  258. var wmPath = aw.GetListOfFiles().Where(x => x.ToLower().Contains($"wmset{Extended.GetLanguageShort(true)}.obj")).Select(x => x).First();
  259. var charaOne = aw.GetListOfFiles().Where(x => x.ToLower().Contains("chara.one")).Select(x => x).First();
  260. var railFile = aw.GetListOfFiles().Where(x => x.ToLower().Contains("rail.obj")).Select(x => x).First();
  261. wmx = aw.GetBinaryFile(wmxPath);
  262. texl = new texl(aw.GetBinaryFile(texlPath));
  263. chara = new CharaOne(aw.GetBinaryFile(charaOne));
  264. wmset = new Wmset(aw.GetBinaryFile(wmPath));
  265. rail = new rail(aw.GetBinaryFile(railFile));
  266. var wm2fieldPath = awMain.GetListOfFiles().Where(x => x.ToLower().Contains("wm2field.tbl")).Select(x => x).First();
  267. wm2field = new wm2field(awMain.GetBinaryFile(wm2fieldPath));
  268. //let's update chara texture indexes due to worldmap VRAM tex atlas behaviour
  269. chara.AssignTextureSizesForMchInstance(0, new int[] { 0, 1 }); //naturally
  270. chara.AssignTextureSizesForMchInstance(1, new int[] { 2, 3, 4, 5 }); //ragnarok uses 4 textures!
  271. for (var i = 2; i < Enum.GetNames(typeof(worldCharacters)).Length; i++)
  272. chara.AssignTextureSizesForMchInstance(i, new int[] { i * 2 + 2, i * 2 + 3 }); //after ragnarok casual two textures per mesh + two additional due to ragnarok
  273. segments = new Segment[WM_SEGMENTS_COUNT];
  274. MemoryStream ms = null;
  275. using (var br = new BinaryReader(ms = new MemoryStream(wmx)))
  276. {
  277. for (var i = 0; i < segments.Length; i++)
  278. {
  279. ms.Seek(GetSegment(i), SeekOrigin.Begin);
  280. segments[i] = new Segment { segmentId = i, headerData = Extended.ByteArrayToStructure<SegHeader>(br.ReadBytes(68)), block = new Block[16] };
  281. ms.Seek(GetSegment(i), SeekOrigin.Begin);
  282. for (var n = 0; n < segments[i].block.Length; n++)
  283. {
  284. ms.Seek(segments[i].headerData.blockOffsets[n] + GetSegment(i), SeekOrigin.Begin);
  285. segments[i].block[n] = new Block { polyCount = br.ReadByte(), vertCount = br.ReadByte(), normalCount = br.ReadByte(), unkPadd = br.ReadByte() };
  286. segments[i].block[n].polygons = new Polygon[segments[i].block[n].polyCount];
  287. segments[i].block[n].vertices = new Vertex[segments[i].block[n].vertCount];
  288. segments[i].block[n].normals = new Normal[segments[i].block[n].normalCount];
  289. for (var k = 0; k < segments[i].block[n].polyCount; k++)
  290. segments[i].block[n].polygons[k] = Extended.ByteArrayToStructure<Polygon>(br.ReadBytes(16));
  291. for (var k = 0; k < segments[i].block[n].vertCount; k++)
  292. segments[i].block[n].vertices[k] = Extended.ByteArrayToStructure<Vertex>(br.ReadBytes(8));
  293. for (var k = 0; k < segments[i].block[n].normalCount; k++)
  294. segments[i].block[n].normals[k] = Extended.ByteArrayToStructure<Normal>(br.ReadBytes(8));
  295. segments[i].block[n].unkPadd2 = br.ReadInt32();
  296. }
  297. var ptd = new List<ParsedTriangleData>();
  298. var interI = interchangeableZones.GetInterchangableSegmentReplacementIndex(i);
  299. var baseX = 512f * (interI % 32);
  300. var baseY = -512f * (interI / 32);
  301. for (var n = 0; n < segments[i].block.Length; n++)
  302. {
  303. float localX = 2048 * (n % 4);
  304. float localZ = -2048 * (n / 4);
  305. for (var k = 0; k < segments[i].block[n].polyCount; k++)
  306. {
  307. Vector2[] uvs = {
  308. new Vector2(segments[i].block[n].polygons[k].U1 / 256.0f, segments[i].block[n].polygons[k].V1 / 256.0f),
  309. new Vector2(segments[i].block[n].polygons[k].U2 / 256.0f, segments[i].block[n].polygons[k].V2 / 256.0f),
  310. new Vector2(segments[i].block[n].polygons[k].U3 / 256.0f, segments[i].block[n].polygons[k].V3 / 256.0f)
  311. };
  312. if (segments[i].block[n].polygons[k].texFlags.HasFlag(Texflags.TEXFLAGS_ROAD)) //this is roads UV fix
  313. {
  314. uvs[0] += new Vector2(0f, 0.002f);
  315. uvs[1] += new Vector2(0f, 0.002f);
  316. uvs[2] += new Vector2(0f, 0.002f);
  317. }
  318. ptd.Add(new ParsedTriangleData()
  319. {
  320. A = new Vector3(
  321. ((segments[i].block[n].vertices[segments[i].block[n].polygons[k].F1].X + localX) / WORLD_SCALE_MODEL + baseX) * -1f,
  322. segments[i].block[n].vertices[segments[i].block[n].polygons[k].F1].Z1 / WORLD_SCALE_MODEL,
  323. (segments[i].block[n].vertices[segments[i].block[n].polygons[k].F1].Y + localZ) / WORLD_SCALE_MODEL + baseY),
  324. uvA = uvs[0],
  325. parentPolygon = segments[i].block[n].polygons[k],
  326. B = new Vector3(
  327. ((segments[i].block[n].vertices[segments[i].block[n].polygons[k].F2].X + localX) / WORLD_SCALE_MODEL + baseX) * -1f,
  328. segments[i].block[n].vertices[segments[i].block[n].polygons[k].F2].Z1 / WORLD_SCALE_MODEL,
  329. (segments[i].block[n].vertices[segments[i].block[n].polygons[k].F2].Y + localZ) / WORLD_SCALE_MODEL + baseY),
  330. uvB = uvs[1],
  331. C = new Vector3(
  332. ((segments[i].block[n].vertices[segments[i].block[n].polygons[k].F3].X + localX) / WORLD_SCALE_MODEL + baseX) * -1f,
  333. segments[i].block[n].vertices[segments[i].block[n].polygons[k].F3].Z1 / WORLD_SCALE_MODEL,
  334. (segments[i].block[n].vertices[segments[i].block[n].polygons[k].F3].Y + localZ) / WORLD_SCALE_MODEL + baseY),
  335. uvC = uvs[2]
  336. });
  337. var ptda = ptd[ptd.Count - 1];
  338. ptda.boundingBox = Extended.GetBoundingBox(ptda.A, ptda.B, ptda.C);
  339. ptd[ptd.Count - 1] = ptda;
  340. }
  341. }
  342. segments[i].parsedTriangle = ptd.ToArray();
  343. }
  344. ms = null;
  345. }
  346. }
  347. public static bool bHasMoved = false;
  348. public static bool bFirstRun = true;
  349. public static int currentControllableEntity = 0;
  350. public static void Update(GameTime deltaTime)
  351. {
  352. UpdateTextureAnimation();
  353. switch (worldState)
  354. {
  355. case _worldState._0init:
  356. InitWorld();
  357. break;
  358. case _worldState._1active:
  359. OrbitCamera();
  360. break;
  361. case _worldState._9debugFly:
  362. viewMatrix = fps_camera.Update(ref camPosition, ref camTarget, ref degrees);
  363. break;
  364. }
  365. InputUpdate();
  366. CollisionUpdate();
  367. AnimationUpdate();
  368. if (bHasMoved || bFirstRun)
  369. {
  370. worldCharacterInstances[currentControllableEntity].worldPosition = playerPosition;
  371. EncounterUpdate();
  372. bFirstRun = false;
  373. }
  374. if (bHasMoved)
  375. {
  376. UpdatePlayerQuaternion(ref worldCharacterInstances[currentControllableEntity].localquaternion);
  377. }
  378. }
  379. private static void AnimationUpdate()
  380. {
  381. if (bHasMoved)
  382. {
  383. if (worldCharacterInstances[currentControllableEntity].activeCharacter == worldCharacters.Ragnarok)
  384. {
  385. //some other anim system
  386. }
  387. else
  388. {
  389. worldCharacterInstances[currentControllableEntity].currentAnimationId = 1;
  390. }
  391. }
  392. else
  393. worldCharacterInstances[currentControllableEntity].currentAnimationId = 0;
  394. var reverse = false;
  395. for (var i = 0; i < worldCharacterInstances.Length; i++)
  396. {
  397. var instance = worldCharacterInstances[i];
  398. var flying = currentControllableEntity != i;
  399. var framesCount = chara.GetMCH((int)instance.activeCharacter).GetAnimationFramesCount(instance.currentAnimationId);
  400. if (!(instance as worldCharacterInstance?).HasValue)
  401. continue;
  402. if (instance.activeCharacter == worldCharacters.Ragnarok && !flying)
  403. reverse = true;
  404. if (instance.animationDeltaTime >= TimePerFrame)
  405. {
  406. if (reverse)
  407. instance.currentAnimFrame--;
  408. else
  409. {
  410. var Frames = (int)(instance.animationDeltaTime.TotalMilliseconds / TimePerFrame.TotalMilliseconds);
  411. Frames = MathHelper.Clamp(Frames, 0, 3);
  412. instance.currentAnimFrame += Frames ;
  413. }
  414. instance.animationDeltaTime = TimeSpan.Zero;
  415. }
  416. else instance.animationDeltaTime += Memory.ElapsedGameTime;
  417. if (instance.activeCharacter == worldCharacters.Ragnarok)
  418. {
  419. if (flying)
  420. {
  421. if (instance.currentAnimFrame > framesCount - 1)
  422. continue;
  423. }
  424. else
  425. {
  426. if (instance.currentAnimFrame < 0)
  427. continue;
  428. }
  429. }
  430. if (instance.currentAnimFrame > framesCount - 1)
  431. instance.currentAnimFrame = 0;
  432. else if (instance.currentAnimFrame < 0)
  433. instance.currentAnimFrame = checked((int)framesCount - 1);
  434. worldCharacterInstances[i] = instance;
  435. }
  436. }
  437. private static TimeSpan TimePerFrame => TimeSpan.FromMilliseconds(1000 / 60.0);
  438. private static void UpdateTextureAnimation()
  439. {
  440. if (wmset == null)
  441. return;
  442. var beachAnims = wmset.BeachAnimations;
  443. var waterAnims = wmset.WaterAnimations;
  444. UpdateTextureAnimation_SelectedStruct(ref beachAnims);
  445. UpdateTextureAnimation_SelectedStruct(ref waterAnims, true);
  446. wmset.BeachAnimations = beachAnims;
  447. wmset.WaterAnimations = waterAnims;
  448. }
  449. private static void UpdateTextureAnimation_SelectedStruct(ref Wmset.textureAnimation[] beachAnims, bool bWater = false)
  450. {
  451. for (var i = 0; i < beachAnims.Length; i++)
  452. {
  453. var totalMaxValue = TimeSpan.FromMilliseconds((15.625f * beachAnims[i].animTimeout)); //1 is 15.625 milliseconds, because 0x20 is 500 milliseconds
  454. beachAnims[i].deltaTime += Memory.ElapsedGameTime;
  455. if (beachAnims[i].deltaTime > totalMaxValue)
  456. {
  457. if (beachAnims[i].bIncrementing)
  458. beachAnims[i].currentAnimationIndex++;
  459. else
  460. beachAnims[i].currentAnimationIndex--;
  461. beachAnims[i].deltaTime = TimeSpan.Zero;
  462. if (beachAnims[i].currentAnimationIndex >= beachAnims[i].framesCount)
  463. if (beachAnims[i].bLooping > 0)
  464. {
  465. beachAnims[i].bIncrementing = !beachAnims[i].bIncrementing;
  466. beachAnims[i].currentAnimationIndex = beachAnims[i].framesCount - 2;
  467. }
  468. else
  469. beachAnims[i].currentAnimationIndex = 0;
  470. if (beachAnims[i].currentAnimationIndex < 0)
  471. {
  472. beachAnims[i].currentAnimationIndex = 1;
  473. beachAnims[i].bIncrementing = !beachAnims[i].bIncrementing;
  474. }
  475. if (bWater)
  476. wmset.UpdateWorldMapWaterTexturePaletteForAnimation(i, wmset.GetWaterAnimationPalettes(i, beachAnims[i].currentAnimationIndex));
  477. }
  478. }
  479. }
  480. /// <summary>
  481. /// If player moved then check for available encounters and if we should play it
  482. /// </summary>
  483. private static void EncounterUpdate()
  484. {
  485. //RE: if ((world_currentVehicle < 0 || world_currentVehicle > 9) && world_currentVehicle != 128 || !isStateOfMovement //Naturally, we don't want encounters if in vehicle
  486. int regionId = wmset.GetWorldRegionBySegmentPosition((int)segmentPosition.X, (int)segmentPosition.Y); //section2
  487. if (activeCollidePolygon == null)
  488. return;
  489. int groundId = activeCollidePolygon.Value.groundtype;
  490. int encPointer = wmset.GetEncounterHelperPointer(regionId, groundId); //section1
  491. if (encPointer == 0xffff)
  492. return;
  493. var AvailableEncounters = wmset.GetEncounters(encPointer); //section4
  494. //we now have 8 encounters-> 4 casual; 2 mid and 2 rare
  495. var encounterRoll = Memory.Random.Next(16 + 4 + 2);
  496. //casual
  497. if (encounterRoll < 4) //0123
  498. Memory.Encounters.ID = AvailableEncounters[0];
  499. else if (encounterRoll >= 4 && encounterRoll < 8) //4567
  500. Memory.Encounters.ID = AvailableEncounters[1];
  501. else if (encounterRoll >= 8 && encounterRoll < 12) //891011
  502. Memory.Encounters.ID = AvailableEncounters[2];
  503. else if (encounterRoll >= 12 && encounterRoll < 16) //12131415
  504. Memory.Encounters.ID = AvailableEncounters[3];
  505. else if (encounterRoll >= 16 && encounterRoll < 18) //1617
  506. Memory.Encounters.ID = AvailableEncounters[4];
  507. else if (encounterRoll >= 18 && encounterRoll < 20) //1819
  508. Memory.Encounters.ID = AvailableEncounters[5];
  509. else if (encounterRoll == 20) //20
  510. Memory.Encounters.ID = AvailableEncounters[6];
  511. else if (encounterRoll == 21) //21
  512. Memory.Encounters.ID = AvailableEncounters[7];
  513. debugEncounter = Memory.Encounters.ID;
  514. //TODO random + enc.half/none junction + warping to battle
  515. var state = Memory.State;
  516. }
  517. /// <summary>
  518. /// Convert vector to angle
  519. /// </summary>
  520. /// <param name="vector"></param>
  521. /// <returns></returns>
  522. /// <see cref="https://stackoverflow.com/questions/2276855/xna-2d-vector-angles-whats-the-correct-way-to-calculate"/>
  523. public static float VectorToAngle(Vector2 vector) => (float)Math.Atan2(vector.Y, -vector.X);
  524. public static float DetectedSpeed;
  525. private const float playerSpeed = 25f; //the lower the faster
  526. /// <summary>
  527. /// Provides 4-axis support for input of currently controlled entity
  528. /// TODO: extend to 360/ fix diagonal double speed/ calculate local degrees based on sticks
  529. /// </summary>
  530. /// <param name="localquaternion"></param>
  531. private static void InputUpdate()
  532. {
  533. if (Input2.Button(InputActions.Cancel))
  534. {
  535. Memory.Module = OpenVIII.Module.MainMenuDebug;
  536. return;
  537. }
  538. bHasMoved = false;
  539. lastPlayerPosition = playerPosition;
  540. if (Input2.Button(Keys.F1))
  541. bLockMouse = !bLockMouse;
  542. if (bLockMouse != true)
  543. {
  544. Memory.IsMouseVisible = true;
  545. return;
  546. }
  547. else
  548. Memory.IsMouseVisible = false;
  549. if (Input2.DelayedButton(Keys.J) || Input2.DelayedButton(FF8TextTagKey.Select))
  550. MapState = MapState >= MiniMapState.fullscreen ? MapState = 0 : MapState + 1;
  551. if (Input2.DelayedButton(Keys.R))
  552. worldState = _worldState._0init;
  553. if (Input2.Button(Keys.D9))
  554. worldState = worldState == _worldState._1active ? _worldState._9debugFly : _worldState._1active;
  555. if (Input2.Button(Keys.D8))
  556. bDebugDisableCollision = !bDebugDisableCollision;
  557. if (MapState == MiniMapState.fullscreen) //FULLSCREEN MAP
  558. {
  559. MiniMaps.Input();
  560. }
  561. else if (worldState != _worldState._9debugFly)
  562. {
  563. var shift = InputGamePad.Distance(GamePadButtons.ThumbSticks_Left, 1f);
  564. var right = InputGamePad.Distance(GamePadButtons.ThumbSticks_Right, 1f);
  565. if (right.Y != 0 && worldCharacterInstances[currentControllableEntity].activeCharacter == worldCharacters.Ragnarok)
  566. {
  567. playerPosition.Y += MathHelper.Clamp(right.Y, -10f, 10f) / 10f;
  568. bHasMoved = true;
  569. }
  570. if (shift != Vector2.Zero)
  571. {
  572. var angle = VectorToAngle(shift);
  573. //Debug.WriteLine($"Shift: {shift} Angle: {MathHelper.ToDegrees(angle)} Camera: {degrees}");
  574. playerPosition.X += (float)Math.Cos(MathHelper.ToRadians(MathHelper.ToDegrees(angle) + degrees - 90));
  575. playerPosition.Z += (float)Math.Sin(MathHelper.ToRadians(MathHelper.ToDegrees(angle) + degrees - 90));
  576. bHasMoved = true;
  577. }
  578. else
  579. {
  580. // the shift vector in the ifs seemed to give undesired results.
  581. if (Input2.Button(FF8TextTagKey.Up)/* || shift.Y > 0*/)
  582. {
  583. playerPosition.X += (float)Math.Cos(MathHelper.ToRadians(degrees));
  584. playerPosition.Z += (float)Math.Sin(MathHelper.ToRadians(degrees));
  585. //localRotation = (float)Extended.Radians(-degrees - 90f);
  586. bHasMoved = true;
  587. }
  588. else if (Input2.Button(FF8TextTagKey.Down)/* || shift.Y < 0*/)
  589. {
  590. playerPosition.X -= (float)Math.Cos(MathHelper.ToRadians(degrees));
  591. playerPosition.Z -= (float)Math.Sin(MathHelper.ToRadians(degrees));
  592. //localRotation = (float)Extended.Radians(-degrees + 90f);
  593. bHasMoved = true;
  594. }
  595. if (Input2.Button(FF8TextTagKey.Left) /*|| shift.X < 0*/)
  596. {
  597. playerPosition.X += (float)Math.Cos(MathHelper.ToRadians(degrees - 90f));
  598. playerPosition.Z += (float)Math.Sin(MathHelper.ToRadians(degrees - 90f));
  599. //localRotation = (float)Extended.Radians(-degrees);
  600. bHasMoved = true;
  601. }
  602. else if (Input2.Button(FF8TextTagKey.Right)/* || shift.X > 0*/)
  603. {
  604. playerPosition.X += (float)Math.Cos(MathHelper.ToRadians(degrees + 90f));
  605. playerPosition.Z += (float)Math.Sin(MathHelper.ToRadians(degrees + 90f));
  606. //localRotation = (float)Extended.Radians(180f - degrees);
  607. bHasMoved = true;
  608. }
  609. }
  610. if (bHasMoved)
  611. {
  612. var diffvect = (playerPosition - lastPlayerPosition); // gets the vector between the old and new calculated pos.
  613. diffvect.Normalize(); //prevents speed up from going in more than one direction.
  614. // this will slow your movement based on stick.
  615. //not best for squall but maybe for vehicles
  616. if (shift != Vector2.Zero && InVehicle)
  617. {
  618. const float distmax = 10f;
  619. var dist = MathHelper.Clamp(Vector2.Distance(Vector2.Zero, shift), 0f, distmax);
  620. //Debug.WriteLine($"Dist: {dist}, DistMax: {distmax}");
  621. diffvect *= dist / distmax;
  622. }
  623. playerPosition = lastPlayerPosition + (diffvect * (float)(Memory.ElapsedGameTime.TotalMilliseconds/ playerSpeed));
  624. }
  625. }
  626. if (Input2.Button(Keys.F3))
  627. {
  628. Menu.BattleMenus.CameFrom(); // allows returning to current state after victory menu complete.
  629. Extended.postBackBufferDelegate = BattleSwirl.Init;
  630. Extended.RequestBackBuffer();
  631. }
  632. }
  633. private static void UpdatePlayerQuaternion(ref Quaternion localquaternion)
  634. {
  635. if (playerPosition != lastPlayerPosition)
  636. {
  637. DetectedSpeed = Vector3.Distance(playerPosition, lastPlayerPosition);
  638. float yaw = 0f, pitch = 0f, roll = 0f;
  639. //https://www.codeproject.com/Questions/324240/Determining-yaw-pitch-and-roll
  640. var matrix = Matrix.CreateLookAt(lastPlayerPosition, playerPosition, Vector3.Up);
  641. yaw = (float)Math.Atan2(matrix.M13, matrix.M33);
  642. pitch = (float)Math.Asin(-matrix.M23);
  643. //roll = (float)Math.Atan2(matrix.M21, matrix.M22);
  644. //yaw = (float)Math.Atan2(-diffvect.X, -diffvect.Z); //this seems to make squall face the correct direction each time.
  645. //pitch = (float)Math.Atan2(diffvect.Y, Math.Abs(diffvect.X)); // unsure if this is prefect or not.
  646. if (yaw == 0f) yaw = worldCharacterInstances[currentControllableEntity].localRotation;
  647. else
  648. worldCharacterInstances[currentControllableEntity].localRotation = yaw;
  649. localquaternion = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll);
  650. worldCharacterInstances[currentControllableEntity].localvRotation = pitch;
  651. }
  652. else
  653. DetectedSpeed = 0f;
  654. }
  655. /// <summary>
  656. /// ParsedTriangleData- struct contains all available data paired with found triangle Vector3
  657. /// - contains barycentric based on playerPosition bool - bIsSkyRaycasted - used for sky raycast
  658. /// </summary>
  659. private static List<RayCastedTris> RaycastedTris;
  660. /// <summary>
  661. /// This method checks for collision- uses raycasting and 3Dintersection to either allow
  662. /// movement, update it and/or warp player. If all checks fails it returns to last known
  663. /// correct player position This points to polygon structure that is actively used/ character
  664. /// stomps on it </summary>
  665. public static Polygon? activeCollidePolygon = null;
  666. public static int GetRealSegmentId() => (int)(segmentPosition.Y * 32 + segmentPosition.X); //explicit public for wmset and warping sections
  667. public static int GetRealSegmentId(float x, float y) => (int)((y < 0 ? 24 + y : y) * 32 + (x < 0 ? 32 + x : x)); //explicit public for wmset and warping sections
  668. public struct RayCastedTris
  669. {
  670. public ParsedTriangleData data;
  671. public Vector3 pos;
  672. public bool sky;
  673. public RayCastedTris(ParsedTriangleData data, Vector3 pos, bool sky)
  674. {
  675. this.data = data;
  676. this.pos = pos;
  677. this.sky = sky;
  678. }
  679. }
  680. private static float MinY
  681. {
  682. get
  683. {
  684. var worldCharacterInstance = worldCharacterInstances[currentControllableEntity];
  685. switch (worldCharacterInstance.activeCharacter)
  686. {
  687. case worldCharacters.Ragnarok:
  688. return (20 - worldCharacterInstance.currentAnimFrame) * .5f;
  689. default:
  690. return 0f;
  691. }
  692. }
  693. }
  694. private static string CollisionStringDebug;
  695. /// <summary>
  696. /// This method checks for collision- uses raycasting and 3Dintersection to either allow
  697. /// movement, update it and/or warp player. If all checks fails it returns to last known
  698. /// correct player position
  699. /// </summary>
  700. private static void CollisionUpdate()
  701. {
  702. segmentPosition = new Vector2((int)(playerPosition.X / 512) * -1, (int)(playerPosition.Z / 512) * -1); //needs to be updated on pre-new values of movement
  703. var realSegmentId = GetRealSegmentId();
  704. realSegmentId = interchangeableZones.SetInterchangeableZone(realSegmentId);
  705. var seg = segments[realSegmentId];
  706. RaycastedTris = new List<RayCastedTris>();
  707. var position = playerPosition + new Vector3(0, 15f, 0);
  708. var characterRay = new Ray(position, Vector3.Down); //sets ray origin
  709. var characterRay2 = new Ray(position, Vector3.Up); //sets ray origin
  710. var skyRay = new Ray(GetForwardSkyRaycastVector(SKYRAYCAST_FIXEDDISTANCE), Vector3.Down);
  711. //loop through current block triangles - two rays at the same time. There are only two rays and multi triangles, so iterate triangles and check rays instead of double checking
  712. for (var i = 0; i < seg.parsedTriangle.Length; i++)
  713. if (Extended.RayIntersection3D(characterRay, seg.parsedTriangle[i].A, seg.parsedTriangle[i].B, seg.parsedTriangle[i].C, out var characterBarycentric) != 0)
  714. RaycastedTris.Add(new RayCastedTris(seg.parsedTriangle[i], characterBarycentric, false));
  715. // There are spots where you can fly under the map by like flying into the ground or
  716. // a corner. This would put the ship back above around.
  717. else if (BDebugDisableCollision && Extended.RayIntersection3D(characterRay2, seg.parsedTriangle[i].A, seg.parsedTriangle[i].B, seg.parsedTriangle[i].C, out var characterBarycentric2) != 0)
  718. RaycastedTris.Add(new RayCastedTris(seg.parsedTriangle[i], characterBarycentric2, false));
  719. else if (Extended.RayIntersection3D(skyRay, seg.parsedTriangle[i].A, seg.parsedTriangle[i].B, seg.parsedTriangle[i].C, out var skyBarycentric) != 0)
  720. RaycastedTris.Add(new RayCastedTris(seg.parsedTriangle[i], skyBarycentric, true));
  721. //don't allow walking over non-walkable faces - just because we tested both rays we can make this linq appear only once
  722. if (!BDebugDisableCollision)
  723. RaycastedTris = OrderAndCheckTrisForcollisionsBetween().Where(x => (x.data.parentPolygon.vertFlags & TRIFLAGS_COLLIDE) != 0 && x.pos != Vector3.Zero).ToList();
  724. //WORLD MAP TO FIELD CHECK- it should be done on already walked polygon so the player will be able to
  725. //enter the triangle with warp zone and warp AFTER that, not just as he enters it
  726. WorldMapToField();
  727. const float forestAdj = -12f;//-12f;
  728. foreach (var prt in RaycastedTris)
  729. {
  730. if (prt.sky) //we do not want skyRaycasts here, iterate only characterRay
  731. continue;
  732. var distance = playerPosition - prt.pos;
  733. var distY = Math.Abs(distance.Y);
  734. if (distY >= 15f && !InVehicle) // prevents walking off a clifft most of the time.
  735. continue;
  736. if ((prt.data.parentPolygon.vertFlags & TRIFLAGS_FORESTTEST) != 0)
  737. MinYPos(prt.pos);
  738. else
  739. {
  740. MinYPos(prt.pos, forestAdj);
  741. }
  742. activeCollidePolygon = prt.data.parentPolygon;
  743. return;
  744. }
  745. //out of loop- failed to obtain collision or abandon move - we need to check now if player wanted to get to forest
  746. foreach (var prt in RaycastedTris)
  747. {
  748. if (!prt.sky) //we do not want skyRaycasts here, iterate only characterRay
  749. continue;
  750. //we do not want to check for Y here
  751. if ((prt.data.parentPolygon.vertFlags & TRIFLAGS_FORESTTEST) != 0) //this opts out non-forest faces
  752. continue;
  753. MinYPos(prt.pos, forestAdj);
  754. activeCollidePolygon = prt.data.parentPolygon;
  755. return;
  756. }
  757. if (!BDebugDisableCollision)
  758. playerPosition = lastPlayerPosition;
  759. }
  760. private static void WorldMapToField()
  761. {
  762. if (activeCollidePolygon != null)
  763. if (activeCollidePolygon.Value.texFlags.HasFlag(Texflags.TEXFLAGS_ISENTERABLE))
  764. {
  765. foreach (var warpZone in wmset.section8WarpZones)
  766. {
  767. var fieldId = wm2field.GetFieldId(warpZone.field);
  768. var bShouldWarp = true;
  769. if (warpZone.segmentId != GetRealSegmentId())
  770. continue;
  771. if (imguiStrings == null)
  772. imguiStrings = new List<string>();
  773. imguiStrings.Add("WARPZONE!");
  774. imguiStrings.Add("---------");
  775. imguiStrings.Add($"fieldId: {fieldId}");
  776. imguiStrings.Add($"First segmentId: {warpZone.segmentId}");
  777. foreach (var condition in warpZone.conditions)
  778. {
  779. //test conditions here, so far we don't really know them much enough
  780. //for example fire cavern is on the same segment as Balamb, so there's additional check with
  781. //the player position. The Fullscreen map is also created by section8 (probably)
  782. }
  783. if (bShouldWarp)
  784. {
  785. for (var contId = 0; contId < warpZone.conditions.Length; contId++)
  786. imguiStrings.Add($"{contId}: {warpZone.conditions[contId].opcode}({warpZone.conditions[contId].opcode.ToString("X")}):{warpZone.conditions[contId].argument}");
  787. // Fields.Module.ResetField();
  788. //Memory.FieldHolder.FieldID = (ushort)fieldId;
  789. //Memory.Module = MODULE.FIELD_DEBUG;
  790. }
  791. activeCollidePolygon = null; //invalidate current polygon so you won't warp twice when field2wm
  792. //invalidating activecollidepolygon is not enough- set the position too by wmset.section9
  793. }
  794. }
  795. }
  796. private static RayCastedTris? CameraCollisionUpdate()
  797. {
  798. segmentPosition = new Vector2((int)(camPosition.X / 512) * -1, (int)(camPosition.Z / 512) * -1); //needs to be updated on pre-new values of movement
  799. var realSegmentId = (int)(segmentPosition.Y * 32 + segmentPosition.X);
  800. realSegmentId = interchangeableZones.SetInterchangeableZone(realSegmentId);
  801. var seg = segments[realSegmentId];
  802. RaycastedTris = new List<RayCastedTris>();
  803. var skyRay = new Ray(new Vector3(camPosition.X, 5000f, camPosition.Z), Vector3.Down); //drops ray at camPosition from sky
  804. for (var i = 0; i < seg.parsedTriangle.Length; i++)
  805. if (Extended.RayIntersection3D(skyRay, seg.parsedTriangle[i].A, seg.parsedTriangle[i].B, seg.parsedTriangle[i].C, out var skyBarycentric) != 0)
  806. RaycastedTris.Add(new RayCastedTris(seg.parsedTriangle[i], skyBarycentric, true));
  807. //take care of sky rays only
  808. if (RaycastedTris.Count == 0)
  809. return null;
  810. else return RaycastedTris[0];
  811. }
  812. private static List<RayCastedTris> OrderAndCheckTrisForcollisionsBetween()
  813. {
  814. // Order Tris by distance to player
  815. var ordered = RaycastedTris.OrderBy(x => Vector3.Distance(playerPosition, x.pos)).ToList();
  816. var between = false;
  817. // Check to see if there is a collision between the player and the next walkable space.
  818. // prevent jumping over collision.
  819. for (var i = 0; ordered.Count > i; i++)
  820. {
  821. if (ordered[i].sky) continue;
  822. //var d = Vector3.Distance(playerPosition, ordered[i].pos);
  823. var collide = (ordered[i].data.parentPolygon.vertFlags & TRIFLAGS_COLLIDE) != 0;
  824. if (collide)
  825. between = true;
  826. else if (between)
  827. { //bassically if there is collision between mark rest to collide.
  828. //if (bHasMoved)
  829. //Debug.WriteLine(d + "\t");
  830. var x = ordered[i];
  831. x.data.parentPolygon.vertFlags |= TRIFLAGS_COLLIDE;
  832. ordered[i] = x;
  833. }
  834. }
  835. return ordered;
  836. }
  837. private static void MinYPos(Vector3 squaPos, float adj = 0f)
  838. {
  839. if (worldCharacterInstances[currentControllableEntity].activeCharacter != worldCharacters.Ragnarok || playerPosition.Y < squaPos.Y + MinY)
  840. {
  841. //Force character to min Y elivation.
  842. playerPosition.Y = squaPos.Y + MinY + adj;
  843. //This smooths out the drop down.Though this would only trigger while moving.
  844. // So would need to move this or check collision while not moving.so commented out.
  845. //Vector3 min = (squaPos + new Vector3(0, MinY + adj, 0));
  846. //if (min.Y > playerPosition.Y)
  847. // playerPosition.Y = min.Y;
  848. //else if (Vector3.Distance(squaPos, playerPosition) > 1)
  849. //{
  850. // Vector3 adjv = (playerPosition - min) * (-1f);
  851. // adjv.Normalize();
  852. // playerPosition.Y += adjv.Y;
  853. //}
  854. }
  855. }
  856. /// <summary>
  857. /// This is the relative distance that is added to forward vector of character and then
  858. /// casted from sky to bottom of the level
  859. /// </summary>
  860. private const float SKYRAYCAST_FIXEDDISTANCE = 2f;
  861. private const float RotationInterval = 1.5f;
  862. private static bool bLockMouse = true;
  863. private static int orbitCameraMode = 1; //parse from save file [TODO]
  864. private static float camSlider = 0f;
  865. private static bool camSliderDirection = false;
  866. private static float rememberedCamCollideHeight = 0f;
  867. public static void OrbitCamera()
  868. {
  869. if (bLockMouse)
  870. InputMouse.Mode = MouseLockMode.Center;
  871. else
  872. InputMouse.Mode = MouseLockMode.Disabled;
  873. if (Input2.Button(Keys.F, ButtonTrigger.OnPress)) //[TODO]
  874. {
  875. orbitCameraMode = orbitCameraMode == 0 ? 1 : 0;
  876. camSliderDirection = orbitCameraMode == 0;
  877. }
  878. if (camSliderDirection)
  879. camSlider += 1f * (float)(Memory.ElapsedGameTime.Milliseconds / 1000f);
  880. else
  881. camSlider -= 1f * (float)(Memory.ElapsedGameTime.Milliseconds / 1000f);
  882. camSlider = MathHelper.Clamp(camSlider, 0f, 1f);
  883. switch (orbitCameraMode)
  884. {
  885. case 0: //closeup
  886. camHeight = MathHelper.Lerp(200f, 100f + rememberedCamCollideHeight, camSlider);
  887. cameraFOV = MathHelper.Lerp(42f, 44f, camSlider);
  888. var activepoly = CameraCollisionUpdate();
  889. if (activepoly.HasValue)
  890. rememberedCamCollideHeight = activepoly.Value.pos.Y;
  891. break;
  892. case 1: //faraway
  893. camHeight = MathHelper.Lerp(200f, 100f + rememberedCamCollideHeight, camSlider);
  894. cameraFOV = MathHelper.Lerp(42, 44, camSlider);
  895. break;
  896. }
  897. camPosition = new Vector3(
  898. (float)(playerPosition.X + camDistance * Extended.Cos(degrees - 180f)),
  899. camHeight,//playerPosition.Y + 50f,
  900. (float)(playerPosition.Z + camDistance * Extended.Sin(degrees - 180f))
  901. );
  902. // check mouse to adjust camera
  903. var shift = InputMouse.Distance(MouseButtons.MouseToStick, FPS_Camera.maxLookSpeedMouse);
  904. // check right stick to adjust camera
  905. var rightstick = InputGamePad.Distance(GamePadButtons.ThumbSticks_Right, FPS_Camera.maxLookSpeedGamePad);
  906. //Debug.WriteLine($"{rightstick.X}");
  907. shift += rightstick;
  908. if (Input2.Button(FF8TextTagKey.RotateLeft, ButtonTrigger.Press | ButtonTrigger.IgnoreDelay))
  909. degrees -= RotationInterval * (float)Memory.ElapsedGameTime.TotalMilliseconds/25f;
  910. if (Input2.Button(FF8TextTagKey.RotateRight, ButtonTrigger.Press | ButtonTrigger.IgnoreDelay))
  911. degrees += RotationInterval *(float)Memory.ElapsedGameTime.TotalMilliseconds / 25f;
  912. degrees += shift.X;
  913. degrees %= 360f;
  914. if (degrees < 0)
  915. {
  916. degrees += 360f;
  917. }
  918. camTarget = new Vector3(playerPosition.X, 50f, playerPosition.Z);
  919. projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
  920. MathHelper.ToRadians(cameraFOV),
  921. Memory.Graphics.GraphicsDevice.Viewport.AspectRatio,
  922. 1f, 10000f);
  923. viewMatrix = Matrix.CreateLookAt(camPosition, camTarget,
  924. Vector3.Up);
  925. }
  926. private double RadianAngleFromVector3s(Vector3 a, Vector3 b) => Math.Acos(Vector3.Dot(Vector3.Normalize(a), Vector3.Normalize(b)));
  927. static Color bgGradient = Color.CornflowerBlue;
  928. static List<string> imguiStrings;
  929. static bool bImguiSec8 = false;
  930. static bool bImguiSec9 = false;
  931. static bool bImguiSec11 = false;
  932. public static void Draw()
  933. {
  934. Memory.SpriteBatch.GraphicsDevice.Clear(bgGradient);
  935. Memory.Graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
  936. Memory.Graphics.GraphicsDevice.BlendState = BlendState.NonPremultiplied;
  937. Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  938. Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  939. ate.Projection = projectionMatrix;
  940. ate.View = viewMatrix;
  941. ate.World = worldMatrix;
  942. effect.Projection = projectionMatrix;
  943. effect.View = viewMatrix;
  944. effect.World = worldMatrix;
  945. if (bUseCustomShaderTest)
  946. {
  947. worldShaderModel.Parameters["Projection"].SetValue(ate.Projection);
  948. worldShaderModel.Parameters["View"].SetValue(ate.View);
  949. worldShaderModel.Parameters["World"].SetValue(ate.World);
  950. worldShaderModel.Parameters["camWorld"].SetValue(camPosition);
  951. worldShaderModel.Parameters["skyColor"].SetValue(skyColor);
  952. }
  953. segmentPosition = new Vector2((int)(playerPosition.X / 512) * -1, (int)(playerPosition.Z / 512) * -1);
  954. if (segmentPosition.Y == 24)
  955. segmentPosition.Y = 23;
  956. if (segmentPosition.X == 32)
  957. segmentPosition.X = 31;
  958. //let's get segments ids for cube
  959. /*
  960. * SEG0 SEG1 SEG2
  961. * SEG3 CURR SEG5
  962. * SEG6 SEG7 SEG8 */
  963. DrawSegment(-1, -1); //SEG0
  964. DrawSegment(0, -1); //SEG1
  965. DrawSegment(1, -1); //SEG2
  966. DrawSegment(-1, 0); //SEG3
  967. DrawSegment(0, 0); //draws current walkable segment
  968. DrawSegment(+1, 0); //SEG5
  969. DrawSegment(-1, +1); //SEG6
  970. DrawSegment(0, +1); //SEG7
  971. DrawSegment(+1, +1); //SEG8
  972. if (degrees < 90 || degrees > 270)
  973. {
  974. DrawSegment(-2, 0);
  975. DrawSegment(-2, 1);
  976. DrawSegment(-2, -1);
  977. }
  978. if (degrees < 360 && degrees > 180)
  979. {
  980. DrawSegment(0, 2);
  981. DrawSegment(-1, 2);
  982. DrawSegment(1, 2);
  983. }
  984. if (degrees < 180 && degrees > 0)
  985. {
  986. DrawSegment(0, -2);
  987. DrawSegment(-1, -2);
  988. DrawSegment(+1, -2);
  989. if (segmentPosition.X == 31 && segmentPosition.Y == 0) //fix for the bahamuth underwater place was not visible at specific situations
  990. DrawSegment(+2, -2);
  991. }
  992. if (degrees < 270 || degrees > 90)
  993. {
  994. DrawSegment(2, 0);
  995. DrawSegment(2, 1);
  996. DrawSegment(2, -1);
  997. }
  998. TeleportPlayerWarp();
  999. CloudRender.DrawBackgroundClouds();
  1000. foreach (var charaInstance in worldCharacterInstances)
  1001. DrawCharacter(charaInstance);
  1002. DrawDebug_Rays();
  1003. SpecialEffectsRenderer.DrawCharacterShadowSpecialEffects();
  1004. switch (MapState)
  1005. {
  1006. case MiniMapState.noMinimap:
  1007. break;
  1008. case MiniMapState.planet:
  1009. MiniMaps.DrawPlanetMiniMap();
  1010. break;
  1011. case MiniMapState.rectangle:
  1012. MiniMaps.DrawRectangleMiniMap();
  1013. break;
  1014. case MiniMapState.fullscreen:
  1015. MiniMaps.DrawFullScreenMap();
  1016. break;
  1017. }
  1018. var playerangle = MathHelper.ToDegrees(worldCharacterInstances[currentControllableEntity].localRotation);
  1019. if (playerangle < 0) playerangle += 360f;
  1020. ImguiDebugDraw(playerangle);
  1021. }
  1022. private static void ImguiDebugDraw(float playerangle)
  1023. {
  1024. if (Memory.GameTime != null)
  1025. Memory.ImGui.BeforeLayout(Memory.GameTime);
  1026. ImGuiNET.ImGui.SetNextWindowPos(System.Numerics.Vector2.Zero, ImGuiNET.ImGuiCond.Once);
  1027. ImGuiNET.ImGui.SetNextWindowBgAlpha(.25f);
  1028. ImGuiNET.ImGui.Begin("WORLD DEBUG");
  1029. ImGuiNET.ImGui.Text($"Press F1 to lock or unlock mouse: ={bLockMouse}");
  1030. ImGuiNET.ImGui.Text($"Press 8 to enable/disable collision: ={bDebugDisableCollision}");
  1031. ImGuiNET.ImGui.Text($"Press 9 to enable debug FPS camera: ={(worldState == _worldState._1active ? "orbit camera" : "FPS debug camera")}");
  1032. ImGuiNET.ImGui.InputFloat("Debug variable:", ref debugVar);
  1033. ImGuiNET.ImGui.Separator();
  1034. var imgui_skyColor = new System.Numerics.Vector4(bgGradient.R / 255f, bgGradient.G / 255f, bgGradient.B / 255f, bgGradient.A / 255f); //redundancy hell
  1035. ImGuiNET.ImGui.ColorEdit4("Sky color: ", ref imgui_skyColor);
  1036. var imgui_skyColor2 = new System.Numerics.Vector4(skyColor.X, skyColor.Y, skyColor.Z, 1.0f);
  1037. ImGuiNET.ImGui.ColorEdit4("Colorize: ", ref imgui_skyColor2);
  1038. skyColor = new Vector3(imgui_skyColor2.X, imgui_skyColor2.Y, imgui_skyColor2.Z);
  1039. bgGradient = new Color(imgui_skyColor.X, imgui_skyColor.Y, imgui_skyColor.Z, imgui_skyColor.W);
  1040. ImGuiNET.ImGui.Text($"World map MapState: ={MapState}");
  1041. var imgui_cameraPosition = new System.Numerics.Vector3(camPosition.X, camPosition.Y, camPosition.Z);
  1042. ImGuiNET.ImGui.InputFloat3("World map camera", ref imgui_cameraPosition);
  1043. camPosition = new Vector3(imgui_cameraPosition.X, imgui_cameraPosition.Y, imgui_cameraPosition.Z);
  1044. ImGuiNET.ImGui.InputFloat("World map camera distance", ref camDistance);
  1045. ImGuiNET.ImGui.InputFloat("World map camera height", ref camHeight);
  1046. ImGuiNET.ImGui.InputFloat("World map camera FOV", ref cameraFOV);
  1047. ImGuiNET.ImGui.Text($"selWalk2: ={(activeCollidePolygon.HasValue ? activeCollidePolygon.Value.ToString() : "N/A")}");
  1048. ImGuiNET.ImGui.Text($"Camera mode: {orbitCameraMode}");
  1049. ImGuiNET.ImGui.Text($"Camera slide: {camSlider}");
  1050. var imgui_playerPosition = new System.Numerics.Vector3(playerPosition.X, playerPosition.Y, playerPosition.Z);
  1051. ImGuiNET.ImGui.InputFloat3("Player position: ", ref imgui_playerPosition);
  1052. playerPosition = new Vector3(imgui_playerPosition.X, imgui_playerPosition.Y, imgui_playerPosition.Z);
  1053. ImGuiNET.ImGui.Text($"Player rotation: ={playerangle}°");
  1054. ImGuiNET.ImGui.Text($"Player speed: ={DetectedSpeed} units per update");
  1055. ImGuiNET.ImGui.Text($"Segment Position: ={segmentPosition} ({GetSegmentVectorPlayerPosition()})");
  1056. ImGuiNET.ImGui.Text($"encounter: ={debugEncounter}- Press F3 to force battle");
  1057. ImGuiNET.ImGui.Text($"FOV: {FOV}");
  1058. ImGuiNET.ImGui.Text($"1000/deltaTime milliseconds: {(Memory.ElapsedGameTime.TotalSeconds > 0 ? 1d / Memory.ElapsedGameTime.TotalSeconds : 0d)}");
  1059. ImGuiNET.ImGui.Text($"imgui::FPS {ImGuiNET.ImGui.GetIO().Framerate}");
  1060. ImGuiNET.ImGui.Checkbox("Field2WM WARPLIST", ref bImguiSec9);
  1061. ImGuiNET.ImGui.Checkbox("wmset11 locations", ref bImguiSec11);
  1062. ImGuiNET.ImGui.Separator();
  1063. if (imguiStrings != null)
  1064. {
  1065. foreach (var s in imguiStrings)
  1066. ImGuiNET.ImGui.Text(s);
  1067. imguiStrings.Clear();
  1068. }
  1069. ImGuiNET.ImGui.Separator();
  1070. if (bImguiSec9)
  1071. {
  1072. ImGuiNET.ImGui.Text("-Field2WM-");
  1073. for (var x = 0; x < wmset.fieldToWorldMapLocations.Length; x++)
  1074. {
  1075. ImGuiNET.ImGui.Text(
  1076. $"{x}: X={wmset.fieldToWorldMapLocations[x].X} Y={wmset.fieldToWorldMapLocations[x].Y} Z={wmset.fieldToWorldMapLocations[x].Z}");
  1077. ImGuiNET.ImGui.SameLine();
  1078. if (ImGuiNET.ImGui.Button($"WARP {x}"))
  1079. {
  1080. playerPosition.X = wmset.fieldToWorldMapLocations[x].X;
  1081. playerPosition.Y = wmset.fieldToWorldMapLocations[x].Y;
  1082. playerPosition.Z = wmset.fieldToWorldMapLocations[x].Z;
  1083. }
  1084. }
  1085. ImGuiNET.ImGui.Separator();
  1086. }
  1087. if (bImguiSec11)
  1088. {
  1089. ImGuiNET.ImGui.Text("-Section11-");
  1090. for (var x = 0; x < wmset.sec11Locations.Length; x++)
  1091. {
  1092. ImGuiNET.ImGui.Text(
  1093. $"{x}: X={wmset.sec11Locations[x].X} Y={wmset.sec11Locations[x].Y} Z={wmset.sec11Locations[x].Z}");
  1094. ImGuiNET.ImGui.SameLine();
  1095. if (ImGuiNET.ImGui.Button($"WARP s{x}"))
  1096. {
  1097. playerPosition.X = wmset.sec11Locations[x].X;
  1098. playerPosition.Y = wmset.sec11Locations[x].Y;
  1099. playerPosition.Z = wmset.sec11Locations[x].Z;
  1100. }
  1101. }
  1102. }
  1103. if (ImGuiNET.ImGui.CollapsingHeader("fullscreenmap debugger"))
  1104. {
  1105. MiniMaps.imgui();
  1106. }
  1107. Texture2D imguiTex = null;
  1108. if (ImGuiNET.ImGui.CollapsingHeader("wmset textures"))
  1109. {
  1110. var enumValues = Enum.GetValues(typeof(Wmset.Section38_textures));
  1111. for (var i = 0; i < enumValues.Length; i++)
  1112. {
  1113. ImGuiNET.ImGui.Text($"{(Wmset.Section38_textures)i}");
  1114. ImGuiNET.ImGui.SameLine();
  1115. imguiTex = (Texture2D)wmset.GetWorldMapTexture(
  1116. (Wmset.Section38_textures)enumValues.GetValue(i), 0);
  1117. ImGuiNET.ImGui.Image(Memory.ImGui.BindTexture(imguiTex), new System.Numerics.Vector2(64, 64));
  1118. if (ImGuiNET.ImGui.IsItemHovered())
  1119. {
  1120. ImGuiNET.ImGui.BeginTooltip();
  1121. ImGuiNET.ImGui.Text($"W: {imguiTex.Width} H: {imguiTex.Height}");
  1122. ImGuiNET.ImGui.Image(Memory.ImGui.BindTexture(imguiTex), new System.Numerics.Vector2(imguiTex.Width, imguiTex.Height));
  1123. ImGuiNET.ImGui.End();
  1124. }
  1125. }
  1126. }
  1127. if (ImGuiNET.ImGui.CollapsingHeader("wmset33"))
  1128. {
  1129. for (var i = 0; i < wmset.skyColors.Length; i++)
  1130. {
  1131. var col = wmset.skyColors[i].GetLocation();
  1132. ImGuiNET.ImGui.Text($"sec33: {i}={col}");
  1133. ImGuiNET.ImGui.SameLine();
  1134. if (ImGuiNET.ImGui.Button($"sec33WARP{i}"))
  1135. {
  1136. playerPosition = col;
  1137. var color = wmset.skyColors[i].GetShadowsColor();
  1138. Vector3 colorVec = new Vector3(color.R, color.G, color.B);
  1139. colorVec.Normalize();
  1140. skyColor = colorVec;
  1141. }
  1142. ImGuiNET.ImGui.Text($"{i}= shadow {wmset.skyColors[i].GetShadowsColor()}");
  1143. ImGuiNET.ImGui.Text($"{i}= vehicle {wmset.skyColors[i].GetVehiclesColor()}");
  1144. ImGuiNET.ImGui.Text($"{i}= topBG {wmset.skyColors[i].GetTopBGColor()}");
  1145. ImGuiNET.ImGui.Text($"{i}= centerBG {wmset.skyColors[i].GetCenterBGColor()}");
  1146. ImGuiNET.ImGui.Text($"{i}= bottomBG {wmset.skyColors[i].GetBottomBGColor()}");
  1147. ImGuiNET.ImGui.Text($"{i}= 1- {wmset.skyColors[i].unk1_1} {wmset.skyColors[i].unk1_2} {wmset.skyColors[i].unk1_3} {wmset.skyColors[i].unk1_4}");
  1148. ImGuiNET.ImGui.Text($"{i}= 2- {wmset.skyColors[i].unk2_1} {wmset.skyColors[i].unk2_2} {wmset.skyColors[i].unk2_3} {wmset.skyColors[i].unk2_4}");
  1149. ImGuiNET.ImGui.Text($"{i}= 3- {wmset.skyColors[i].unk3_1} {wmset.skyColors[i].unk3_2} {wmset.skyColors[i].unk3_3} {wmset.skyColors[i].unk3_4}");
  1150. }
  1151. }
  1152. //ImGuiNET.ImGui.Begin("!Texture lister!");
  1153. //ImGuiNET.ImGui.Image(Memory.imgui.BindTexture((Texture2D)wmset.GetWorldMapTexture(wmset.Section38_textures.minimapFullScreenPointer, 0)),
  1154. // new System.Numerics.Vector2(64,64));
  1155. //ImGuiNET.ImGui.End();
  1156. ImGuiNET.ImGui.End();
  1157. Memory.ImGui.AfterLayout();
  1158. }
  1159. private static void DrawDebug_VehiclePreview()
  1160. {
  1161. var localTranslation = Module_world_debug.playerPosition + new Vector3(20f, 10f, 20f);
  1162. for (var i = 0; i < Module_world_debug.wmset.GetVehicleModelsCount(); i++)
  1163. {
  1164. var vehTex = (Texture2D)Module_world_debug.wmset.GetVehicleTexture(i, 0);
  1165. var originVector = Module_world_debug.wmset.GetVehicleTextureOriginVector(i, 0);
  1166. var dMod = Module_world_debug.wmset.GetVehicleGeometry(i, localTranslation + Vector3.Left * 50f * i, Quaternion.Identity, new Vector2(vehTex.Width, vehTex.Height), originVector);
  1167. for (var n = 0; n < dMod.Item1.Length; n += 3)
  1168. {
  1169. Module_world_debug.ate.Texture = (Texture2D)Module_world_debug.wmset.GetVehicleTexture(i, 0);
  1170. foreach (var pass in Module_world_debug.ate.CurrentTechnique.Passes)
  1171. {
  1172. pass.Apply();
  1173. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, dMod.Item1, n, 1);
  1174. }
  1175. }
  1176. }
  1177. }
  1178. private static void DrawDebug_Rays()
  1179. {
  1180. var playerRaycastDownVerts = new[] { new VertexPositionColor(playerPosition, Color.White), new VertexPositionColor(new Vector3(playerPosition.X, -1, playerPosition.Z), Color.White) };
  1181. var skyRaycastDownVerts = GetForwardSkyRaycastVector(SKYRAYCAST_FIXEDDISTANCE);
  1182. var skyVectorDropVerts = new[]
  1183. {
  1184. new VertexPositionColor(skyRaycastDownVerts, Color.White), //draw line from mockup up to the bottom fake infinity
  1185. new VertexPositionColor(new Vector3(skyRaycastDownVerts.X, -5000f, skyRaycastDownVerts.Z), Color.White)
  1186. };
  1187. if (RaycastedTris.Count != 0)
  1188. foreach (var tt in RaycastedTris)
  1189. {
  1190. var triangle = tt.data;
  1191. var verts2 = new[] {new VertexPositionColor(triangle.A, Color.White),
  1192. new VertexPositionColor(triangle.B, Color.White),
  1193. new VertexPositionColor(triangle.B, Color.White),
  1194. new VertexPositionColor(triangle.C, Color.White),
  1195. new VertexPositionColor(triangle.C, Color.White),
  1196. new VertexPositionColor(triangle.A, Color.White)
  1197. };
  1198. foreach (var pass in ate.CurrentTechnique.Passes)
  1199. {
  1200. pass.Apply();
  1201. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, verts2, 0, 3);
  1202. }
  1203. }
  1204. foreach (var pass in ate.CurrentTechnique.Passes)
  1205. {
  1206. pass.Apply();
  1207. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, playerRaycastDownVerts, 0, 1);
  1208. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, skyVectorDropVerts, 0, 1);
  1209. }
  1210. }
  1211. private static float GetSegmentVectorPlayerPosition() => segmentPosition.Y * 32 + segmentPosition.X;
  1212. /// <summary>
  1213. /// Gets the vector3 position of the raycast that drops from sky and is used for forest
  1214. /// </summary>
  1215. /// <returns></returns>
  1216. private static Vector3 GetForwardSkyRaycastVector(float distance = 5f)
  1217. {
  1218. var playerangle = MathHelper.ToDegrees(worldCharacterInstances[currentControllableEntity].localRotation);
  1219. var degreesRadians = (float)Extended.Radians(playerangle + 180f); //gets radians value of current degrees
  1220. var relativeTranslation = new Vector3(
  1221. (float)Math.Sin(degreesRadians) * distance,
  1222. 5000f,
  1223. (float)Math.Cos(degreesRadians) * distance
  1224. );
  1225. return new Vector3(playerPosition.X + relativeTranslation.X,
  1226. relativeTranslation.Y,
  1227. playerPosition.Z + relativeTranslation.Z);
  1228. }
  1229. /// <summary>
  1230. /// translates the world map model so it's vertices are drawn as close to playerPosition
  1231. /// vector as possible
  1232. /// </summary>
  1233. private static Vector3 localMchTranslation = new Vector3(0, 6f, 0);
  1234. private static Vector2 Scale;
  1235. public static bool InVehicle => worldCharacterInstances[currentControllableEntity].activeCharacter == worldCharacters.Ragnarok;
  1236. private static bool bDebugDisableCollision = false;
  1237. private static string collisionString;
  1238. public static bool BDebugDisableCollision { get => bDebugDisableCollision || worldCharacterInstances[currentControllableEntity].activeCharacter == worldCharacters.Ragnarok; set => bDebugDisableCollision = value; }
  1239. private static void DrawCharacter(worldCharacterInstance? charaInstance_)
  1240. {
  1241. if (!charaInstance_.HasValue)
  1242. return;
  1243. if (!charaInstance_.Value.bDraw)
  1244. return;
  1245. var charaInstance = charaInstance_.Value;
  1246. var MchIndex = (int)charaInstance.activeCharacter;
  1247. if (charaInstance.currentAnimationId >= chara.GetMCH(MchIndex).GetAnimationCount())
  1248. charaInstance.currentAnimationId = 0;
  1249. var charaCollection = chara.GetMCH(MchIndex).GetVertexPositions(charaInstance.worldPosition + localMchTranslation, charaInstance.localquaternion /*Quaternion.CreateFromYawPitchRoll(charaInstance.localRotation, charaInstance.localvRotation, 0f)*/, charaInstance.currentAnimationId, charaInstance.currentAnimFrame);
  1250. int textureIndexBase; //chara.one contains textures one-by-one but mch indexes are based from zero for each character. That's why we have to sum texIndexes from previous meshes
  1251. switch (charaInstance.activeCharacter)
  1252. {
  1253. case worldCharacters.Ragnarok:
  1254. textureIndexBase = 2;
  1255. break;
  1256. case worldCharacters.Chocobo:
  1257. textureIndexBase = 6;
  1258. break;
  1259. case worldCharacters.BokoChocobo:
  1260. textureIndexBase = 8;
  1261. break;
  1262. case worldCharacters.SquallSeed:
  1263. textureIndexBase = 10;
  1264. break;
  1265. case worldCharacters.ZellCasual:
  1266. textureIndexBase = 12;
  1267. break;
  1268. case worldCharacters.SelphieCasual:
  1269. textureIndexBase = 14;
  1270. break;
  1271. case worldCharacters.SquallCasual:
  1272. default:
  1273. textureIndexBase = 0;
  1274. break;
  1275. }
  1276. var vptCollection = new Dictionary<Texture2D, List<VertexPositionColorTexture>>();
  1277. for (var i = 0; i < charaCollection.Item2.Length; i += 3)
  1278. {
  1279. var charaTexture = chara.GetCharaTexture(textureIndexBase + charaCollection.Item2[i]);
  1280. if (!vptCollection.ContainsKey(charaTexture))
  1281. vptCollection.Add(charaTexture, new List<VertexPositionColorTexture>());
  1282. vptCollection[charaTexture].AddRange(charaCollection.Item1.Skip(i).Take(3).ToArray());
  1283. }
  1284. foreach (var kvp in vptCollection)
  1285. {
  1286. ate.Texture = kvp.Key;
  1287. if (bUseCustomShaderTest)
  1288. {
  1289. worldShaderModel.Parameters["ModelTexture"].SetValue(ate.Texture);
  1290. worldShaderModel.CurrentTechnique = worldShaderModel.Techniques["Texture_fog_bend"];
  1291. }
  1292. foreach (var pass in bUseCustomShaderTest ? worldShaderModel.CurrentTechnique.Passes : ate.CurrentTechnique.Passes)
  1293. {
  1294. pass.Apply();
  1295. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, kvp.Value.ToArray(), 0, kvp.Value.Count / 3);
  1296. }
  1297. }
  1298. }
  1299. /// <summary>
  1300. /// This prevents camera/player to get out of playable zone and wraps it to the other side
  1301. /// like it's 360o
  1302. /// </summary>
  1303. private static void TeleportPlayerWarp()
  1304. {
  1305. if (playerPosition.X > 0)
  1306. playerPosition.X = 32 * 512 * -1;
  1307. if (playerPosition.X < 32 * 512 * -1)
  1308. playerPosition.X = 0;
  1309. if (playerPosition.Z > 0)
  1310. playerPosition.Z = 24 * 512 * -1;
  1311. if (playerPosition.Z < 24 * 512 * -1)
  1312. playerPosition.Z = 0;
  1313. }
  1314. private static void DrawSegment(int xTranslation, int yTranslation)
  1315. {
  1316. effect.TextureEnabled = true;
  1317. var _i = GetRealSegmentId((segmentPosition.X + xTranslation) % 32, (segmentPosition.Y + yTranslation) % 24);
  1318. _i = interchangeableZones.SetInterchangeableZone(_i);
  1319. var seg = segments[_i];
  1320. var translationVector = Vector3.Zero;
  1321. var playerSegmentVector = segmentPosition;
  1322. if (playerSegmentVector.X + xTranslation < 0)
  1323. translationVector = new Vector3(32 * 512, 0, 0); //LEFT
  1324. if (playerSegmentVector.Y + yTranslation < 0)
  1325. translationVector = new Vector3(0, 0, 24 * 512); //UP
  1326. if (playerSegmentVector.X + xTranslation > 31)
  1327. translationVector = new Vector3(32 * -512, 0, 0); //RIGHT
  1328. if (playerSegmentVector.Y + yTranslation > 23)
  1329. translationVector = new Vector3(0, 0, 24 * -512); //BOTTOM
  1330. if (playerSegmentVector.X + xTranslation < 0 && playerSegmentVector.Y + yTranslation < 0 && xTranslation < 0 && yTranslation < 0) //UL diagonal wrap
  1331. translationVector = new Vector3(32 * 512, 0, 24 * 512);
  1332. if (playerSegmentVector.X + xTranslation > 31 && playerSegmentVector.Y + yTranslation < 0 && xTranslation > 0 && yTranslation < 0) //UR diagonal wrap
  1333. translationVector = new Vector3(32 * -512, 0, 24 * 512);
  1334. if (playerSegmentVector.X + xTranslation > 31 && playerSegmentVector.Y + yTranslation > 23 && xTranslation > 0 && yTranslation > 0) //BR diagonal wrap
  1335. translationVector = new Vector3(32 * -512, 0, 24 * -512);
  1336. if (playerSegmentVector.X + xTranslation < 0 && playerSegmentVector.Y + yTranslation > 23 && xTranslation < 0 && yTranslation > 0) //BL diagonal wrap
  1337. translationVector = new Vector3(32 * 512, 0, 24 * -512);
  1338. Dictionary<Texture2D, Tuple<List<VertexPositionTexture>, bool>> groupedPolygons = new Dictionary<Texture2D, Tuple<List<VertexPositionTexture>, bool>>();
  1339. Dictionary<Texture2D, Tuple<List<VertexPositionTexture>, bool>> transparentGroupedPolygons = new Dictionary<Texture2D, Tuple<List<VertexPositionTexture>, bool>>();
  1340. for (var k = 0; k < seg.parsedTriangle.Length; k++)
  1341. {
  1342. var firstEdge = seg.parsedTriangle[k].A + translationVector;
  1343. var faceDistance = Extended.Distance3D(playerPosition, firstEdge);
  1344. if (faceDistance > renderCamDistance) //this face is beyond the rendering zone; ignore whole segment!
  1345. continue;
  1346. if (CheckFrustrumView(firstEdge.X, firstEdge.Z))
  1347. continue;
  1348. var parsedTriangleB = seg.parsedTriangle[k].B + translationVector;
  1349. var parsedTriangleC = seg.parsedTriangle[k].C + translationVector;
  1350. var bIsWaterBlock = false;
  1351. var vpc = new VertexPositionTexture[3];
  1352. vpc[0] = new VertexPositionTexture(
  1353. firstEdge,
  1354. seg.parsedTriangle[k].uvA);
  1355. vpc[1] = new VertexPositionTexture(
  1356. parsedTriangleB,
  1357. seg.parsedTriangle[k].uvB);
  1358. vpc[2] = new VertexPositionTexture(
  1359. parsedTriangleC,
  1360. seg.parsedTriangle[k].uvC);
  1361. var poly = seg.parsedTriangle[k].parentPolygon;
  1362. if (poly.texFlags.HasFlag(Texflags.TEXFLAGS_ROAD))
  1363. ate.Texture = wmset.GetRoadsMiscTextures();
  1364. else if (poly.texFlags.HasFlag(Texflags.TEXFLAGS_WATER))
  1365. {
  1366. SetWaterAnimationTexture(seg, k, vpc, poly);
  1367. bIsWaterBlock = true;
  1368. }
  1369. else
  1370. ate.Texture = (Texture2D)texl.GetTexture(poly.TPage, poly.Clut);
  1371. if (poly.texFlags.HasFlag(Texflags.TEXFLAGS_TRANSPARENT))
  1372. {
  1373. if (transparentGroupedPolygons.ContainsKey(ate.Texture))
  1374. transparentGroupedPolygons[ate.Texture].Item1.AddRange(vpc);
  1375. else
  1376. transparentGroupedPolygons.Add(ate.Texture, new Tuple<List<VertexPositionTexture>, bool>(new List<VertexPositionTexture>() { vpc[0], vpc[1], vpc[2] }, bIsWaterBlock));
  1377. }
  1378. else
  1379. {
  1380. if (groupedPolygons.ContainsKey(ate.Texture))
  1381. groupedPolygons[ate.Texture].Item1.AddRange(vpc);
  1382. else
  1383. groupedPolygons.Add(ate.Texture, new Tuple<List<VertexPositionTexture>, bool>(new List<VertexPositionTexture>() { vpc[0], vpc[1], vpc[2] }, bIsWaterBlock));
  1384. }
  1385. }
  1386. //I hate to do so much redundancy here, but that's dictionary lookup, also it's important to draw important stuff
  1387. //at the very end after opaque triangles
  1388. foreach (var kvp in groupedPolygons) //normal draw
  1389. {
  1390. ate.Texture = kvp.Key;
  1391. VertexPositionTexture[] vptFinal = kvp.Value.Item1.ToArray();
  1392. ate.Alpha = 1f;
  1393. if (bUseCustomShaderTest)
  1394. {
  1395. worldShaderModel.Parameters["ModelTexture"].SetValue(ate.Texture);
  1396. worldShaderModel.Parameters["Transparency"].SetValue(1f);
  1397. }
  1398. if (kvp.Value.Item2 && bUseCustomShaderTest)
  1399. worldShaderModel.CurrentTechnique = worldShaderModel.Techniques["Texture_fog_bend_waterAnim"];
  1400. else if (bUseCustomShaderTest)
  1401. worldShaderModel.CurrentTechnique = worldShaderModel.Techniques["Texture_fog_bend"];
  1402. foreach (var pass in bUseCustomShaderTest ? worldShaderModel.CurrentTechnique.Passes : ate.CurrentTechnique.Passes)
  1403. {
  1404. pass.Apply();
  1405. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vptFinal, 0, vptFinal.Length / 3);
  1406. }
  1407. }
  1408. foreach (var kvp in transparentGroupedPolygons) //transparent draw
  1409. {
  1410. ate.Texture = kvp.Key;
  1411. VertexPositionTexture[] vptFinal = kvp.Value.Item1.ToArray();
  1412. ate.Alpha = 0.5f;
  1413. if (bUseCustomShaderTest)
  1414. {
  1415. worldShaderModel.Parameters["ModelTexture"].SetValue(ate.Texture);
  1416. worldShaderModel.Parameters["Transparency"].SetValue(0.5f);
  1417. }
  1418. if (kvp.Value.Item2 && bUseCustomShaderTest)
  1419. worldShaderModel.CurrentTechnique = worldShaderModel.Techniques["Texture_fog_bend_waterAnim"];
  1420. else if (bUseCustomShaderTest)
  1421. worldShaderModel.CurrentTechnique = worldShaderModel.Techniques["Texture_fog_bend"];
  1422. foreach (var pass in bUseCustomShaderTest ? worldShaderModel.CurrentTechnique.Passes : ate.CurrentTechnique.Passes)
  1423. {
  1424. pass.Apply();
  1425. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vptFinal, 0, vptFinal.Length / 3);
  1426. }
  1427. }
  1428. }
  1429. private static void SetWaterAnimationTexture(Segment seg, int k, VertexPositionTexture[] vpc, Polygon poly)
  1430. {
  1431. /*
  1432. * GP=10 - Beach [ANIMATED]
  1433. GP=31/CLUT0 - river endflow
  1434. GP=31/CLUT6 - waterfall [ANIMATED]
  1435. GP=31/CLUT3 - river [ANIMATED]
  1436. GP=32 - Thin water- walkable with chocobo
  1437. GP=33 - transition between thin water to ocean
  1438. GP=34 - ocean
  1439. */
  1440. var waterAtlas = wmset.GetWorldMapWaterTexture();
  1441. if (poly.groundtype == 10 || poly.groundtype == 32 || (poly.groundtype == 31 && poly.Clut == 3)) //BEACH + flat water + river flowing down
  1442. {
  1443. var @as = seg.parsedTriangle[k].parentPolygon;
  1444. var animationIdPointer = 1; //beach corner
  1445. if (@as.Clut == 2)
  1446. animationIdPointer = 0; //beach atlas
  1447. if (@as.Clut == 3 && poly.groundtype == 31)
  1448. animationIdPointer = 2; //river anim
  1449. var texx = wmset.GetBeachAnimationTextureFrame(animationIdPointer, wmset.BeachAnimations[animationIdPointer].currentAnimationIndex);
  1450. var Ucoorder = @as.Clut == 2 ? 128f : 192;
  1451. var Vcoorder = @as.Clut == 3 && poly.groundtype == 31 ? 32f : 0f;
  1452. if (poly.groundtype == 10 || (poly.groundtype == 32 && poly.Clut == 2) || (poly.groundtype == 31 && poly.Clut == 3))
  1453. {
  1454. vpc[0].TextureCoordinate = new Vector2((@as.U1 - Ucoorder) / texx.Width, (@as.V1 - Vcoorder) / texx.Height);
  1455. vpc[1].TextureCoordinate = new Vector2((@as.U2 - Ucoorder) / texx.Width, (@as.V2 - Vcoorder) / texx.Height);
  1456. vpc[2].TextureCoordinate = new Vector2((@as.U3 - Ucoorder) / texx.Width, (@as.V3 - Vcoorder) / texx.Height);
  1457. }
  1458. if (poly.groundtype == 10 || (poly.groundtype == 32 && poly.Clut == 2) || (poly.groundtype == 31 && poly.Clut == 3))
  1459. ate.Texture = wmset.GetBeachAnimationTextureFrame(animationIdPointer, wmset.BeachAnimations[animationIdPointer].currentAnimationIndex);
  1460. else if (poly.groundtype == 32)
  1461. {
  1462. vpc[0].TextureCoordinate = new Vector2(@as.U1 / (float)waterAtlas.Width, @as.V1 / (float)waterAtlas.Height);
  1463. vpc[1].TextureCoordinate = new Vector2(@as.U2 / (float)waterAtlas.Width, @as.V2 / (float)waterAtlas.Height);
  1464. vpc[2].TextureCoordinate = new Vector2(@as.U3 / (float)waterAtlas.Width, @as.V3 / (float)waterAtlas.Height);
  1465. ate.Texture = waterAtlas;
  1466. }
  1467. }
  1468. else if (Extended.In(poly.groundtype, 31, 34))
  1469. {
  1470. var @as = seg.parsedTriangle[k].parentPolygon;
  1471. vpc[0].TextureCoordinate = new Vector2(@as.U1 / (float)waterAtlas.Width, @as.V1 / (float)waterAtlas.Height);
  1472. vpc[1].TextureCoordinate = new Vector2(@as.U2 / (float)waterAtlas.Width, @as.V2 / (float)waterAtlas.Height);
  1473. vpc[2].TextureCoordinate = new Vector2(@as.U3 / (float)waterAtlas.Width, @as.V3 / (float)waterAtlas.Height);
  1474. ate.Texture = waterAtlas;
  1475. }
  1476. else
  1477. ate.Texture = (Texture2D)wmset.GetWorldMapTexture(Wmset.Section38_textures.waterTex2, 0); //FAIL- should not be used (I think)
  1478. }
  1479. /// <summary>
  1480. /// This method checks if it should not draw some faces based on frustum culling method of
  1481. /// checking point in triangle (2D geometry)
  1482. /// </summary>
  1483. /// <param name="pointX">X coordinate</param>
  1484. /// <param name="pointY">Y (actually Z from 3D) coordinate</param>
  1485. /// <returns></returns>
  1486. private static bool CheckFrustrumView(float pointX, float pointY)
  1487. {
  1488. float ax, ay, d1, d2, d3;
  1489. ax = camPosition.X + (float)Math.Cos(MathHelper.ToRadians(degrees)) * -100f;
  1490. ay = camPosition.Z + (float)Math.Sin(MathHelper.ToRadians(degrees)) * -100f;
  1491. Vector3 left = Vector3.Zero, right = Vector3.Zero;
  1492. left.X = camPosition.X + (float)Math.Cos(MathHelper.ToRadians(Extended.ClampOverload(degrees - FOV, 0, 359))) * renderCamDistance * 2;
  1493. left.Z = camPosition.Z + (float)Math.Sin(MathHelper.ToRadians(Extended.ClampOverload(degrees - FOV, 0, 359))) * renderCamDistance * 2;
  1494. right.X = camPosition.X + (float)Math.Cos(MathHelper.ToRadians(Extended.ClampOverload(degrees + FOV, 0, 359))) * renderCamDistance * 2;
  1495. right.Z = camPosition.Z + (float)Math.Sin(MathHelper.ToRadians(Extended.ClampOverload(degrees + FOV, 0, 359))) * renderCamDistance * 2;
  1496. d1 = pointX * (ay - left.Z) + pointY * (left.X - ax) + (ax * left.Z - ay * left.X);
  1497. d2 = pointX * (left.Z - right.Z) + pointY * (right.X - left.X) + (left.X * right.Z - left.Z * right.X);
  1498. d3 = pointX * (right.Z - ay) + pointY * (ax - right.X) + (right.X * ay - right.Z * ax);
  1499. return ((d1 > 0 || d2 > 0 || d3 > 0) && (d1 < 0 || d2 < 0 || d3 < 0));
  1500. }
  1501. }
  1502. }