using System; using System.Runtime.CompilerServices; using RobocraftX.Character; using RobocraftX.Character.Movement; using RobocraftX.Common.Players; using RobocraftX.Common.Input; using RobocraftX.Physics; using RobocraftX.Blocks.Ghost; using RobocraftX.Character.Camera; using RobocraftX.Character.Factories; using Gamecraft.CharacterVulnerability; using Gamecraft.CharacterVulnerability.Entities; using Svelto.ECS; using Unity.Mathematics; using Unity.Physics; using GamecraftModdingAPI.Engines; namespace GamecraftModdingAPI.Players { internal class PlayerEngine : IApiEngine, IFactoryEngine { public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine"; public EntitiesDB entitiesDB { set; private get; } public bool isRemovable => false; public IEntityFactory Factory { set; private get; } private bool isReady = false; public void Dispose() { isReady = false; } public void Ready() { isReady = true; } public uint GetLocalPlayer() { if (!isReady) return uint.MaxValue; PlayerIDStruct[] localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers).ToFastAccess(out uint count); if (count > 0) { return localPlayers[0].ID.entityID; } return uint.MaxValue; } public uint GetRemotePlayer() { if (!isReady) return uint.MaxValue; PlayerIDStruct[] localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers).ToFastAccess(out uint count); if (count > 0) { return localPlayers[0].ID.entityID; } return uint.MaxValue; } public bool ExistsById(uint playerId) { PlayerIDStruct[] players = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers).ToFastAccess(out uint count); for (int i = 0; i < count; i++) { if (players[i].ID.entityID == playerId) { return true; } } players = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers).ToFastAccess(out count); for (int i = 0; i < count; i++) { if (players[i].ID.entityID == playerId) { return true; } } return false; } public float3 GetLocation(uint playerId) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { return rbes.position; } return float3.zero; } public bool SetLocation(uint playerId, float3 location, bool exitSeat = true) { ExclusiveGroup[] characterGroups = CharacterExclusiveGroups.AllCharacters; for (int i = 0; i < characterGroups.Length; i++) { EGID egid = new EGID(playerId, characterGroups[i]); if (entitiesDB.Exists(egid)) { ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity(egid); if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat) { entitiesDB.QueryEntity(egid).instantExit = true; entitiesDB.PublishEntityChange(egid); } rbes.position = location; return true; } } return false; } public quaternion GetRotation(uint playerId) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { return rbes.rotation; } return quaternion.identity; } public bool SetRotation(uint playerId, quaternion value) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { rbes.rotation = value; return true; } return false; } public float3 GetLinearVelocity(uint playerId) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { return rbes.velocity; } return float3.zero; } public bool SetLinearVelocity(uint playerId, float3 value) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { rbes.velocity = value; return true; } return false; } public float3 GetAngularVelocity(uint playerId) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { return rbes.angularVelocity; } return float3.zero; } public bool SetAngularVelocity(uint playerId, float3 value) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { rbes.angularVelocity = value; return true; } return false; } public PhysicsMass GetMass(uint playerId) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { return rbes.physicsMass; } return default; } public bool SetInverseMass(uint playerId, float inverseMass) { if (GetCharacterStruct(playerId, out RigidBodyEntityStruct rbes)) { rbes.physicsMass.InverseInertia = inverseMass; return true; } return false; } public float? GetLastPingTime(uint playerId, PlayerType type) { EGID egid = new EGID(playerId, PlayerGroupFromEnum(type)); if (entitiesDB.Exists(egid)) { return entitiesDB.QueryEntity(egid).lastPingTimeSinceLevelLoad; } return null; } public float GetInitialHealth(uint playerId) { if (GetCharacterStruct(playerId, out CharacterHealthEntityStruct c)) { return c.initialHealth; } return -1f; } public bool SetInitialHealth(uint playerId, float val) { if (GetCharacterStruct(playerId, out CharacterHealthEntityStruct c)) { c.initialHealth = val; return true; } return false; } public float GetCurrentHealth(uint playerId) { if (GetCharacterStruct(playerId, out CharacterHealthEntityStruct c)) { return c.currentHealth; } return -1f; } public bool SetCurrentHealth(uint playerId, float val) { if (GetCharacterStruct(playerId, out CharacterHealthEntityStruct c)) { c.currentHealth = val; return true; } return false; } public bool DamagePlayer(uint playerId, float amount) { Factory.BuildEntity( new EGID(CharacterVulnerabilityExclusiveGroups.NextDamageEntityId, CharacterVulnerabilityExclusiveGroups.CharacterDamageExclusiveGroup) ).Init(new DamageEntityStruct { damage = amount, targetPlayerEntityId = playerId, }); return true; } public bool GetDamageable(uint playerId) { if (GetCharacterStruct(playerId, out CharacterHealthEntityStruct c)) { return c.canTakeDamageStat; } return false; } public bool SetDamageable(uint playerId, bool val) { if (GetCharacterStruct(playerId, out CharacterHealthEntityStruct ches)) { ches.canTakeDamage = val; ches.canTakeDamage = val; return true; } return false; } public uint GetInitialLives(uint playerId) { if (GetCharacterStruct(playerId, out CharacterLivesEntityComponent c)) { return c.initialLives; } return uint.MaxValue; } public bool SetInitialLives(uint playerId, uint val) { if (GetCharacterStruct(playerId, out CharacterLivesEntityComponent c)) { c.initialLives = val; return true; } return false; } public uint GetCurrentLives(uint playerId) { if (GetCharacterStruct(playerId, out CharacterLivesEntityComponent c)) { return c.currentLives; } return uint.MaxValue; } public bool SetCurrentLives(uint playerId, uint val) { if (GetCharacterStruct(playerId, out CharacterLivesEntityComponent c)) { c.currentLives = val; return true; } return false; } public bool GetGameOverScreen(uint playerId) { if (GetCharacterStruct(playerId, out CharacterLivesEntityComponent c)) { return c.gameOverScreen; } return false; } public bool IsDead(uint playerId) { return entitiesDB.Exists(playerId, CharacterExclusiveGroups.DeadGroup); } public int GetSelectedBlock(uint playerId) { if (GetCharacterStruct(playerId, out EquippedPartStruct c)) { return c.SelectedDBPartID; } return ushort.MaxValue; } public byte GetSelectedColor(uint playerId) { if (GetCharacterStruct(playerId, out EquippedColourStruct c)) { return c.indexInPalette; } return 255; } // reusable methods [MethodImpl(MethodImplOptions.AggressiveInlining)] private ExclusiveGroup PlayerGroupFromEnum(PlayerType type) { return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool GetCharacterStruct(uint playerId, out T s) where T : unmanaged, IEntityComponent { ExclusiveGroup[] characterGroups = CharacterExclusiveGroups.AllCharacters; for (int i = 0; i < characterGroups.Length; i++) { EGID egid = new EGID(playerId, characterGroups[i]); if (entitiesDB.Exists(egid)) { s = entitiesDB.QueryEntity(egid); return true; } } s = default; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool GetPlayerStruct(uint playerId, out T s) where T : unmanaged, IEntityComponent { ExclusiveGroup[] playerGroups = PlayersExclusiveGroups.AllPlayers; for (int i = 0; i < playerGroups.Length; i++) { EGID egid = new EGID(playerId, playerGroups[i]); if (entitiesDB.Exists(egid)) { s = entitiesDB.QueryEntity(egid); return true; } } s = default; return false; } public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f) { if (!entitiesDB.TryQueryMappedEntities( CameraExclusiveGroups.CameraGroup, out var mapper)) return null; mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast); float distance = maxDistance < 0 ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast) : maxDistance; if (rayCast.hit && rayCast.distance <= distance) return rayCast.hitEgid; return null; } } }