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 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 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("res://prefabs/gameObjects/node.tscn"); Node3D instance = modulePrefab.Instantiate(); 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(); } }