476 lines
14 KiB
C#
476 lines
14 KiB
C#
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 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<PlayerIDStruct>(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<PlayerIDStruct>(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<PlayerIDStruct>(eg);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public long GetLocalPlayerCount()
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
|
}
|
|
|
|
public long GetRemotePlayerCount()
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
|
}
|
|
|
|
public bool ExistsById(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
|
|
|| entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
|
|
}
|
|
|
|
public float3 GetLocation(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return float3.zero;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(egid))
|
|
{
|
|
ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity<RigidBodyEntityStruct>(egid);
|
|
if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
|
{
|
|
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
|
|
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
|
}
|
|
rbes.position = location;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float3 GetRotation(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return float3.zero;
|
|
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<RigidBodyEntityStruct>(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<PlayerNetworkStatsEntityStruct>(egid))
|
|
{
|
|
return entitiesDB.QueryEntity<PlayerNetworkStatsEntityStruct>(egid).lastPingTimeSinceLevelLoad;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public float GetInitialHealth(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(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<CharacterHealthEntityStruct>(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<CharacterHealthEntityStruct>(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<CharacterHealthEntityStruct>(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;
|
|
return SetCurrentHealth(playerId, GetCurrentHealth(playerId) - amount);
|
|
}
|
|
|
|
public bool GetDamageable(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return false;
|
|
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(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<CharacterHealthEntityStruct>(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<CharacterLivesEntityComponent>(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<CharacterLivesEntityComponent>(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<CharacterLivesEntityComponent>(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<CharacterLivesEntityComponent>(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<CharacterLivesEntityComponent>(playerId, out bool exists);
|
|
if (exists)
|
|
{
|
|
return c.gameOverScreen;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool IsDead(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return true;
|
|
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
|
|
}
|
|
|
|
public int GetSelectedBlock(uint playerId)
|
|
{
|
|
if (entitiesDB == null) return 0;
|
|
ref var c = ref GetCharacterStruct<EquippedPartStruct>(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<EquippedColourStruct>(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<T>(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<T>(egid))
|
|
{
|
|
exists = true;
|
|
return ref entitiesDB.QueryEntity<T>(egid);
|
|
}
|
|
}
|
|
|
|
exists = false;
|
|
T[] arr = new T[1];
|
|
return ref arr[0]; //Return default value
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool GetPlayerStruct<T>(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<T>(egid))
|
|
{
|
|
s = entitiesDB.QueryEntity<T>(egid);
|
|
return true;
|
|
}
|
|
}
|
|
s = default;
|
|
return false;
|
|
}
|
|
|
|
public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f)
|
|
{
|
|
if (!entitiesDB.TryQueryMappedEntities<CharacterCameraRayCastEntityStruct>(
|
|
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<BoxSelectStateEntityStruct>(playerid,
|
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
|
return new Block[0];
|
|
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
|
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
|
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(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;
|
|
}
|
|
}
|
|
}
|