/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; using System.Collections.Generic; using System.Linq; using Unity.Mathematics; using static Unity.Mathematics.math; using float2 = Unity.Mathematics.float2; using int2 = Unity.Mathematics.int2; [Inspectable] public class Shape { public bool[,] Cells; private bool _dirty = true; public Shape() { Cells = new bool[1, 1]; Cells[0, 0] = true; } public Shape(int width, int height) { Cells = new bool[width, height]; } public int Width { get { return Cells.GetLength(0); } set { Resize(value, Height); } } public int Height { get { return Cells.GetLength(1); } set { Resize(Width, value); } } public void Resize(int width, int height) { var newCells = new bool[width, height]; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { newCells[x, y] = x >= Width || y >= Height || Cells[x, y]; } } Cells = newCells; } public int2 Rotate(int2 position, ItemRotation rotation) { return rotation switch { ItemRotation.Clockwise => int2(position.y, Width - 1 - position.x), ItemRotation.Reversed => int2(Width - 1 - position.x, Height - 1 - position.y), ItemRotation.CounterClockwise => int2(Height - 1 - position.y, position.x), _ => int2(position.x, position.y) }; } private int2[] _cachedShapeCoordinates; public int2[] Coordinates { get { if (_dirty) { _cachedShapeCoordinates = EnumerateShapeCoordinates().ToArray(); _dirty = false; } return _cachedShapeCoordinates; } } private IEnumerable EnumerateShapeCoordinates() { for (var y = 0; y < Height; y++) { for (var x = 0; x < Width; x++) { if (Cells[x, y]) yield return int2(x, y); } } } private int2[] _cachedAllShapeCoordinates; public int2[] AllCoordinates => _cachedAllShapeCoordinates ??= EnumerateAllShapeCoordinates().ToArray(); private IEnumerable EnumerateAllShapeCoordinates() { for (var y = 0; y < Height; y++) { for (var x = 0; x < Width; x++) { yield return int2(x, y); } } } private float2? _centerOfMass; public float2 CenterOfMass => _centerOfMass ??= Coordinates .Aggregate(float2.zero, (total, coord) => total + coord) / Coordinates.Length; public bool this[int2 pos] { get { return pos.x >= 0 && pos.y >= 0 && pos.x < Width && pos.y < Height && Cells[pos.x, pos.y]; } set { if (pos.x < 0 || pos.y < 0 || pos.x >= Width || pos.y >= Height) return; _dirty = true; Cells[pos.x, pos.y] = value; } } public Shape Shrink() { var shape = new Shape(Width, Height); foreach (var shapeCoord in Coordinates) { shape[shapeCoord] = ( this[shapeCoord + int2(-1, -1)] && this[shapeCoord + int2(0, -1)] && this[shapeCoord + int2(1, -1)] && this[shapeCoord + int2(-1, 0)] && this[shapeCoord + int2(1, 0)] && this[shapeCoord + int2(-1, 1)] && this[shapeCoord + int2(0, 1)] && this[shapeCoord + int2(1, 1)] ); } return shape; } public Shape Inset(Shape inset, int2 insetPosition, ItemRotation rotation = ItemRotation.None) { var shape = new Shape(max(Width, insetPosition.x + inset.Width - 1), max(Height, insetPosition.y + inset.Height - 1)); foreach (var v in inset.Coordinates) { var insetCoord = inset.Rotate(v, rotation) + insetPosition; shape[insetCoord] = true; } return shape; } public Shape Expand() { var shape = new Shape(Width, Height); foreach (var shapeCoord in AllCoordinates) { shape[shapeCoord] = ( this[shapeCoord + int2(-1, -1)] || this[shapeCoord + int2(0, -1)] || this[shapeCoord + int2(1, -1)] || this[shapeCoord + int2(-1, 0)] || this[shapeCoord] || this[shapeCoord + int2(1, 0)] || this[shapeCoord + int2(-1, 1)] || this[shapeCoord + int2(0, 1)] || this[shapeCoord + int2(1, 1)] ); } return shape; } public bool FitsWithin(Shape other, out ItemRotation rotation, out int2 position) { foreach (var rot in (ItemRotation[])Enum.GetValues(typeof(ItemRotation))) { rotation = rot; if (FitsWithin(other, rot, out var pos)) { position = pos; return true; } } rotation = ItemRotation.None; position = int2.zero; return false; } public bool FitsWithin(Shape other, ItemRotation rotation, out int2 position) { var width = rotation == ItemRotation.Clockwise || rotation == ItemRotation.CounterClockwise ? Height : Width; var height = rotation == ItemRotation.Clockwise || rotation == ItemRotation.CounterClockwise ? Width : Height; for (var x = 0; x < other.Width - width + 1; x++) { for (var y = 0; y < other.Height - height + 1; y++) { position = int2(x, y); var fits = true; foreach (var v in Coordinates) { fits = fits && other[Rotate(v, rotation) + position]; if (!fits) break; } if (fits) return true; } } position = int2.zero; return false; } public void SetLine(float2 a, float2 b) { if (a.Equals(b)) return; var steep = Math.Abs(b.y - a.y) > Math.Abs(b.x - a.x); if (steep) { a = new float2(a.y, a.x); b = new float2(b.y, b.x); } if (a.x > b.x) { var temp = a; a = b; b = temp; } var dx = b.x - a.x; var dy = b.y - a.y; var derr = Math.Abs(dy / dx); var y = (int)Math.Round(a.y); var xStart = (int)Math.Round(a.x); var error = (a.y - y) + (xStart - a.x) * derr; for (var x = xStart; x <= (int)Math.Round(b.x); x++) { if (x >= 0 && x < Width && y >= 0 && y < Height) { Cells[steep ? y : x, steep ? x : y] = true; } error += derr; if (error >= 0.5f) { y += Math.Sign(dy); error -= 1f; } } } } [Inspectable] public class HardpointData : ITintInspector { [Inspectable] public HardpointType Type; [Inspectable] public int2 Position; [Inspectable] public Shape Shape = new Shape(); [Inspectable] public string Transform; [Inspectable] public ItemRotation Rotation; [Inspectable] public float Armor; public override string ToString() { return $"{Enum.GetName(typeof(HardpointType), Type)} Hardpoint {Rotation.Arrow()}"; } public float3 TintColor { get { return GetColor(Type); } } public static float3 GetColor(HardpointType type) { if (_tintColors == null) { var hardpointTypes = (HardpointType[])Enum.GetValues(typeof(HardpointType)); _tintColors = hardpointTypes.ToDictionary( x => x, x => ColorMath.HsvToRgb(float3(frac((float)(int)x / hardpointTypes.Length + .25f), 1, 1))); } return _tintColors.TryGetValue(type, out var color) ? color : _tintColors[HardpointType.Hull]; } private static Dictionary _tintColors; } public class PerformanceStat { public float Min; public float Max; public float HeatExponentMultiplier; public float DurabilityExponentMultiplier; public float QualityExponent; private Dictionary> _scaleModifiers; private Dictionary> _constantModifiers; private Dictionary> ScaleModifiers => _scaleModifiers ??= new Dictionary>(); private Dictionary> ConstantModifiers => _constantModifiers ??= new Dictionary>(); public Dictionary GetScaleModifiers(Entity entity) { if (!ScaleModifiers.ContainsKey(entity)) ScaleModifiers[entity] = new Dictionary(); return ScaleModifiers[entity]; } public Dictionary GetConstantModifiers(Entity entity) { if (!ConstantModifiers.ContainsKey(entity)) ConstantModifiers[entity] = new Dictionary(); return ConstantModifiers[entity]; } }