|
- using System;
- using Gamecraft.Wires;
- using RobocraftX.Character;
- using RobocraftX.Character.Movement;
- using Unity.Mathematics;
- using RobocraftX.Common;
- using RobocraftX.Common.Players;
- using RobocraftX.GUI.Wires;
- using RobocraftX.Physics;
- using Svelto.ECS;
- using Techblox.BuildingDrone;
- using Techblox.Camera;
- using TechbloxModdingAPI.Blocks;
- using TechbloxModdingAPI.Players;
- using TechbloxModdingAPI.Utility;
- using UnityEngine;
-
- namespace TechbloxModdingAPI
- {
- /// <summary>
- /// An in-game player character. Any Leo you see is a player.
- /// </summary>
- public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
- {
- // static functionality
- private static readonly PlayerEngine playerEngine = new PlayerEngine();
- private static readonly PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine();
- private static Player localPlayer;
-
- /// <summary>
- /// Checks if the specified player exists.
- /// </summary>
- /// <returns>Whether the player exists.</returns>
- /// <param name="player">Player type.</param>
- public static bool Exists(PlayerType player)
- {
- switch (player)
- {
- case PlayerType.Remote:
- return playerEngine.GetRemotePlayer() != uint.MaxValue;
- case PlayerType.Local:
- return playerEngine.GetLocalPlayer() != uint.MaxValue;
- }
- return false;
- }
-
- /// <summary>
- /// Checks if the specified player exists.
- /// </summary>
- /// <returns>Whether the player exists.</returns>
- /// <param name="player">The player's unique identifier.</param>
- public static bool Exists(uint player)
- {
- return playerEngine.ExistsById(player);
- }
-
- /// <summary>
- /// The amount of Players in the current game.
- /// </summary>
- /// <returns>The count.</returns>
- public static uint Count()
- {
- return (uint) playerEngine.GetAllPlayerCount();
- }
-
- /// <summary>
- /// Returns the current player belonging to this client. It will be different after entering/leaving simulation.
- /// </summary>
- public static Player LocalPlayer
- {
- get
- {
- if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer())
- localPlayer = GetInstance(playerEngine.GetLocalPlayer());
- return localPlayer;
- }
- }
-
- internal static Player GetInstance(uint id)
- {
- return EcsObjectBase.GetInstance(new EGID(id, CharacterExclusiveGroups.OnFootGroup),
- e => new Player(e.entityID));
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
- /// </summary>
- /// <param name="id">The player's unique identifier.</param>
- public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup))
- {
- this.Id = id;
- if (!Exists(id))
- {
- throw new PlayerNotFoundException($"No player with id {id} exists");
- }
- this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
- /// </summary>
- /// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
- public Player(PlayerType player) : base(ecs =>
- {
- uint id;
- switch (player)
- {
- case PlayerType.Local:
- id = playerEngine.GetLocalPlayer();
- break;
- case PlayerType.Remote:
- id = playerEngine.GetRemotePlayer();
- break;
- default:
- id = uint.MaxValue;
- break;
- }
-
- if (id == uint.MaxValue)
- {
- throw new PlayerNotFoundException($"No player of {player} type exists");
- }
-
- return new EGID(id, CharacterExclusiveGroups.OnFootGroup);
- })
- {
- this.Type = player;
- Id = base.Id.entityID;
- }
-
- // object fields & properties
-
- /// <summary>
- /// The player's type.
- /// The player type is always relative to the current client, not the game host.
- /// </summary>
- /// <value>The enumerated player type.</value>
- public PlayerType Type { get; }
-
- /// <summary>
- /// The player's unique identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public new uint Id { get; }
-
- /// <summary>
- /// The player's current position.
- /// </summary>
- /// <value>The position.</value>
- public float3 Position
- {
- get => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().position;
- set => playerEngine.SetLocation(Id, value, false);
- }
-
- /// <summary>
- /// The player's current rotation.
- /// </summary>
- /// <value>The rotation.</value>
- public float3 Rotation
- {
- get => ((Quaternion) (GameState.IsBuildMode()
- ? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation
- : playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation)).eulerAngles;
- set => _ = GameState.IsBuildMode()
- ? playerEngine.GetCameraStruct<CameraEntityStruct>(Id).Get().rotation = quaternion.Euler(value)
- : playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().rotation = quaternion.Euler(value);
- }
-
- /// <summary>
- /// The player's current velocity.
- /// </summary>
- /// <value>The velocity.</value>
- public float3 Velocity
- {
- get => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().velocity;
- set => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().velocity = value;
- }
-
- /// <summary>
- /// The player's current angular velocity.
- /// </summary>
- /// <value>The angular velocity.</value>
- public float3 AngularVelocity
- {
- get => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().angularVelocity;
- set => playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().angularVelocity = value;
- }
-
- /// <summary>
- /// The player's mass.
- /// </summary>
- /// <value>The mass.</value>
- public float Mass =>
- 1f / playerEngine.GetCharacterStruct<RigidBodyEntityStruct>(Id).Get().physicsMass.InverseMass;
-
- /// <summary>
- /// The player's latest network ping time.
- /// </summary>
- /// <value>The ping (s).</value>
- public float Ping
- {
- get
- {
- return playerEngine.GetPing() / 1000f;
- }
- }
-
- /// <summary>
- /// The player's initial health when entering Simulation (aka Time Running) mode.
- /// </summary>
- /// <value>The initial health.</value>
- public float InitialHealth
- {
- get
- {
- var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
- return opt ? opt.Get().initialHealth : -1f;
- }
-
- set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().initialHealth = value;
- }
-
- /// <summary>
- /// The player's current health in Simulation (aka Time Running) mode.
- /// </summary>
- /// <value>The current health.</value>
- public float CurrentHealth
- {
- get
- {
- var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
- return opt ? opt.Get().currentHealth : -1f;
- }
-
- set => playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get().currentHealth = value;
- }
-
- /// <summary>
- /// Whether this <see cref="T:TechbloxModdingAPI.Player"/> is damageable.
- /// </summary>
- /// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
- public bool Damageable
- {
- get
- {
- var opt = playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id);
- return opt.Get().canTakeDamageStat;
- }
-
- set
- {
- ref var healthStruct = ref playerEngine.GetCharacterStruct<CharacterHealthEntityStruct>(Id).Get();
- healthStruct.canTakeDamage = value;
- healthStruct.canTakeDamageStat = value;
- }
- }
-
- /// <summary>
- /// The player's lives when initially entering Simulation (aka Time Running) mode.
- /// </summary>
- /// <value>The initial lives.</value>
- public uint InitialLives
- {
- get
- {
- var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
- return opt ? opt.Get().initialLives : uint.MaxValue;
- }
-
- set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().initialLives = value;
- }
-
- /// <summary>
- /// The player's current lives in Simulation (aka Time Running) mode.
- /// </summary>
- /// <value>The current lives.</value>
- public uint CurrentLives
- {
- get
- {
- var opt = playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id);
- return opt ? opt.Get().currentLives : uint.MaxValue;
- }
-
- set => playerEngine.GetCharacterStruct<CharacterLivesEntityComponent>(Id).Get().currentLives = value;
- }
-
- /*/// <summary>
- /// Whether the Game Over screen is displayed for the player.
- /// </summary>
- /// <value><c>true</c> if game over; otherwise, <c>false</c>.</value>
- public bool GameOver
- {
- get => playerEngine.GetGameOverScreen(Id);
- }*/
-
- /// <summary>
- /// Whether the player is dead.
- /// If <c>true</c>, hopefully it was quick.
- /// </summary>
- /// <value><c>true</c> if dead; otherwise, <c>false</c>.</value>
- public bool Dead
- {
- get => playerEngine.IsDead(Id);
- }
-
- /// <summary>
- /// The player's selected block ID in their hand.
- /// </summary>
- /// <value>The selected block.</value>
- public BlockIDs SelectedBlock
- {
- get
- {
- var optstruct = playerEngine.GetCharacterStruct<EquippedPartStruct>(Id);
- return optstruct ? (BlockIDs) optstruct.Get().selectedDBPartID : BlockIDs.Invalid;
- }
- }
-
- /// <summary>
- /// The player's selected block color in their hand.
- /// </summary>
- /// <value>The selected block's color.</value>
- public BlockColor SelectedColor
- {
- get
- {
- var optstruct = playerEngine.GetCharacterStruct<EquippedColourStruct>(Id);
- return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default;
- }
- }
-
- /// <summary>
- /// The player's selected block colour in their hand.
- /// </summary>
- /// <value>The selected block's colour.</value>
- public BlockColor SelectedColour
- {
- get
- {
- var optstruct = playerEngine.GetCharacterStruct<EquippedColourStruct>(Id);
- return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default;
- }
- }
-
- /// <summary>
- /// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage.
- /// </summary>
- public Blueprint SelectedBlueprint
- {
- get
- {
- var lbiso = playerEngine.GetPlayerStruct<LocalBlueprintInputStruct>(Id, Type);
- return lbiso ? new Blueprint(lbiso.Get().selectedBlueprintId) : null;
- }
- set => BlockGroup._engine.SelectBlueprint(value?.Id ?? uint.MaxValue);
- }
-
- /// <summary>
- /// The player's mode in time stopped mode, determining what they place.
- /// </summary>
- public PlayerBuildingMode BuildingMode => (PlayerBuildingMode)Math.Log((double)playerEngine
- .GetCharacterStruct<TimeStoppedModeComponent>(Id).Get().timeStoppedContext, 2); // It's a bit field in game now
-
- public PlayerState State =>
- playerEngine.GetCharacterStruct<CharacterTagEntityStruct>(Id).Get().ID.groupID switch
- {
- var group when group == CharacterExclusiveGroups.MachineSpawningGroup => PlayerState.HoldingMachine,
- var group when group == CharacterExclusiveGroups.OnFootGroup => PlayerState.OnFoot,
- var group when group == CharacterExclusiveGroups.InPilotSeatGroup => PlayerState.InSeat,
- _ => throw new ArgumentOutOfRangeException("", "Unknown player state")
- };
-
- /// <summary>
- /// Whether the player is sprinting.
- /// </summary>
- public bool Sprinting
- {
- get => GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting
- : playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting;
- set => _ = GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementComponent>(Id).Get().sprinting = value
- : playerEngine.GetCharacterStruct<CharacterMovementEntityStruct>(Id).Get().isSprinting = value;
- }
-
- /// <summary>
- /// Movement speed setting. Build mode (camera) and simulation mode settings are separate.
- /// </summary>
- public float SpeedSetting
- {
- get => GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed
- : playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed;
- set => _ = GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speed = value
- : playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().moveSpeed = value;
- }
-
- /// <summary>
- /// The multiplier setting to use when sprinting. Build mode (camera) and simulation mode settings are separate.
- /// </summary>
- public float SpeedSprintMultiplierSetting
- {
- get => GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier
- : playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier;
- set => _ = GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().speedSprintMultiplier = value
- : playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().sprintSpeedMultiplier = value;
- }
-
- /// <summary>
- /// The acceleration setting of the player. Build mode (camera) and simulation mode settings are separate.
- /// </summary>
- public float AccelerationSetting
- {
- get => GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration
- : playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration;
- set => _ = GameState.IsBuildMode()
- ? playerEngine.GetCharacterStruct<BuildingDroneMovementSettingsComponent>(Id).Get().acceleration = value
- : playerEngine.GetCharacterStruct<CharacterMovementSettingsEntityStruct>(Id).Get().acceleration = value;
- }
-
- // object methods
-
- /// <summary>
- /// Teleport the player to the specified coordinates.
- /// </summary>
- /// <param name="x">The x coordinate.</param>
- /// <param name="y">The y coordinate.</param>
- /// <param name="z">The z coordinate.</param>
- /// <param name="relative">If set to <c>true</c> teleport relative to the player's current position.</param>
- /// <param name="exitSeat">If set to <c>true</c> exit any seat the player is in.</param>
- public void Teleport(float x, float y, float z, bool relative = true, bool exitSeat = true)
- {
- float3 location = new float3(x, y, z);
- if (relative)
- {
- location += Position;
- }
- playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
- }
-
- /// <summary>
- /// Enter the given seat.
- /// </summary>
- /// <param name="seat">The seat to enter.</param>
- public void EnterSeat(Seat seat)
- {
- playerEngine.EnterSeat(Id, seat.Id);
- }
-
- /// <summary>
- /// Exit the seat the player is currently in.
- /// </summary>
- public void ExitSeat()
- {
- playerEngine.ExitSeat(Id);
- }
-
- /// <summary>
- /// Spawn the machine the player is holding in time running mode.
- /// </summary>
- public bool SpawnMachine()
- {
- return playerEngine.SpawnMachine(Id);
- }
-
- /// <summary>
- /// Despawn the player's machine in time running mode and place it in their hand.
- /// </summary>
- public bool DespawnMachine()
- {
- return playerEngine.DespawnMachine(Id);
- }
-
- /// <summary>
- /// Returns the block the player is currently looking at in build mode.
- /// </summary>
- /// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
- /// <returns>The block or null if not found</returns>
- public Block GetBlockLookedAt(float maxDistance = -1f)
- {
- var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
- return egid != default && egid.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP
- && egid.groupID != WiresGUIExclusiveGroups.WireGroup
- ? Block.New(egid)
- : null;
- }
-
- /// <summary>
- /// Returns the rigid body the player is currently looking at during simulation.
- /// </summary>
- /// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
- /// <returns>The body or null if not found</returns>
- public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
- {
- var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
- return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
- ? EcsObjectBase.GetInstance(egid, e => new SimBody(e))
- : null;
- }
-
- /// <summary>
- /// Returns the wire the player is currently looking at in build mode.
- /// </summary>
- /// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
- /// <returns>The wire or null if not found</returns>
- public Wire GetWireLookedAt(float maxDistance = -1f)
- {
- var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
- return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup
- ? EcsObjectBase.GetInstance(new EGID(egid.entityID, BuildModeWiresGroups.WiresGroup.Group),
- e => new Wire(e))
- : null;
- }
-
- /// <summary>
- /// Returns the blocks that are in the player's current selection.
- /// </summary>
- /// <returns>An array of blocks or an empty array</returns>
- public Block[] GetSelectedBlocks()
- {
- return playerEngine.GetSelectedBlocks(Id);
- }
-
- public bool Equals(Player other)
- {
- if (ReferenceEquals(null, other)) return false;
- if (ReferenceEquals(this, other)) return true;
- return Id == other.Id;
- }
-
- public bool Equals(EGID other)
- {
- return Id == other.entityID && other.groupID == (Type == PlayerType.Local
- ? PlayersExclusiveGroups.LocalPlayers
- : PlayersExclusiveGroups.RemotePlayers);
- }
-
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((Player) obj);
- }
-
- public override int GetHashCode()
- {
- return (int) Id;
- }
-
- public override string ToString()
- {
- return $"{nameof(Type)}: {Type}, {nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(Mass)}: {Mass}";
- }
-
- // internal methods
-
- internal static void Init()
- {
- Utility.GameEngineManager.AddGameEngine(playerEngine);
- Utility.GameEngineManager.AddGameEngine(playerEventsEngine);
- }
- }
- }
|