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.

452 lines
13KB

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