using Sandbox.Definitions; using Sandbox.ModAPI; using System.Text; using VRage.Game; using VRage.Game.ModAPI; using VRage.Utils; using VRageMath; namespace SpaceEngineersBlueprintEditor.SpaceEngineersCore.GridConverter; public class GridExportHelper { private const float MAX_DISTANCE = 1000; private const string SEPARATOR = "|"; private const string FILE_PREFIX = "GridExport_"; private const string FILE_TYPE = ".txt"; private const float UNIFIED_SIZE = 0.25f; private const string DEFAULT_LARGE_BLOCK = "2eacbbf2-d8fb-4a78-91dc-7b492517ef97"; private const string DEFAULT_SMALL_BLOCK = "632d7385-12b9-47a6-802a-a610d0cbd1e0"; private List _blocks = new List(); private HashSet _unrecognizedBlocks = new HashSet(); private MyStringId _debugMaterial = MyStringId.GetOrCompute("Square"); private Vector4 _forwardColor = new Vector4(0, 0, 10, 10); private Vector4 _upColor = new Vector4(0, 10, 0, 10); private Vector4 _rightColor = new Vector4(10, 0, 0, 10); private const float LINE_THICKNESS = 0.01f; private ConversionDefinitions _definitions; /// /// Exports the grid to a text file /// /// /// Unknown blocks will be replaced with armor unless told to skip /// /// Unknown blocks without physics will be replaced with armor unless told to skip public void ExportGrid(IMyCubeGrid grid, string blueprintName, Action showMessage, bool replaceUnknownBlocks = true, bool replacePhysicless = false) { _blocks.Clear(); _unrecognizedBlocks.Clear(); grid.GetBlocks(_blocks); StringBuilder export = new StringBuilder(); MyAPIGateway.Parallel.Start(() => { foreach (var block in _blocks) { if (_definitions.BlackList.Contains(block.BlockDefinition.Id)) continue; MyCubeBlockDefinition blockDefinition = block.BlockDefinition as MyCubeBlockDefinition; BlockConversionDefinition conversionDefinition; bool knownBlock = _definitions.BlockTypes.Dictionary.TryGetValue(block.BlockDefinition.Id, out conversionDefinition); int gridToUniform = (int)(grid.GridSize / UNIFIED_SIZE); if (!knownBlock && _unrecognizedBlocks.Add(block.BlockDefinition.Id)) { showMessage($"Did not recognize block '{block.BlockDefinition.Id}'"); } if (!knownBlock && !replaceUnknownBlocks || !knownBlock && replaceUnknownBlocks && !blockDefinition.HasPhysics && !replacePhysicless) continue; /* * Follows the format of * BlockId|PositionX|PositionY|PositionZ|ColorHUE|ColorSATURATION|ColorVALUE|OrientationFORWARD|OrientationUP|Integrity| */ Vector3 color = MyColorPickerConstants.HSVOffsetToHSV(block.ColorMaskHSV); MyBlockOrientation orientation; MatrixI matrix = MatrixI.CreateRotation(Base6Directions.Direction.Forward, Base6Directions.Direction.Up, block.Orientation.Forward, block.Orientation.Up); Vector3I size = blockDefinition.Size * gridToUniform; Vector3 minOffset = (size - 1) / 2f; Vector3 maxOffset = size / 2f; if (knownBlock) { foreach (var conversion in conversionDefinition.Blocks) { Quaternion quaternion; orientation = block.Orientation; orientation.GetQuaternion(out quaternion); if (!Quaternion.IsZero(conversion.Rotation)) { quaternion *= conversion.Rotation; orientation.Forward = Base6Directions.GetForward(quaternion); orientation.Up = Base6Directions.GetUp(quaternion); } if (!orientation.IsValid) { showMessage($"Invalid rotation for block {block.BlockDefinition.Id} F:'{orientation.Forward}' U:'{orientation.Up}'"); continue; } Vector3I transformSize = Vector3I.Transform(size, quaternion); Vector3 minOffsetTransformed = Vector3.Transform(minOffset, quaternion); Vector3 maxOffsetTransformed = Vector3.Transform(maxOffset, quaternion); Vector3I offset = Vector3I.Round(minOffset - minOffsetTransformed) - Vector3I.Round(maxOffset - maxOffsetTransformed); Vector3I forwardOffset = Vector3I.TransformNormal(Vector3I.Forward * conversion.ForwardOffset, ref matrix); Vector3I upOffset = Vector3I.TransformNormal(Vector3I.Up * conversion.UpOffset, ref matrix); Vector3I rightOffset = Vector3I.TransformNormal(Vector3I.Right * conversion.RightOffset, ref matrix); Vector3I origin = Vector3I.Round((block.Min * gridToUniform + block.Max * gridToUniform) / 2f) - offset + forwardOffset + upOffset + rightOffset; export.Append(conversion.BlockServerPrefab + SEPARATOR); export.Append(origin.X + SEPARATOR + origin.Y + SEPARATOR + origin.Z + SEPARATOR); export.Append(color.X + SEPARATOR + color.Y + SEPARATOR + color.Z + SEPARATOR); export.Append((int)orientation.Forward + SEPARATOR + (int)orientation.Up + SEPARATOR); export.Append(block.Integrity / block.MaxIntegrity + SEPARATOR); export.AppendLine(); } } else if (replaceUnknownBlocks) { string prefab; if (grid.GridSizeEnum == MyCubeSize.Large) prefab = DEFAULT_LARGE_BLOCK; else prefab = DEFAULT_SMALL_BLOCK; for (int x = block.Min.X; x <= block.Max.X; x++) { for (int y = block.Min.Y; y <= block.Max.Y; y++) { for (int z = block.Min.Z; z <= block.Max.Z; z++) { Vector3I origin = new Vector3I(x, y, z) * gridToUniform; export.Append(prefab + SEPARATOR); export.Append(origin.X + SEPARATOR + origin.Y + SEPARATOR + origin.Z + SEPARATOR); export.Append(color.X + SEPARATOR + color.Y + SEPARATOR + color.Z + SEPARATOR); export.Append((int)Base6Directions.Direction.Forward + SEPARATOR + (int)Base6Directions.Direction.Up + SEPARATOR); export.Append(block.Integrity / block.MaxIntegrity + SEPARATOR); export.AppendLine(); } } } } } try { string fileName = FILE_PREFIX + blueprintName + FILE_TYPE; TextWriter writer = MyAPIGateway.Utilities.WriteFileInGlobalStorage(fileName); writer.Write(export); writer.Close(); showMessage($"Saved grid to '{MyAPIGateway.Utilities.GamePaths.UserDataPath + "\\Storage\\" + fileName}'"); } catch (Exception e) { showMessage("Encountered an exception when writing to storage, see log."); MyLog.Default.WriteLine(e.ToString()); } }); } }