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.

443 lines
15KB

  1. using System;
  2. using RobocraftX.Character;
  3. using Unity.Mathematics;
  4. using RobocraftX.Common;
  5. using RobocraftX.Common.Players;
  6. using RobocraftX.Physics;
  7. using Svelto.ECS;
  8. using Techblox.Camera;
  9. using Techblox.FlyCam;
  10. using TechbloxModdingAPI.Blocks;
  11. using TechbloxModdingAPI.Players;
  12. using TechbloxModdingAPI.Utility;
  13. using UnityEngine;
  14. namespace TechbloxModdingAPI
  15. {
  16. /// <summary>
  17. /// An in-game player character. Any Leo you see is a player.
  18. /// </summary>
  19. public partial class Player : IEquatable<Player>, IEquatable<EGID>
  20. {
  21. // static functionality
  22. private static PlayerEngine playerEngine = new PlayerEngine();
  23. private static Player localPlayer;
  24. /// <summary>
  25. /// Checks if the specified player exists.
  26. /// </summary>
  27. /// <returns>Whether the player exists.</returns>
  28. /// <param name="player">Player type.</param>
  29. public static bool Exists(PlayerType player)
  30. {
  31. switch (player)
  32. {
  33. case PlayerType.Remote:
  34. return playerEngine.GetRemotePlayer() != uint.MaxValue;
  35. case PlayerType.Local:
  36. return playerEngine.GetLocalPlayer() != uint.MaxValue;
  37. }
  38. return false;
  39. }
  40. /// <summary>
  41. /// Checks if the specified player exists.
  42. /// </summary>
  43. /// <returns>Whether the player exists.</returns>
  44. /// <param name="player">The player's unique identifier.</param>
  45. public static bool Exists(uint player)
  46. {
  47. return playerEngine.ExistsById(player);
  48. }
  49. /// <summary>
  50. /// The amount of Players in the current game.
  51. /// </summary>
  52. /// <returns>The count.</returns>
  53. public static uint Count()
  54. {
  55. return (uint) playerEngine.GetAllPlayerCount();
  56. }
  57. /// <summary>
  58. /// Returns the current player belonging to this client.
  59. /// </summary>
  60. public static Player LocalPlayer
  61. {
  62. get
  63. {
  64. if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer())
  65. localPlayer = new Player(PlayerType.Local);
  66. return localPlayer;
  67. }
  68. }
  69. /// <summary>
  70. /// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
  71. /// </summary>
  72. /// <param name="id">The player's unique identifier.</param>
  73. public Player(uint id)
  74. {
  75. this.Id = id;
  76. if (!Exists(id))
  77. {
  78. throw new PlayerNotFoundException($"No player with id {id} exists");
  79. }
  80. this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
  81. }
  82. /// <summary>
  83. /// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
  84. /// </summary>
  85. /// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
  86. public Player(PlayerType player)
  87. {
  88. switch (player)
  89. {
  90. case PlayerType.Local:
  91. this.Id = playerEngine.GetLocalPlayer();
  92. break;
  93. case PlayerType.Remote:
  94. this.Id = playerEngine.GetRemotePlayer();
  95. break;
  96. }
  97. if (this.Id == uint.MaxValue)
  98. {
  99. throw new PlayerNotFoundException($"No player of {player} type exists");
  100. }
  101. this.Type = player;
  102. }
  103. // object fields & properties
  104. /// <summary>
  105. /// The player's type.
  106. /// The player type is always relative to the current client, not the game host.
  107. /// </summary>
  108. /// <value>The enumerated player type.</value>
  109. public PlayerType Type { get; }
  110. /// <summary>
  111. /// The player's unique identifier.
  112. /// </summary>
  113. /// <value>The identifier.</value>
  114. public uint Id { get; }
  115. /// <summary>
  116. /// The player's current position.
  117. /// </summary>
  118. /// <value>The position.</value>
  119. public float3 Position
  120. {
  121. get => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().position;
  122. set => playerEngine.SetLocation(Id, value, false);
  123. }
  124. /// <summary>
  125. /// The player's current rotation.
  126. /// </summary>
  127. /// <value>The rotation.</value>
  128. public float3 Rotation
  129. {
  130. get => ((Quaternion) (GameState.IsBuildMode()
  131. ? playerEngine.GetCameraStruct<CharacterCameraEntityStruct>(Id).Get().rotation
  132. : playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
  133. set => _ = GameState.IsBuildMode()
  134. ? playerEngine.GetCameraStruct<CharacterCameraEntityStruct>(Id).Get().rotation = quaternion.Euler(value)
  135. : playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation = quaternion.Euler(value);
  136. }
  137. /// <summary>
  138. /// The player's current velocity.
  139. /// </summary>
  140. /// <value>The velocity.</value>
  141. public float3 Velocity
  142. {
  143. get => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().velocity;
  144. set => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().velocity = value;
  145. }
  146. /// <summary>
  147. /// The player's current angular velocity.
  148. /// </summary>
  149. /// <value>The angular velocity.</value>
  150. public float3 AngularVelocity
  151. {
  152. get => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().angularVelocity;
  153. set => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().angularVelocity = value;
  154. }
  155. /// <summary>
  156. /// The player's mass.
  157. /// </summary>
  158. /// <value>The mass.</value>
  159. public float Mass =>
  160. 1f / playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().physicsMass.InverseMass;
  161. private float _ping = -1f;
  162. /// <summary>
  163. /// The player's latest network ping time.
  164. /// </summary>
  165. /// <value>The ping (s).</value>
  166. public float Ping
  167. {
  168. get
  169. {
  170. var opt = playerEngine.GetPlayerStruct<PlayerNetworkStatsEntityStruct>(Id, Type);
  171. if (opt)
  172. {
  173. _ping = opt.Get().lastPingTimeSinceLevelLoad ?? _ping;
  174. }
  175. return _ping;
  176. }
  177. }
  178. /// <summary>
  179. /// The player's initial health when entering Simulation (aka Time Running) mode.
  180. /// </summary>
  181. /// <value>The initial health.</value>
  182. public float InitialHealth
  183. {
  184. get
  185. {
  186. var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
  187. return opt ? opt.Get().initialHealth : -1f;
  188. }
  189. set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().initialHealth = value;
  190. }
  191. /// <summary>
  192. /// The player's current health in Simulation (aka Time Running) mode.
  193. /// </summary>
  194. /// <value>The current health.</value>
  195. public float CurrentHealth
  196. {
  197. get
  198. {
  199. var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
  200. return opt ? opt.Get().currentHealth : -1f;
  201. }
  202. set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().currentHealth = value;
  203. }
  204. /// <summary>
  205. /// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable.
  206. /// </summary>
  207. /// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
  208. public bool Damageable
  209. {
  210. get
  211. {
  212. var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
  213. return opt.Get().canTakeDamageStat;
  214. }
  215. set
  216. {
  217. ref var healthStruct = ref playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get();
  218. healthStruct.canTakeDamage = value;
  219. healthStruct.canTakeDamageStat = value;
  220. }
  221. }
  222. /// <summary>
  223. /// The player's lives when initially entering Simulation (aka Time Running) mode.
  224. /// </summary>
  225. /// <value>The initial lives.</value>
  226. public uint InitialLives
  227. {
  228. get
  229. {
  230. var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
  231. return opt ? opt.Get().initialLives : uint.MaxValue;
  232. }
  233. set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().initialLives = value;
  234. }
  235. /// <summary>
  236. /// The player's current lives in Simulation (aka Time Running) mode.
  237. /// </summary>
  238. /// <value>The current lives.</value>
  239. public uint CurrentLives
  240. {
  241. get
  242. {
  243. var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
  244. return opt ? opt.Get().currentLives : uint.MaxValue;
  245. }
  246. set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().currentLives = value;
  247. }
  248. /*/// <summary>
  249. /// Whether the Game Over screen is displayed for the player.
  250. /// </summary>
  251. /// <value><c>true</c> if game over; otherwise, <c>false</c>.</value>
  252. public bool GameOver
  253. {
  254. get => playerEngine.GetGameOverScreen(Id);
  255. }*/
  256. /// <summary>
  257. /// Whether the player is dead.
  258. /// If <c>true</c>, hopefully it was quick.
  259. /// </summary>
  260. /// <value><c>true</c> if dead; otherwise, <c>false</c>.</value>
  261. public bool Dead
  262. {
  263. get => playerEngine.IsDead(Id);
  264. }
  265. /// <summary>
  266. /// The player's selected block ID in their hand.
  267. /// </summary>
  268. /// <value>The selected block.</value>
  269. public BlockIDs SelectedBlock
  270. {
  271. get
  272. {
  273. var optstruct = playerEngine.GetCharacterStruct<EquippedPartStruct>(Id);
  274. return optstruct ? (BlockIDs) optstruct.Get().SelectedDBPartID : BlockIDs.Invalid;
  275. }
  276. }
  277. /// <summary>
  278. /// The player's selected block color in their hand.
  279. /// </summary>
  280. /// <value>The selected block's color.</value>
  281. public BlockColor SelectedColor
  282. {
  283. get
  284. {
  285. var optstruct = playerEngine.GetCharacterStruct<EquippedColourStruct>(Id);
  286. return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default;
  287. }
  288. }
  289. /// <summary>
  290. /// The player's selected block colour in their hand.
  291. /// </summary>
  292. /// <value>The selected block's colour.</value>
  293. public BlockColor SelectedColour
  294. {
  295. get
  296. {
  297. var optstruct = playerEngine.GetCharacterStruct<EquippedColourStruct>(Id);
  298. return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default;
  299. }
  300. }
  301. /// <summary>
  302. /// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage.
  303. /// </summary>
  304. public Blueprint SelectedBlueprint
  305. {
  306. get
  307. {
  308. var lbiso = playerEngine.GetPlayerStruct<LocalBlueprintInputStruct>(Id, Type);
  309. return lbiso ? new Blueprint(lbiso.Get().selectedBlueprintId) : null;
  310. }
  311. set => BlockGroup._engine.SelectBlueprint(value?.Id ?? uint.MaxValue);
  312. }
  313. /// <summary>
  314. /// The player's mode in time stopped mode, determining what they place.
  315. /// </summary>
  316. public PlayerBuildingMode BuildingMode => (PlayerBuildingMode) playerEngine
  317. .GetCharacterStruct<PlayerInputTimeStoppedContextStruct>(Id).Get().timeStoppedContext;
  318. // object methods
  319. /// <summary>
  320. /// Teleport the player to the specified coordinates.
  321. /// </summary>
  322. /// <param name="x">The x coordinate.</param>
  323. /// <param name="y">The y coordinate.</param>
  324. /// <param name="z">The z coordinate.</param>
  325. /// <param name="relative">If set to <c>true</c> teleport relative to the player's current position.</param>
  326. /// <param name="exitSeat">If set to <c>true</c> exit any seat the player is in.</param>
  327. public void Teleport(float x, float y, float z, bool relative = true, bool exitSeat = true)
  328. {
  329. float3 location = new float3(x, y, z);
  330. if (relative)
  331. {
  332. location += Position;
  333. }
  334. playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
  335. }
  336. /// <summary>
  337. /// Returns the block the player is currently looking at in build mode.
  338. /// </summary>
  339. /// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
  340. /// <returns>The block or null if not found</returns>
  341. public Block GetBlockLookedAt(float maxDistance = -1f)
  342. {
  343. var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
  344. return egid != EGID.Empty && egid.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP
  345. ? Block.New(egid)
  346. : null;
  347. }
  348. /// <summary>
  349. /// Returns the rigid body the player is currently looking at during simulation.
  350. /// </summary>
  351. /// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
  352. /// <returns>The body or null if not found</returns>
  353. public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
  354. {
  355. var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
  356. return egid != EGID.Empty && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
  357. ? new SimBody(egid)
  358. : null;
  359. }
  360. /// <summary>
  361. /// Returns the blocks that are in the player's current selection.
  362. /// </summary>
  363. /// <returns>An array of blocks or an empty array</returns>
  364. public Block[] GetSelectedBlocks()
  365. {
  366. return playerEngine.GetSelectedBlocks(Id);
  367. }
  368. public bool Equals(Player other)
  369. {
  370. if (ReferenceEquals(null, other)) return false;
  371. if (ReferenceEquals(this, other)) return true;
  372. return Id == other.Id;
  373. }
  374. public bool Equals(EGID other)
  375. {
  376. return Id == other.entityID && other.groupID == (Type == PlayerType.Local
  377. ? PlayersExclusiveGroups.LocalPlayers
  378. : PlayersExclusiveGroups.RemotePlayers);
  379. }
  380. public override bool Equals(object obj)
  381. {
  382. if (ReferenceEquals(null, obj)) return false;
  383. if (ReferenceEquals(this, obj)) return true;
  384. if (obj.GetType() != this.GetType()) return false;
  385. return Equals((Player) obj);
  386. }
  387. public override int GetHashCode()
  388. {
  389. return (int) Id;
  390. }
  391. // internal methods
  392. internal static void Init()
  393. {
  394. Utility.GameEngineManager.AddGameEngine(playerEngine);
  395. }
  396. }
  397. }