using System; using RobocraftX.Character; using RobocraftX.Character.Movement; using Unity.Mathematics; using RobocraftX.Common; using RobocraftX.Common.Players; using RobocraftX.Physics; using Svelto.ECS; using Techblox.Camera; using Techblox.FlyCam; using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Players; using TechbloxModdingAPI.Utility; using UnityEngine; namespace TechbloxModdingAPI { /// /// An in-game player character. Any Leo you see is a player. /// public class Player : IEquatable, IEquatable { // static functionality private static PlayerEngine playerEngine = new PlayerEngine(); private static Player localPlayer; /// /// Checks if the specified player exists. /// /// Whether the player exists. /// Player type. 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; } /// /// Checks if the specified player exists. /// /// Whether the player exists. /// The player's unique identifier. public static bool Exists(uint player) { return playerEngine.ExistsById(player); } /// /// The amount of Players in the current game. /// /// The count. public static uint Count() { return (uint) playerEngine.GetAllPlayerCount(); } /// /// Returns the current player belonging to this client. /// public static Player LocalPlayer { get { if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer()) localPlayer = new Player(PlayerType.Local); return localPlayer; } } /// /// Initializes a new instance of the class. /// /// The player's unique identifier. public Player(uint id) { this.Id = id; if (!Exists(id)) { throw new PlayerNotFoundException($"No player with id {id} exists"); } this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote; } /// /// Initializes a new instance of the class. /// /// The player type. Chooses the first available player matching the criteria. public Player(PlayerType player) { switch (player) { case PlayerType.Local: this.Id = playerEngine.GetLocalPlayer(); break; case PlayerType.Remote: this.Id = playerEngine.GetRemotePlayer(); break; } if (this.Id == uint.MaxValue) { throw new PlayerNotFoundException($"No player of {player} type exists"); } this.Type = player; } // object fields & properties /// /// The player's type. /// The player type is always relative to the current client, not the game host. /// /// The enumerated player type. public PlayerType Type { get; } /// /// The player's unique identifier. /// /// The identifier. public uint Id { get; } /// /// The player's current position. /// /// The position. public float3 Position { get => playerEngine.GetCharacterStruct(Id).Get().position; set => playerEngine.SetLocation(Id, value, false); } /// /// The player's current rotation. /// /// The rotation. public float3 Rotation { get => ((Quaternion) (GameState.IsBuildMode() ? playerEngine.GetCameraStruct(Id).Get().rotation : playerEngine.GetCharacterStruct(Id).Get().rotation)).eulerAngles; set => _ = GameState.IsBuildMode() ? playerEngine.GetCameraStruct(Id).Get().rotation = quaternion.Euler(value) : playerEngine.GetCharacterStruct(Id).Get().rotation = quaternion.Euler(value); } /// /// The player's current velocity. /// /// The velocity. public float3 Velocity { get => playerEngine.GetCharacterStruct(Id).Get().velocity; set => playerEngine.GetCharacterStruct(Id).Get().velocity = value; } /// /// The player's current angular velocity. /// /// The angular velocity. public float3 AngularVelocity { get => playerEngine.GetCharacterStruct(Id).Get().angularVelocity; set => playerEngine.GetCharacterStruct(Id).Get().angularVelocity = value; } /// /// The player's mass. /// /// The mass. public float Mass => 1f / playerEngine.GetCharacterStruct(Id).Get().physicsMass.InverseMass; private float _ping = -1f; /// /// The player's latest network ping time. /// /// The ping (s). public float Ping { get { var opt = playerEngine.GetPlayerStruct(Id, Type); if (opt) { _ping = opt.Get().lastPingTimeSinceLevelLoad ?? _ping; } return _ping; } } /// /// The player's initial health when entering Simulation (aka Time Running) mode. /// /// The initial health. public float InitialHealth { get { var opt = playerEngine.GetCharacterStruct(Id); return opt ? opt.Get().initialHealth : -1f; } set => playerEngine.GetCharacterStruct(Id).Get().initialHealth = value; } /// /// The player's current health in Simulation (aka Time Running) mode. /// /// The current health. public float CurrentHealth { get { var opt = playerEngine.GetCharacterStruct(Id); return opt ? opt.Get().currentHealth : -1f; } set => playerEngine.GetCharacterStruct(Id).Get().currentHealth = value; } /// /// Whether this is damageable. /// /// true if damageable; otherwise, false. public bool Damageable { get { var opt = playerEngine.GetCharacterStruct(Id); return opt.Get().canTakeDamageStat; } set { ref var healthStruct = ref playerEngine.GetCharacterStruct(Id).Get(); healthStruct.canTakeDamage = value; healthStruct.canTakeDamageStat = value; } } /// /// The player's lives when initially entering Simulation (aka Time Running) mode. /// /// The initial lives. public uint InitialLives { get { var opt = playerEngine.GetCharacterStruct(Id); return opt ? opt.Get().initialLives : uint.MaxValue; } set => playerEngine.GetCharacterStruct(Id).Get().initialLives = value; } /// /// The player's current lives in Simulation (aka Time Running) mode. /// /// The current lives. public uint CurrentLives { get { var opt = playerEngine.GetCharacterStruct(Id); return opt ? opt.Get().currentLives : uint.MaxValue; } set => playerEngine.GetCharacterStruct(Id).Get().currentLives = value; } /*/// /// Whether the Game Over screen is displayed for the player. /// /// true if game over; otherwise, false. public bool GameOver { get => playerEngine.GetGameOverScreen(Id); }*/ /// /// Whether the player is dead. /// If true, hopefully it was quick. /// /// true if dead; otherwise, false. public bool Dead { get => playerEngine.IsDead(Id); } /// /// The player's selected block ID in their hand. /// /// The selected block. public BlockIDs SelectedBlock { get { var optstruct = playerEngine.GetCharacterStruct(Id); return optstruct ? (BlockIDs) optstruct.Get().SelectedDBPartID : BlockIDs.Invalid; } } /// /// The player's selected block color in their hand. /// /// The selected block's color. public BlockColor SelectedColor { get { var optstruct = playerEngine.GetCharacterStruct(Id); return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default; } } /// /// The player's selected block colour in their hand. /// /// The selected block's colour. public BlockColor SelectedColour { get { var optstruct = playerEngine.GetCharacterStruct(Id); return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default; } } /// /// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage. /// public Blueprint SelectedBlueprint { get { var lbiso = playerEngine.GetPlayerStruct(Id, Type); return lbiso ? new Blueprint(lbiso.Get().selectedBlueprintId) : null; } set => BlockGroup._engine.SelectBlueprint(value?.Id ?? uint.MaxValue); } /// /// The player's mode in time stopped mode, determining what they place. /// public PlayerBuildingMode BuildingMode => (PlayerBuildingMode) playerEngine .GetCharacterStruct(Id).Get().timeStoppedContext; /// /// Whether the player is sprinting. /// public bool Sprinting { get => GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().sprinting : playerEngine.GetCharacterStruct(Id).Get().isSprinting; set => _ = GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().sprinting = value : playerEngine.GetCharacterStruct(Id).Get().isSprinting = value; } /// /// Movement speed setting. Build mode (camera) and simulation mode settings are separate. /// public float SpeedSetting { get => GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().speed : playerEngine.GetCharacterStruct(Id).Get().moveSpeed; set => _ = GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().speed = value : playerEngine.GetCharacterStruct(Id).Get().moveSpeed = value; } /// /// The multiplier setting to use when sprinting. Build mode (camera) and simulation mode settings are separate. /// public float SpeedSprintMultiplierSetting { get => GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().speedSprintMultiplier : playerEngine.GetCharacterStruct(Id).Get().sprintSpeedMultiplier; set => _ = GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().speedSprintMultiplier = value : playerEngine.GetCharacterStruct(Id).Get().sprintSpeedMultiplier = value; } /// /// The acceleration setting of the player. Build mode (camera) and simulation mode settings are separate. /// public float AccelerationSetting { get => GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().acceleration : playerEngine.GetCharacterStruct(Id).Get().acceleration; set => _ = GameState.IsBuildMode() ? playerEngine.GetCharacterStruct(Id).Get().acceleration = value : playerEngine.GetCharacterStruct(Id).Get().acceleration = value; } // object methods /// /// Teleport the player to the specified coordinates. /// /// The x coordinate. /// The y coordinate. /// The z coordinate. /// If set to true teleport relative to the player's current position. /// If set to true exit any seat the player is in. 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); } /// /// Returns the block the player is currently looking at in build mode. /// /// The maximum distance from the player (default is the player's building reach) /// The block or null if not found public Block GetBlockLookedAt(float maxDistance = -1f) { var egid = playerEngine.GetThingLookedAt(Id, maxDistance); return egid != EGID.Empty && egid.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP ? Block.New(egid) : null; } /// /// Returns the rigid body the player is currently looking at during simulation. /// /// The maximum distance from the player (default is the player's building reach) /// The body or null if not found public SimBody GetSimBodyLookedAt(float maxDistance = -1f) { var egid = playerEngine.GetThingLookedAt(Id, maxDistance); return egid != EGID.Empty && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP ? new SimBody(egid) : null; } /// /// Returns the blocks that are in the player's current selection. /// /// An array of blocks or an empty array 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; } // internal methods internal static void Init() { Utility.GameEngineManager.AddGameEngine(playerEngine); } } }