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.

232 lines
8.0KB

  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.CR.MachineEditing.BoxSelect;
  8. using RobocraftX.Physics;
  9. using RobocraftX.Blocks.Ghost;
  10. using Gamecraft.GUI.HUDFeedbackBlocks;
  11. using RobocraftX.Blocks;
  12. using RobocraftX.PilotSeat;
  13. using Svelto.ECS;
  14. using Techblox.Camera;
  15. using Unity.Mathematics;
  16. using Svelto.ECS.DataStructures;
  17. using Svelto.ECS.EntityStructs;
  18. using Techblox.BuildingDrone;
  19. using TechbloxModdingAPI.Engines;
  20. using TechbloxModdingAPI.Utility;
  21. namespace TechbloxModdingAPI.Players
  22. {
  23. internal class PlayerEngine : IFunEngine
  24. {
  25. public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine";
  26. public EntitiesDB entitiesDB { set; private get; }
  27. public bool isRemovable => false;
  28. public IEntityFunctions Functions { get; set; }
  29. private bool isReady = false;
  30. public void Dispose()
  31. {
  32. isReady = false;
  33. }
  34. public void Ready()
  35. {
  36. isReady = true;
  37. }
  38. public uint GetLocalPlayer()
  39. {
  40. if (!isReady) return uint.MaxValue;
  41. var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToBuffer();
  42. if (localPlayers.count > 0)
  43. {
  44. return localPlayers.buffer[0].ID.entityID;
  45. }
  46. return uint.MaxValue;
  47. }
  48. public uint GetRemotePlayer()
  49. {
  50. if (!isReady) return uint.MaxValue;
  51. var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToBuffer();
  52. if (localPlayers.count > 0)
  53. {
  54. return localPlayers.buffer[0].ID.entityID;
  55. }
  56. return uint.MaxValue;
  57. }
  58. public long GetAllPlayerCount()
  59. {
  60. if (entitiesDB == null) return 0;
  61. long count = 0;
  62. foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
  63. {
  64. count += entitiesDB.Count<PlayerIDStruct>(eg);
  65. }
  66. return count;
  67. }
  68. public long GetLocalPlayerCount()
  69. {
  70. if (entitiesDB == null) return 0;
  71. return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
  72. }
  73. public long GetRemotePlayerCount()
  74. {
  75. if (entitiesDB == null) return 0;
  76. return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
  77. }
  78. public bool ExistsById(uint playerId)
  79. {
  80. if (entitiesDB == null) return false;
  81. return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
  82. || entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
  83. }
  84. public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
  85. {
  86. if (entitiesDB == null) return false;
  87. var rbesOpt = GetCharacterStruct<RigidBodyEntityStruct>(playerId, out var group);
  88. if (!rbesOpt)
  89. return false;
  90. if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
  91. {
  92. ExitSeat(playerId);
  93. }
  94. rbesOpt.Get().position = location;
  95. return true;
  96. }
  97. public bool GetGameOverScreen(uint playerId)
  98. {
  99. if (entitiesDB == null) return false;
  100. ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity<HudActivatedBlocksEntityStruct>(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid);
  101. NativeDynamicArrayCast<EGID> nativeDynamicArrayCast = new NativeDynamicArrayCast<EGID>(habes.activatedBlocksOrdered);
  102. return nativeDynamicArrayCast.count > 0;
  103. }
  104. public bool IsDead(uint playerId)
  105. {
  106. if (entitiesDB == null) return true;
  107. return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
  108. }
  109. // reusable methods
  110. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  111. public OptionalRef<T> GetCharacterStruct<T>(uint playerId) where T : unmanaged, IEntityComponent =>
  112. GetCharacterStruct<T>(playerId, out _);
  113. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  114. public OptionalRef<T> GetCharacterStruct<T>(uint playerId, out ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
  115. {
  116. group = default;
  117. if (GameState.IsBuildMode())
  118. return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, LocalBuildingDrone.BuildGroup));
  119. var characterGroups = CharacterExclusiveGroups.AllCharacters;
  120. for (int i = 0; i < characterGroups.count; i++)
  121. {
  122. EGID egid = new EGID(playerId, characterGroups[i]);
  123. var opt = entitiesDB.QueryEntityOptional<T>(egid);
  124. if (opt.Exists)
  125. {
  126. group = characterGroups[i];
  127. return opt;
  128. }
  129. }
  130. return new OptionalRef<T>();
  131. }
  132. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  133. public OptionalRef<T> GetPlayerStruct<T>(uint playerId, PlayerType type) where T : unmanaged, IEntityComponent
  134. {
  135. var playerGroup = type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
  136. EGID egid = new EGID(playerId, playerGroup);
  137. return entitiesDB.QueryEntityOptional<T>(egid);
  138. }
  139. public OptionalRef<T> GetCameraStruct<T>(uint playerId) where T : unmanaged, IEntityComponent
  140. {
  141. return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup));
  142. }
  143. public EGID GetThingLookedAt(uint playerId, float maxDistance = -1f)
  144. {
  145. var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId);
  146. if (!opt) return default;
  147. PhysicCameraRayCastEntityStruct rayCast = opt;
  148. float distance = maxDistance < 0
  149. ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast,
  150. GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
  151. : maxDistance;
  152. if (rayCast.hit && rayCast.distance <= distance)
  153. return rayCast.hitEgid; //May be EGID.Empty (default)
  154. return default;
  155. }
  156. public unsafe Block[] GetSelectedBlocks(uint playerid)
  157. {
  158. if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
  159. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
  160. return Array.Empty<Block>();
  161. var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
  162. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
  163. var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
  164. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
  165. if (!state.active) return Array.Empty<Block>();
  166. var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
  167. var ret = new Block[blocks.count];
  168. for (int j = 0; j < blocks.count; j++)
  169. {
  170. var egid = pointer[j];
  171. ret[j] = Block.New(egid);
  172. }
  173. return ret;
  174. }
  175. public void EnterSeat(uint playerId, EGID seatId)
  176. {
  177. PilotSeatGroupUtils.SwapTagTo<OCCUPIED_TAG>(Functions, seatId);
  178. var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(playerId, out var group);
  179. if (!opt) return;
  180. ref CharacterPilotSeatEntityStruct charSeat = ref opt.Get();
  181. var charId = new EGID(playerId, group);
  182. charSeat.pilotSeatEntity = entitiesDB.GetEntityReference(seatId);
  183. charSeat.entryPositionOffset =
  184. entitiesDB.QueryEntity<PositionEntityStruct>(charId).position -
  185. entitiesDB.QueryEntity<PositionEntityStruct>(seatId).position;
  186. ref var seat = ref entitiesDB.QueryEntity<PilotSeatEntityStruct>(seatId);
  187. seat.occupyingCharacter = entitiesDB.GetEntityReference(charId);
  188. charSeat.followCam = entitiesDB.QueryEntity<SeatFollowCamComponent>(seatId).followCam;
  189. Functions.SwapEntityGroup<CharacterEntityDescriptor>(charId, CharacterExclusiveGroups.InPilotSeatGroup);
  190. }
  191. public void ExitSeat(uint playerId)
  192. {
  193. EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup);
  194. var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid);
  195. if (!opt) return;
  196. opt.Get().instantExit = true;
  197. entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
  198. }
  199. }
  200. }