332 lines
7.2 KiB
C#
332 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Godot;
|
|
|
|
public abstract class GameObject
|
|
{
|
|
[Flags]
|
|
public enum DirtyFlags : ulong
|
|
{
|
|
None = 0,
|
|
UUID = 1UL << 0,
|
|
Sector = 1UL << 1,
|
|
LocalCoordinates = 1UL << 2,
|
|
Rotation = 1UL << 3,
|
|
Velocity = 1UL << 4,
|
|
AngularVelocity = 1UL << 5
|
|
}
|
|
public DirtyFlags DirtyBits { get; protected set; } = DirtyFlags.None;
|
|
|
|
protected Guid _uuid;
|
|
public Guid UUID
|
|
{
|
|
get => _uuid;
|
|
set
|
|
{
|
|
if (_uuid != value)
|
|
{
|
|
_uuid = value;
|
|
DirtyBits |= DirtyFlags.UUID;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Vector3I _currentSectorCoordinates;
|
|
protected Sector _currentSector;
|
|
public Sector CurrentSector
|
|
{
|
|
get => _currentSector;
|
|
protected set
|
|
{
|
|
if (_currentSector != value)
|
|
{
|
|
_currentSector = value;
|
|
_currentSectorCoordinates = value.Coordinates;
|
|
DirtyBits |= DirtyFlags.Sector;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Vector3 _localCoordinates;
|
|
public Vector3 LocalCoordinates
|
|
{
|
|
get => _localCoordinates;
|
|
protected set
|
|
{
|
|
if (_localCoordinates != value)
|
|
{
|
|
_localCoordinates = value;
|
|
DirtyBits |= DirtyFlags.LocalCoordinates;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Vector3 _rotation;
|
|
public Vector3 Rotation
|
|
{
|
|
get => _rotation;
|
|
protected set
|
|
{
|
|
if (_rotation != value)
|
|
{
|
|
_rotation = value;
|
|
DirtyBits |= DirtyFlags.Rotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Vector3 _velocity;
|
|
public Vector3 Velocity
|
|
{
|
|
get => _velocity;
|
|
protected set
|
|
{
|
|
if (_velocity != value)
|
|
{
|
|
_velocity = value;
|
|
DirtyBits |= DirtyFlags.Velocity;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Vector3 _angularVelocity;
|
|
public Vector3 AngularVelocity
|
|
{
|
|
get => _angularVelocity;
|
|
protected set
|
|
{
|
|
if (_angularVelocity != value)
|
|
{
|
|
_angularVelocity = value;
|
|
DirtyBits |= DirtyFlags.AngularVelocity;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Vector3Dec GlobalCoordinates { get; protected set; }
|
|
public Vector3 SectorOffset { get; protected set; }
|
|
|
|
protected bool reassigning = false;
|
|
|
|
public GameObject(Sector sector, Vector3 localCoordinates)
|
|
{
|
|
UUID = Guid.NewGuid();
|
|
|
|
CurrentSector = sector;
|
|
LocalCoordinates = localCoordinates;
|
|
|
|
Velocity = new(0, 0, 1);
|
|
AngularVelocity = Vector3.Zero;
|
|
Rotation = Vector3.Zero;
|
|
|
|
GlobalCoordinates = CalculateGlobalCoordinates(sector.GlobalCenterCoordinates, localCoordinates);
|
|
}
|
|
|
|
public GameObject(Vector3Dec coordinates)
|
|
{
|
|
GlobalCoordinates = coordinates;
|
|
UpdateSector();
|
|
}
|
|
|
|
public GameObject(decimal x, decimal y, decimal z)
|
|
{
|
|
GlobalCoordinates = new(x, y, z);
|
|
UpdateSector();
|
|
}
|
|
|
|
public Vector3Dec CalculateGlobalCoordinates(Vector3Dec sectorCenter, Vector3 local)
|
|
{
|
|
return new
|
|
(
|
|
sectorCenter.X + (decimal)local.X,
|
|
sectorCenter.Y + (decimal)local.Y,
|
|
sectorCenter.Z + (decimal)local.Z
|
|
);
|
|
}
|
|
|
|
public void ApplyVelocity(double delta)
|
|
{
|
|
SetCoordinatesFromLocal(LocalCoordinates + Velocity * delta);
|
|
Rotation += AngularVelocity * delta;
|
|
}
|
|
|
|
public bool IsInCurrentSector()
|
|
{
|
|
return Helpers.IsInsideArea(CurrentSector.Size / 2, LocalCoordinates);
|
|
}
|
|
|
|
public void UpdateSector()
|
|
{
|
|
List<Sector> neighbours = CurrentSector.GetNeighbouringSectors();
|
|
foreach (Sector sector in neighbours)
|
|
{
|
|
if (sector.IsObjectInSector(this))
|
|
{
|
|
reassigning = true;
|
|
sector.AssignObject(this);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!GameManager.GameUniverse.IsInside(CurrentSector.Coordinates, LocalCoordinates))
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (Sector sector in GameManager.GameUniverse.Sectors)
|
|
{
|
|
if (sector.IsObjectInSector(this))
|
|
{
|
|
reassigning = true;
|
|
sector.AssignObject(this);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void AssignSector(Sector sector)
|
|
{
|
|
CurrentSector = sector;
|
|
ResetLocalCoordinates();
|
|
|
|
UpdateSectorOffsetToMainPlayer();
|
|
|
|
List<Sector> neighbours = GameManager.Instance.GetCurrentSector().GetNeighbouringSectors();
|
|
|
|
if (neighbours.Contains(sector))
|
|
{
|
|
GameManager.Instance.Spawn(this);
|
|
}
|
|
else
|
|
{
|
|
GameManager.Instance.Despawn(this);
|
|
}
|
|
|
|
reassigning = false;
|
|
}
|
|
|
|
public Vector3 GetSectorOffset(Sector sector)
|
|
{
|
|
Vector3Dec relative = CurrentSector.GlobalCenterCoordinates - sector.GlobalCenterCoordinates;
|
|
return relative.ToVector3();
|
|
}
|
|
|
|
public void UpdateSectorOffset(Sector sector)
|
|
{
|
|
SectorOffset = GetSectorOffset(sector);
|
|
}
|
|
|
|
public void UpdateSectorOffsetToMainPlayer()
|
|
{
|
|
UpdateSectorOffset(GameManager.Instance.GetCurrentSector());
|
|
}
|
|
|
|
public void SetCoordinatesFromGlobal(Vector3Dec globalCoordinates)
|
|
{
|
|
GlobalCoordinates = globalCoordinates;
|
|
LocalCoordinates = (globalCoordinates - CurrentSector.GlobalCenterCoordinates).ToVector3();
|
|
|
|
UpdateNodePosition();
|
|
}
|
|
|
|
public void SetCoordinatesFromLocal(Vector3 localCoordinates)
|
|
{
|
|
LocalCoordinates = localCoordinates;
|
|
GlobalCoordinates = Vector3Dec.FromVector3(localCoordinates) + CurrentSector.GlobalCenterCoordinates;
|
|
|
|
UpdateNodePosition();
|
|
}
|
|
|
|
public void ResetLocalCoordinates()
|
|
{
|
|
SetCoordinatesFromGlobal(GlobalCoordinates);
|
|
}
|
|
|
|
public virtual void UpdateNodePosition() { }
|
|
|
|
public virtual void Simulate(double delta)
|
|
{
|
|
ApplyVelocity(delta);
|
|
|
|
if (!reassigning && !IsInCurrentSector())
|
|
{
|
|
UpdateSector();
|
|
}
|
|
}
|
|
|
|
public virtual Node3D Instantiate(Sector sector)
|
|
{
|
|
PackedScene modulePrefab = ResourceLoader.Load<PackedScene>("res://prefabs/gameObjects/node.tscn");
|
|
Node3D instance = modulePrefab.Instantiate<Node3D>();
|
|
|
|
instance.Name = $"GameObject-{UUID}";
|
|
|
|
return instance;
|
|
}
|
|
|
|
public virtual Godot.Collections.Dictionary NetworkWrite(long id, bool full)
|
|
{
|
|
Godot.Collections.Dictionary gameObjectData = [];
|
|
|
|
if (full)
|
|
gameObjectData.Add("type", GetType().ToString());
|
|
|
|
if (DirtyBits.HasFlag(DirtyFlags.Sector) || full)
|
|
gameObjectData.Add("sectorCoordinates", _currentSectorCoordinates);
|
|
|
|
if (DirtyBits.HasFlag(DirtyFlags.LocalCoordinates) || full)
|
|
gameObjectData.Add("localCoordinates", _localCoordinates);
|
|
|
|
if (DirtyBits.HasFlag(DirtyFlags.Rotation) || full)
|
|
gameObjectData.Add("rotation", _rotation);
|
|
|
|
if (DirtyBits.HasFlag(DirtyFlags.Velocity) || full)
|
|
gameObjectData.Add("velocity", _velocity);
|
|
|
|
if (DirtyBits.HasFlag(DirtyFlags.AngularVelocity) || full)
|
|
gameObjectData.Add("angularVelocity", _angularVelocity);
|
|
|
|
if (gameObjectData.Count > 0)
|
|
{
|
|
gameObjectData.Add("uuid", UUID.ToString());
|
|
return gameObjectData;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public virtual void NetworkRead(Godot.Collections.Dictionary gameObjectData)
|
|
{
|
|
if (gameObjectData.TryGetValue("sectorCoordinates", out var sectorCoordinatesData))
|
|
{
|
|
Sector newSector = GameManager.GameUniverse.GetSector((Vector3I)sectorCoordinatesData);
|
|
CurrentSector.GameObjects.Remove(this);
|
|
newSector.GameObjects.Add(this);
|
|
|
|
AssignSector(newSector);
|
|
}
|
|
|
|
if (gameObjectData.TryGetValue("localCoordinates", out var localCoordinatesData))
|
|
LocalCoordinates = (Vector3)localCoordinatesData;
|
|
|
|
if (gameObjectData.TryGetValue("rotation", out var rotationData))
|
|
Rotation = (Vector3)rotationData;
|
|
|
|
if (gameObjectData.TryGetValue("velocity", out var velocityData))
|
|
Velocity = (Vector3)velocityData;
|
|
|
|
if (gameObjectData.TryGetValue("angularVelocity", out var angularVelocityData))
|
|
AngularVelocity = (Vector3)angularVelocityData;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
GameObject gameObj = (GameObject)obj;
|
|
return gameObj.UUID == UUID;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return UUID.GetHashCode();
|
|
}
|
|
}
|