A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

413 lines
12KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using RobocraftX.Character;
  4. using RobocraftX.Character.Movement;
  5. using RobocraftX.Common.Players;
  6. using RobocraftX.Common.Input;
  7. using RobocraftX.Physics;
  8. using RobocraftX.Blocks.Ghost;
  9. using RobocraftX.Character.Camera;
  10. using RobocraftX.Character.Factories;
  11. using Gamecraft.CharacterVulnerability;
  12. using Gamecraft.CharacterVulnerability.Entities;
  13. using Svelto.ECS;
  14. using Unity.Mathematics;
  15. using Unity.Physics;
  16. using UnityEngine;
  17. using GamecraftModdingAPI.Engines;
  18. namespace GamecraftModdingAPI.Players
  19. {
  20. internal class PlayerEngine : IApiEngine, IFactoryEngine
  21. {
  22. public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine";
  23. public EntitiesDB entitiesDB { set; private get; }
  24. public bool isRemovable => false;
  25. public IEntityFactory Factory { set; private get; }
  26. private bool isReady = false;
  27. public void Dispose()
  28. {
  29. isReady = false;
  30. }
  31. public void Ready()
  32. {
  33. isReady = true;
  34. }
  35. public uint GetLocalPlayer()
  36. {
  37. if (!isReady) return uint.MaxValue;
  38. PlayerIDStruct[] localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToFastAccess(out uint count);
  39. if (count > 0)
  40. {
  41. return localPlayers[0].ID.entityID;
  42. }
  43. return uint.MaxValue;
  44. }
  45. public uint GetRemotePlayer()
  46. {
  47. if (!isReady) return uint.MaxValue;
  48. PlayerIDStruct[] localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToFastAccess(out uint count);
  49. if (count > 0)
  50. {
  51. return localPlayers[0].ID.entityID;
  52. }
  53. return uint.MaxValue;
  54. }
  55. public bool ExistsById(uint playerId)
  56. {
  57. return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
  58. || entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
  59. }
  60. public float3 GetLocation(uint playerId)
  61. {
  62. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  63. if (exists)
  64. {
  65. return rbes.position;
  66. }
  67. return float3.zero;
  68. }
  69. public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
  70. {
  71. ExclusiveGroup[] characterGroups = CharacterExclusiveGroups.AllCharacters;
  72. for (int i = 0; i < characterGroups.Length; i++)
  73. {
  74. EGID egid = new EGID(playerId, characterGroups[i]);
  75. if (entitiesDB.Exists<RigidBodyEntityStruct>(egid))
  76. {
  77. ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity<RigidBodyEntityStruct>(egid);
  78. if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
  79. {
  80. entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
  81. entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
  82. }
  83. rbes.position = location;
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. public float3 GetRotation(uint playerId)
  90. {
  91. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  92. if (exists)
  93. {
  94. return ((Quaternion) rbes.rotation).eulerAngles;
  95. }
  96. return default;
  97. }
  98. public bool SetRotation(uint playerId, float3 value)
  99. {
  100. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  101. if (exists)
  102. {
  103. Quaternion q = rbes.rotation;
  104. q.eulerAngles = value;
  105. rbes.rotation = q;
  106. return true;
  107. }
  108. return false;
  109. }
  110. public float3 GetLinearVelocity(uint playerId)
  111. {
  112. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  113. if (exists)
  114. {
  115. return rbes.velocity;
  116. }
  117. return float3.zero;
  118. }
  119. public bool SetLinearVelocity(uint playerId, float3 value)
  120. {
  121. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  122. if (exists)
  123. {
  124. rbes.velocity = value;
  125. return true;
  126. }
  127. return false;
  128. }
  129. public float3 GetAngularVelocity(uint playerId)
  130. {
  131. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  132. if (exists)
  133. {
  134. return rbes.angularVelocity;
  135. }
  136. return float3.zero;
  137. }
  138. public bool SetAngularVelocity(uint playerId, float3 value)
  139. {
  140. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  141. if (exists)
  142. {
  143. rbes.angularVelocity = value;
  144. return true;
  145. }
  146. return false;
  147. }
  148. public PhysicsMass GetMass(uint playerId)
  149. {
  150. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  151. if (exists)
  152. {
  153. return rbes.physicsMass;
  154. }
  155. return default;
  156. }
  157. public bool SetInverseMass(uint playerId, float inverseMass)
  158. {
  159. ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
  160. if (exists)
  161. {
  162. rbes.physicsMass.InverseInertia = inverseMass;
  163. return true;
  164. }
  165. return false;
  166. }
  167. public float? GetLastPingTime(uint playerId, PlayerType type)
  168. {
  169. EGID egid = new EGID(playerId, PlayerGroupFromEnum(type));
  170. if (entitiesDB.Exists<PlayerNetworkStatsEntityStruct>(egid))
  171. {
  172. return entitiesDB.QueryEntity<PlayerNetworkStatsEntityStruct>(egid).lastPingTimeSinceLevelLoad;
  173. }
  174. return null;
  175. }
  176. public float GetInitialHealth(uint playerId)
  177. {
  178. ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
  179. if (exists)
  180. {
  181. return c.initialHealth;
  182. }
  183. return -1f;
  184. }
  185. public bool SetInitialHealth(uint playerId, float val)
  186. {
  187. ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
  188. if (exists)
  189. {
  190. c.initialHealth = val;
  191. return true;
  192. }
  193. return false;
  194. }
  195. public float GetCurrentHealth(uint playerId)
  196. {
  197. ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
  198. if (exists)
  199. {
  200. return c.currentHealth;
  201. }
  202. return -1f;
  203. }
  204. public bool SetCurrentHealth(uint playerId, float val)
  205. {
  206. ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
  207. if (exists)
  208. {
  209. c.currentHealth = val;
  210. return true;
  211. }
  212. return false;
  213. }
  214. public bool DamagePlayer(uint playerId, float amount)
  215. {
  216. Factory.BuildEntity<DamageEntityDescriptor>(
  217. new EGID(CharacterVulnerabilityExclusiveGroups.NextDamageEntityId, CharacterVulnerabilityExclusiveGroups.CharacterDamageExclusiveGroup)
  218. ).Init(new DamageEntityStruct
  219. {
  220. damage = amount,
  221. targetPlayerEntityId = playerId,
  222. });
  223. return true;
  224. }
  225. public bool GetDamageable(uint playerId)
  226. {
  227. ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
  228. if (exists)
  229. {
  230. return c.canTakeDamageStat;
  231. }
  232. return false;
  233. }
  234. public bool SetDamageable(uint playerId, bool val)
  235. {
  236. ref var ches = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
  237. if (exists)
  238. {
  239. ches.canTakeDamage = val;
  240. ches.canTakeDamage = val;
  241. return true;
  242. }
  243. return false;
  244. }
  245. public uint GetInitialLives(uint playerId)
  246. {
  247. ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
  248. if (exists)
  249. {
  250. return c.initialLives;
  251. }
  252. return uint.MaxValue;
  253. }
  254. public bool SetInitialLives(uint playerId, uint val)
  255. {
  256. ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
  257. if (exists)
  258. {
  259. c.initialLives = val;
  260. return true;
  261. }
  262. return false;
  263. }
  264. public uint GetCurrentLives(uint playerId)
  265. {
  266. ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
  267. if (exists)
  268. {
  269. return c.currentLives;
  270. }
  271. return uint.MaxValue;
  272. }
  273. public bool SetCurrentLives(uint playerId, uint val)
  274. {
  275. ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
  276. if (exists)
  277. {
  278. c.currentLives = val;
  279. return true;
  280. }
  281. return false;
  282. }
  283. public bool GetGameOverScreen(uint playerId)
  284. {
  285. ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
  286. if (exists)
  287. {
  288. return c.gameOverScreen;
  289. }
  290. return false;
  291. }
  292. public bool IsDead(uint playerId)
  293. {
  294. return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadGroup);
  295. }
  296. public int GetSelectedBlock(uint playerId)
  297. {
  298. ref var c = ref GetCharacterStruct<EquippedPartStruct>(playerId, out bool exists);
  299. if (exists)
  300. {
  301. return c.SelectedDBPartID;
  302. }
  303. return ushort.MaxValue;
  304. }
  305. public byte GetSelectedColor(uint playerId)
  306. {
  307. ref var c = ref GetCharacterStruct<EquippedColourStruct>(playerId, out bool exists);
  308. if (exists)
  309. {
  310. return c.indexInPalette;
  311. }
  312. return 255;
  313. }
  314. // reusable methods
  315. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  316. private ExclusiveGroup PlayerGroupFromEnum(PlayerType type)
  317. {
  318. return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
  319. }
  320. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  321. public ref T GetCharacterStruct<T>(uint playerId, out bool exists) where T : unmanaged, IEntityComponent
  322. {
  323. ExclusiveGroup[] characterGroups = CharacterExclusiveGroups.AllCharacters;
  324. for (int i = 0; i < characterGroups.Length; i++)
  325. {
  326. EGID egid = new EGID(playerId, characterGroups[i]);
  327. if (entitiesDB.Exists<T>(egid))
  328. {
  329. exists = true;
  330. return ref entitiesDB.QueryEntity<T>(egid);
  331. }
  332. }
  333. exists = false;
  334. T[] arr = new T[1];
  335. return ref arr[0]; //Return default value
  336. }
  337. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  338. public bool GetPlayerStruct<T>(uint playerId, out T s) where T : unmanaged, IEntityComponent
  339. {
  340. ExclusiveGroup[] playerGroups = PlayersExclusiveGroups.AllPlayers;
  341. for (int i = 0; i < playerGroups.Length; i++)
  342. {
  343. EGID egid = new EGID(playerId, playerGroups[i]);
  344. if (entitiesDB.Exists<T>(egid))
  345. {
  346. s = entitiesDB.QueryEntity<T>(egid);
  347. return true;
  348. }
  349. }
  350. s = default;
  351. return false;
  352. }
  353. public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f)
  354. {
  355. if (!entitiesDB.TryQueryMappedEntities<CharacterCameraRayCastEntityStruct>(
  356. CameraExclusiveGroups.CameraGroup, out var mapper))
  357. return null;
  358. mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast);
  359. float distance = maxDistance < 0
  360. ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast)
  361. : maxDistance;
  362. if (rayCast.hit && rayCast.distance <= distance)
  363. return rayCast.hitEgid;
  364. return null;
  365. }
  366. }
  367. }