using System; using System.Runtime.CompilerServices; using RobocraftX.Character; using RobocraftX.Character.Movement; using RobocraftX.Common.Players; using RobocraftX.Common.Input; using RobocraftX.CR.MachineEditing.BoxSelect; 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 UnityEngine; 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; var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers); if (localPlayers.count > 0) { return localPlayers[0].ID.entityID; } return uint.MaxValue; } public uint GetRemotePlayer() { if (!isReady) return uint.MaxValue; var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers); if (localPlayers.count > 0) { return localPlayers[0].ID.entityID; } return uint.MaxValue; } public long GetAllPlayerCount() { if (entitiesDB == null) return 0; long count = 0; foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers) { count += entitiesDB.Count(eg); } return count; } public long GetLocalPlayerCount() { if (entitiesDB == null) return 0; return entitiesDB.Count(PlayersExclusiveGroups.LocalPlayers); } public long GetRemotePlayerCount() { if (entitiesDB == null) return 0; return entitiesDB.Count(PlayersExclusiveGroups.RemotePlayers); } public bool ExistsById(uint playerId) { if (entitiesDB == null) return false; return entitiesDB.Exists(playerId, PlayersExclusiveGroups.LocalPlayers) || entitiesDB.Exists(playerId, PlayersExclusiveGroups.RemotePlayers); } public float3 GetLocation(uint playerId) { if (entitiesDB == null) return float3.zero; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return rbes.position; } return float3.zero; } public bool SetLocation(uint playerId, float3 location, bool exitSeat = true) { if (entitiesDB == null) return false; var characterGroups = CharacterExclusiveGroups.AllCharacters; for (int i = 0; i < characterGroups.count; 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 float3 GetRotation(uint playerId) { if (entitiesDB == null) return float3.zero; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return ((Quaternion) rbes.rotation).eulerAngles; } return default(float3); } public bool SetRotation(uint playerId, float3 value) { if (entitiesDB == null) return false; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { Quaternion q = rbes.rotation; q.eulerAngles = value; rbes.rotation = q; return true; } return false; } public float3 GetLinearVelocity(uint playerId) { if (entitiesDB == null) return float3.zero; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return rbes.velocity; } return float3.zero; } public bool SetLinearVelocity(uint playerId, float3 value) { if (entitiesDB == null) return false; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { rbes.velocity = value; return true; } return false; } public float3 GetAngularVelocity(uint playerId) { if (entitiesDB == null) return float3.zero; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return rbes.angularVelocity; } return float3.zero; } public bool SetAngularVelocity(uint playerId, float3 value) { if (entitiesDB == null) return false; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { rbes.angularVelocity = value; return true; } return false; } public PhysicsMass GetMass(uint playerId) { if (entitiesDB == null) return default(PhysicsMass); ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return rbes.physicsMass; } return default(PhysicsMass); } public bool SetInverseMass(uint playerId, float inverseMass) { if (entitiesDB == null) return false; ref var rbes = ref GetCharacterStruct(playerId, out bool exists); if (exists) { rbes.physicsMass.InverseInertia = inverseMass; return true; } return false; } public float? GetLastPingTime(uint playerId, PlayerType type) { if (entitiesDB == null) return null; EGID egid = new EGID(playerId, PlayerGroupFromEnum(type)); if (entitiesDB.Exists(egid)) { return entitiesDB.QueryEntity(egid).lastPingTimeSinceLevelLoad; } return null; } public float GetInitialHealth(uint playerId) { if (entitiesDB == null) return 0; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.initialHealth; } return -1f; } public bool SetInitialHealth(uint playerId, float val) { if (entitiesDB == null) return false; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { c.initialHealth = val; return true; } return false; } public float GetCurrentHealth(uint playerId) { if (entitiesDB == null) return 0; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.currentHealth; } return -1f; } public bool SetCurrentHealth(uint playerId, float val) { if (entitiesDB == null) return false; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { c.currentHealth = val; return true; } return false; } public bool DamagePlayer(uint playerId, float amount) { if (entitiesDB == null) return false; Factory.BuildEntity( new EGID(CharacterVulnerabilityExclusiveGroups.NextDamageEntityId, CharacterVulnerabilityExclusiveGroups.CharacterDamageExclusiveGroup) ).Init(new DamageEntityStruct { damage = amount, targetPlayerEntityId = playerId, }); return true; } public bool GetDamageable(uint playerId) { if (entitiesDB == null) return false; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.canTakeDamageStat; } return false; } public bool SetDamageable(uint playerId, bool val) { if (entitiesDB == null) return false; ref var ches = ref GetCharacterStruct(playerId, out bool exists); if (exists) { ches.canTakeDamage = val; ches.canTakeDamage = val; return true; } return false; } public uint GetInitialLives(uint playerId) { if (entitiesDB == null) return 0; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.initialLives; } return uint.MaxValue; } public bool SetInitialLives(uint playerId, uint val) { if (entitiesDB == null) return false; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { c.initialLives = val; return true; } return false; } public uint GetCurrentLives(uint playerId) { if (entitiesDB == null) return 0; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.currentLives; } return uint.MaxValue; } public bool SetCurrentLives(uint playerId, uint val) { if (entitiesDB == null) return false; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { c.currentLives = val; return true; } return false; } public bool GetGameOverScreen(uint playerId) { if (entitiesDB == null) return false; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.gameOverScreen; } return false; } public bool IsDead(uint playerId) { if (entitiesDB == null) return true; return entitiesDB.Exists(playerId, CharacterExclusiveGroups.DeadCharacters); } public int GetSelectedBlock(uint playerId) { if (entitiesDB == null) return 0; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { return c.SelectedDBPartID; } return ushort.MaxValue; } public byte GetSelectedColor(uint playerId) { if (entitiesDB == null) return 0; ref var c = ref GetCharacterStruct(playerId, out bool exists); if (exists) { 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 ref T GetCharacterStruct(uint playerId, out bool exists) where T : unmanaged, IEntityComponent { var characterGroups = CharacterExclusiveGroups.AllCharacters; for (int i = 0; i < characterGroups.count; i++) { EGID egid = new EGID(playerId, characterGroups[i]); if (entitiesDB.Exists(egid)) { exists = true; return ref entitiesDB.QueryEntity(egid); } } exists = false; T[] arr = new T[1]; return ref arr[0]; //Return default value } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool GetPlayerStruct(uint playerId, out T s) where T : unmanaged, IEntityComponent { var playerGroups = PlayersExclusiveGroups.AllPlayers; for (int i = 0; i < playerGroups.count; 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, GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize) : maxDistance; if (rayCast.hit && rayCast.distance <= distance) return rayCast.hitEgid; return null; } public unsafe Block[] GetSelectedBlocks(uint playerid) { if (!entitiesDB.Exists(playerid, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)) return new Block[0]; var state = entitiesDB.QueryEntity(playerid, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); var blocks = entitiesDB.QueryEntity(playerid, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); if (!state.active) return new Block[0]; var pointer = (EGID*) blocks.selectedBlocks.ToPointer(); var ret = new Block[blocks.count]; for (int j = 0; j < blocks.count; j++) { var egid = pointer[j]; ret[j] = new Block(egid); } return ret; } } }