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.

496 lines
17KB

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