#region Using
using Havok;
using ProtoBuf;
using Sandbox.Common;
using Sandbox.Common.ObjectBuilders;
using Sandbox.Common.ObjectBuilders.Definitions;
using Sandbox.Definitions;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Physics;
using Sandbox.Engine.Utils;
using Sandbox.Game.Components;
using Sandbox.Game.Entities.Character;
using Sandbox.Game.Entities.Cube;
using Sandbox.Game.GameSystems;
using Sandbox.Game.GameSystems.StructuralIntegrity;
using Sandbox.Game.GUI;
using Sandbox.Game.Localization;
using Sandbox.Game.Multiplayer;
using Sandbox.Game.World;
using Sandbox.ModAPI;
using Sandbox.ModAPI.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using VRage;
using VRage.Collections;
using VRage.Game.Components;
using VRage.Library.Utils;
using VRage.ModAPI;
using VRage.Network;
using VRage.ObjectBuilders;
using VRage.Utils;
using VRageMath;
using VRageRender;
using Sandbox.Game.EntityComponents;
using VRage.Game.ObjectBuilders.ComponentSystem;
using Sandbox.Game.Replication;
using Sandbox.Game.GameSystems.CoordinateSystem;
using VRage.Game.Entity;
using VRage.Game.Models;
using Sandbox.Game.Weapons;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.Game.ModAPI.Interfaces;
using Sandbox.Game.Gui;
using Sandbox.Game.SessionComponents.Clipboard;
using VRage.Audio;
using VRage.Game.ObjectBuilders.Definitions.SessionComponents;
using ParallelTasks;
using VRage.Game.Entity.EntityComponents;
using VRage.Profiler;
using VRage.Sync;
using VRage.Library.Collections;
#endregion
namespace Sandbox.Game.Entities
{
class MySyncGridThrustState
{
public Vector3B LastSendState;
public int SleepFrames = 0;
public bool ShouldSend(Vector3B newThrust)
{
if (SleepFrames > 4 && LastSendState != newThrust)
{
SleepFrames = 0;
LastSendState = newThrust;
return true;
}
else
{
SleepFrames++;
return false;
}
}
}
///
/// Grid - small ship, large ship, station
/// Cubes (armor, walls...) are merge and rendered by this entity
/// Blocks (turret, thrusts...) are rendered as child entities
///
[StaticEventOwner]
[MyEntityType(typeof(MyObjectBuilder_CubeGrid))]
public partial class MyCubeGrid : MyEntity, IMyGridConnectivityTest, IMyEventProxy
{
static MyCubeGridHitInfo m_hitInfoTmp;
static HashSet m_tmpBuildList = new HashSet();
static List m_tmpPositionListReceive = new List();
static List m_tmpPositionListSend = new List();
private List m_removeBlockQueueWithGenerators = new List();
private List m_removeBlockQueueWithoutGenerators = new List();
private List m_destroyBlockQueue = new List();
private List m_destructionDeformationQueue = new List();
private List m_destroyBlockWithIdQueueWithGenerators = new List();
private List m_destroyBlockWithIdQueueWithoutGenerators = new List();
private List m_removeBlockWithIdQueueWithGenerators = new List();
private List m_removeBlockWithIdQueueWithoutGenerators = new List();
static List m_boneByteList = new List();
private List m_tmpBlockIdList = new List();
Vector3 m_gravity = Vector3.Zero;
private MySyncGridThrustState m_thrustState = new MySyncGridThrustState();
public event Action SyncPropertyChanged
{
add { SyncType.PropertyChanged += value; }
remove { SyncType.PropertyChanged -= value; }
}
public readonly SyncType SyncType;
readonly Sync m_handBrakeSync;
static List m_recievedGrids = new List();
static MyCubeGrid()
{
for (int i = 0; i < 26; ++i)
{
m_neighborOffsetIndices.Add((NeighborOffsetIndex)i);
m_neighborDistances.Add(0.0f);
m_neighborOffsets.Add(new Vector3I(0, 0, 0));
}
m_neighborOffsets[(int)NeighborOffsetIndex.XUP] = new Vector3I(1, 0, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN] = new Vector3I(-1, 0, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.YUP] = new Vector3I(0, 1, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.YDOWN] = new Vector3I(0, -1, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.ZUP] = new Vector3I(0, 0, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.ZDOWN] = new Vector3I(0, 0, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_YUP] = new Vector3I(1, 1, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_YDOWN] = new Vector3I(1, -1, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_YUP] = new Vector3I(-1, 1, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_YDOWN] = new Vector3I(-1, -1, 0);
m_neighborOffsets[(int)NeighborOffsetIndex.YUP_ZUP] = new Vector3I(0, 1, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.YUP_ZDOWN] = new Vector3I(0, 1, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.YDOWN_ZUP] = new Vector3I(0, -1, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.YDOWN_ZDOWN] = new Vector3I(0, -1, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_ZUP] = new Vector3I(1, 0, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_ZDOWN] = new Vector3I(1, 0, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_ZUP] = new Vector3I(-1, 0, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_ZDOWN] = new Vector3I(-1, 0, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_YUP_ZUP] = new Vector3I(1, 1, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_YUP_ZDOWN] = new Vector3I(1, 1, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_YDOWN_ZUP] = new Vector3I(1, -1, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.XUP_YDOWN_ZDOWN] = new Vector3I(1, -1, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_YUP_ZUP] = new Vector3I(-1, 1, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_YUP_ZDOWN] = new Vector3I(-1, 1, -1);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_YDOWN_ZUP] = new Vector3I(-1, -1, 1);
m_neighborOffsets[(int)NeighborOffsetIndex.XDOWN_YDOWN_ZDOWN] = new Vector3I(-1, -1, -1);
GridCounter = 0;
}
///
/// Used when calculating damage from deformation application
///
private float m_totalBoneDisplacement;
///
/// Value used by MoveCornerBones() to precalculate the bones displacement distance.
/// A performance boost is gained because we can then avoid having to use Vector3.Length() which means we don't need sqrt() each time.
///
private static float m_precalculatedCornerBonesDisplacementDistance = 0;
internal MyVoxelSegmentation BonesToSend = new MyVoxelSegmentation();
private int m_bonesSendCounter = 0;
private MyDirtyRegion m_dirtyRegion = new MyDirtyRegion();
private int m_updateDirtyCounter;
private MyCubeSize m_gridSizeEnum;
private Vector3I m_min = Vector3I.MaxValue;
private Vector3I m_max = Vector3I.MinValue;
private readonly Dictionary m_cubes = new Dictionary(1024);
///
/// This caches if grid can have physics, once set to false, it stays false and grid is eventually closed.
///
private bool m_canHavePhysics = true;
private readonly HashSet m_cubeBlocks = new HashSet();
private List m_fatBlocks = new List(100);
private MyLocalityGrouping m_explosions = new MyLocalityGrouping(MyLocalityGrouping.GroupingMode.Overlaps);
public HashSetReader DirtyBlocks { get { return new HashSetReader(m_dirtyRegion.Cubes); } }
public MyCubeGridRenderData RenderData { get { return Render.RenderData; } }
private HashSet m_processedBlocks = new HashSet();
private HashSet m_blocksForDraw = new HashSet();
private List m_tmpGrids = new List();
public HashSet BlocksForDraw { get { return m_blocksForDraw; } }
private bool m_disconnectsDirty;
private bool m_blocksForDamageApplicationDirty;
private bool m_boundsDirty = false;
private int m_lastUpdatedDirtyBounds = 0;
private HashSet m_blocksForDamageApplication = new HashSet();
private List m_blocksForDamageApplicationCopy = new List();
internal MyStructuralIntegrity StructuralIntegrity { get; private set; }
private HashSet m_tmpBuildFailList = new HashSet();
private List m_tmpBuildOffsets = new List();
private List m_tmpBuildSuccessBlocks = new List();
static List m_tmpBlockPositions = new List();
static List m_tmpBlockListReceive = new List();
public bool IsSplit { get; set; }
[ThreadStatic]
private static List m_tmpOccupiedCockpitsPerThread;
[ThreadStatic]
private static List m_tmpBlockGroupsPerThread;
private static List m_tmpOccupiedCockpits { get { return MyUtils.Init(ref m_tmpOccupiedCockpitsPerThread); } }
private static List m_tmpBlockGroups { get { return MyUtils.Init(ref m_tmpBlockGroupsPerThread); } }
public List AdditionalModelGenerators { get { return Render.AdditionalModelGenerators; } }
public bool HasShipSoundEvents = false;
public int NumberOfReactors = 0;
public float GridGeneralDamageModifier = 1f;
internal MyGridSkeleton Skeleton;
public readonly BlockTypeCounter BlockCounter = new BlockTypeCounter();
public MyCubeGridSystems GridSystems { get; private set; }
public Dictionary BlocksCounters = new Dictionary();
private const float m_gizmoMaxDistanceFromCamera = 100.0f;
private const float m_gizmoDrawLineScale = 0.002f;
private bool m_isStatic;
public bool IsStatic
{
get { return m_isStatic; }
private set
{
if (m_isStatic != value)
{
m_isStatic = value;
NotifyIsStaticChanged(m_isStatic);
}
}
}
public float GridSize { get; private set; }
public float GridScale { get; private set; } // PARODY
public float GridSizeHalf { get; private set; }
public Vector3 GridSizeHalfVector { get; private set; }
///
/// Reciprocal of gridsize
///
public float GridSizeR { get; private set; }
public Vector3I Min { get { return m_min; } }
public Vector3I Max { get { return m_max; } }
public Vector3I? XSymmetryPlane = null;
public Vector3I? YSymmetryPlane = null;
public Vector3I? ZSymmetryPlane = null;
public bool XSymmetryOdd = false;
public bool YSymmetryOdd = false;
public bool ZSymmetryOdd = false;
///
/// Indicates if a grid coresponds to a respawn ship/cart.
///
private readonly Sync m_isRespawnGrid;
///
/// Gets or sets indication if a grid coresponds to a respawn ship/cart.
///
public bool IsRespawnGrid { get { return m_isRespawnGrid; } set { m_isRespawnGrid.Value = value; } }
///
/// Grid play time with player. Used by respawn ship.
///
public int m_playedTime;
public bool ControlledFromTurret = false;
private readonly Sync m_destructibleBlocks;
// Used for UI & Sync
public bool DestructibleBlocks
{
get
{
return m_destructibleBlocks;
}
set
{
m_destructibleBlocks.Value = value;
}
}
private Sync m_editable;
//Defines if blocks can be build/removed from this grid
//CubeBuilder reads this flag and controls the building Add/Remove block functions still work
//TODO: unify Add/Remove block calls and integrate this flag, beware destroyed blocks can use RemoveBlock too
public bool Editable
{
get { return m_editable; }
set { m_editable.ValidateAndSet(value); }
}
// Used to determine if blocks are destructible
public bool BlocksDestructionEnabled
{
get
{
if (MySession.Static.Settings.DestructibleBlocks)
{
return m_destructibleBlocks;
}
else
{
return false;
}
}
}
internal List BlockGroups = new List();
internal MyCubeGridOwnershipManager m_ownershipManager;
public Sandbox.Game.Entities.Blocks.MyProjectorBase Projector;
///
/// players that have at least one block in cube grid
///
public List SmallOwners
{
get
{
return m_ownershipManager.SmallOwners;
}
}
///
/// players that have the maximum number of functional blocks in cube grid
///
public List BigOwners
{
get
{
return m_ownershipManager.BigOwners;
}
}
public MyCubeSize GridSizeEnum
{
get { return m_gridSizeEnum; }
set
{
m_gridSizeEnum = value;
GridSize = MyDefinitionManager.Static.GetCubeSize(value);
GridSizeHalf = GridSize / 2;
GridSizeHalfVector = new Vector3(GridSizeHalf);
GridSizeR = 1 / GridSize;
}
}
public new MyGridPhysics Physics { get { return (MyGridPhysics)base.Physics; } set { base.Physics = value; } }
//public int HavokCollisionSystemID { get; private set; }
public event Action OnBlockAdded;
public event Action OnBlockRemoved;
public event Action OnBlockIntegrityChanged;
public event Action OnBlockClosed;
public event Action OnAuthorshipChanged;
internal void NotifyBlockAdded(MySlimBlock block)
{
if (OnBlockAdded != null)
OnBlockAdded(block);
GridSystems.OnBlockAdded(block);
}
internal void NotifyBlockRemoved(MySlimBlock block)
{
if (OnBlockRemoved != null)
OnBlockRemoved(block);
if (MyVisualScriptLogicProvider.BlockDestroyed != null)
MyVisualScriptLogicProvider.BlockDestroyed(block.FatBlock != null ? block.FatBlock.Name : string.Empty, Name, block.BlockDefinition.Id.TypeId.ToString(), block.BlockDefinition.Id.SubtypeName);
MyCubeGrids.NotifyBlockDestroyed(this, block);
GridSystems.OnBlockRemoved(block);
}
internal void NotifyBlockClosed(MySlimBlock block)
{
if (OnBlockClosed != null)
OnBlockClosed(block);
}
internal void NotifyBlockIntegrityChanged(MySlimBlock block)
{
if (OnBlockIntegrityChanged != null)
OnBlockIntegrityChanged(block);
GridSystems.OnBlockIntegrityChanged(block);
}
//Called when ownership recalculation is actually done
public event Action OnBlockOwnershipChanged;
internal void NotifyBlockOwnershipChange(MyCubeGrid cubeGrid)
{
if (OnBlockOwnershipChanged != null)
OnBlockOwnershipChanged(cubeGrid);
GridSystems.OnBlockOwnershipChanged(cubeGrid);
}
public event Action OnIsStaticChanged;
internal void NotifyIsStaticChanged(bool newIsStatic)
{
if (OnIsStaticChanged != null)
OnIsStaticChanged(newIsStatic);
}
public event Action OnGridSplit;
internal event Action AddedToLogicalGroup;
internal event Action RemovedFromLogicalGroup;
public event Action OnHavokSystemIDChanged;//jn: not nice
public static int GridCounter
{
get;
private set;
}
public int BlocksCount { get { return m_cubeBlocks.Count; } }
public HashSet CubeBlocks { get { return m_cubeBlocks; } }
public event Action OnGridChanged;
public bool CreatePhysics;
public void RaiseGridChanged()
{
if (OnGridChanged != null)
{
OnGridChanged(this);
}
}
private static readonly HashSet m_tmpSinks = new HashSet();
private static List m_tmpLocationsAndIdsSend = new List();
private static List> m_tmpLocationsAndIdsReceive = new List>();
private bool m_smallToLargeConnectionsInitialized = false;
internal bool SmallToLargeConnectionsInitialized { get { return m_smallToLargeConnectionsInitialized; } }
private bool m_enableSmallToLargeConnections = true;
internal bool EnableSmallToLargeConnections { get { return m_enableSmallToLargeConnections; } }
// Flag if SI should check connectivity of the grid
// PM: It got more advanced when started to be used to test dynamic generally
internal enum MyTestDynamicReason
{
NoReason,
GridCopied,
GridSplit
}
internal MyTestDynamicReason TestDynamic = MyTestDynamicReason.NoReason;
internal new MyRenderComponentCubeGrid Render
{
get { return (MyRenderComponentCubeGrid)base.Render; }
set { base.Render = value; }
}
// Flag if the world matrix has been changed this frame (flag cleared in UpdateAfterSimulation)
private bool m_worldPositionChanged;
// Flag if the grid has any model generators (for performance reasons)
private bool m_hasAdditionalModelGenerators;
public MyTerminalBlock MainCockpit = null;
public MyTerminalBlock MainRemoteControl = null;
// Cached map from multiblock id to info, not saved.
private Dictionary m_multiBlockInfos;
///
/// Local coord system under which this cube exists. (its id)
///
public long LocalCoordSystem { get; set; }
public bool HasMainCockpit()
{
return MainCockpit != null;
}
public bool IsMainCockpit(MyTerminalBlock cockpit)
{
return MainCockpit == cockpit;
}
public void SetMainCockpit(MyTerminalBlock cockpit)
{
MainCockpit = cockpit;
}
public bool HasMainRemoteControl()
{
return MainRemoteControl != null;
}
public bool IsMainRemoteControl(MyTerminalBlock remoteControl)
{
return MainRemoteControl == remoteControl;
}
public void SetMainRemoteControl(MyTerminalBlock remoteControl)
{
MainRemoteControl = remoteControl;
}
///
/// Return how much fat blocks defined by T is pressent in grid.
///
/// Type of Fatblock
///
public int GetFatBlockCount() where T : MyCubeBlock
{
int count = 0;
foreach (var block in GetFatBlocks())
if (block is T)
count++;
return count;
}
public MyCubeGrid() :
this(MyCubeSize.Large)
{
GridScale = 1;
Render = new MyRenderComponentCubeGrid();
Render.NeedsDraw = true;
PositionComp = new MyCubeGridPosition();
Hierarchy.QueryAABBImpl = QueryAABB;
Hierarchy.QuerySphereImpl = QuerySphere;
Hierarchy.QueryLineImpl = QueryLine;
Components.Add(new MyGridTargeting());
#if !XB1 // !XB1_SYNC_NOREFLECTION
SyncType = SyncHelpers.Compose(this);
#else // XB1
SyncType = new SyncType(new List());
m_handBrakeSync = SyncType.CreateAndAddProp();
m_isRespawnGrid = SyncType.CreateAndAddProp();
m_destructibleBlocks = SyncType.CreateAndAddProp();
#endif // XB1
m_handBrakeSync.ValueChanged += (x)=> HandBrakeChanged();
}
private MyCubeGrid(MyCubeSize gridSize)
{
GridScale = 1;
GridSizeEnum = gridSize;
GridSize = MyDefinitionManager.Static.GetCubeSize(gridSize);
GridSizeHalf = GridSize / 2;
GridSizeHalfVector = new Vector3(GridSizeHalf);
GridSizeR = 1 / GridSize;
NeedsUpdate = MyEntityUpdateEnum.EACH_FRAME | MyEntityUpdateEnum.EACH_10TH_FRAME | MyEntityUpdateEnum.EACH_100TH_FRAME; //because of debug draw
Skeleton = new MyGridSkeleton();
GridCounter++;
AddDebugRenderComponent(new MyDebugRenderComponentCubeGrid(this));
if (MyPerGameSettings.Destruction)
{
OnPhysicsChanged += delegate(MyEntity entity)
{
MyPhysics.RemoveDestructions(entity);
};
}
if (MyFakes.ASSERT_CHANGES_IN_SIMULATION)
{
OnPhysicsChanged += e => Debug.Assert(!MyPhysics.InsideSimulation, "Physics change inside simulation!");
OnGridSplit += (g1, g2) => Debug.Assert(!MyPhysics.InsideSimulation, "Physics change inside simulation!");
}
}
private void CreateSystems()
{
ProfilerShort.Begin("CubeGrid.CreateSystems()");
GridSystems = (MyCubeGridSystems)Activator.CreateInstance(m_gridSystemsType, this);
ProfilerShort.End();
}
#region Overrides
public override void Init(MyObjectBuilder_EntityBase objectBuilder)
{
InitInternal(objectBuilder, rebuildGrid: true);
}
[Conditional("DEBUG")]
private void AssertNonPublicBlock(MyObjectBuilder_CubeBlock block)
{
MyCubeBlockDefinition definition;
var upgBlock = UpgradeCubeBlock(block, out definition);
var compound = upgBlock as MyObjectBuilder_CompoundCubeBlock;
if (compound != null)
{
foreach (var b in compound.Blocks)
{
MyCubeBlockDefinition def;
Debug.Assert(!MyDefinitionManager.Static.TryGetCubeBlockDefinition(b.GetId(), out def) || def.Public || def.IsGeneratedBlock, "Non-public block definition being added to world: " + def.Id);
}
}
else if (definition != null)
{
Debug.Assert(definition.Public, "Non-public block definition being added to world: " + definition.Id);
}
}
[Conditional("DEBUG")]
private void AssertNonPublicBlocks(MyObjectBuilder_CubeGrid builder)
{
foreach (var block in builder.CubeBlocks)
{
AssertNonPublicBlock(block);
}
}
private bool RemoveNonPublicBlock(MyObjectBuilder_CubeBlock block)
{
MyCubeBlockDefinition definition;
var upgBlock = UpgradeCubeBlock(block, out definition);
var compound = upgBlock as MyObjectBuilder_CompoundCubeBlock;
if (compound != null)
{
MyCubeBlockDefinition def;
compound.Blocks = compound.Blocks.Where(s => !MyDefinitionManager.Static.TryGetCubeBlockDefinition(s.GetId(), out def) || def.Public || def.IsGeneratedBlock).ToArray();
return compound.Blocks.Length == 0;
}
else if (definition != null && !definition.Public)
{
return true;
}
return false;
}
private void RemoveNonPublicBlocks(MyObjectBuilder_CubeGrid builder)
{
builder.CubeBlocks.RemoveAll(s => RemoveNonPublicBlock(s));
}
private void InitInternal(MyObjectBuilder_EntityBase objectBuilder, bool rebuildGrid)
{
var lst = new List();
SyncFlag = true;
// parody
var builder = (MyObjectBuilder_CubeGrid)objectBuilder;
if (builder != null)
GridSizeEnum = builder.GridSizeEnum;
GridScale = MyDefinitionManager.Static.GetCubeSize(GridSizeEnum) / MyDefinitionManager.Static.GetCubeSizeOriginal(GridSizeEnum);
base.Init(objectBuilder);
Init(null, null, null, null, null);
m_destructibleBlocks.Value = builder.DestructibleBlocks;
if (MyFakes.ASSERT_NON_PUBLIC_BLOCKS)
AssertNonPublicBlocks(builder);
if (MyFakes.REMOVE_NON_PUBLIC_BLOCKS)
RemoveNonPublicBlocks(builder);
Render.CreateAdditionalModelGenerators(builder != null ? builder.GridSizeEnum : MyCubeSize.Large);
m_hasAdditionalModelGenerators = AdditionalModelGenerators.Count > 0;
CreateSystems();
if (builder != null)
{
IsStatic = builder.IsStatic;
CreatePhysics = builder.CreatePhysics;
m_enableSmallToLargeConnections = builder.EnableSmallToLargeConnections;
GridSizeEnum = builder.GridSizeEnum;
Editable = builder.Editable;
GridSystems.BeforeBlockDeserialization(builder);
m_cubes.Clear();
m_cubeBlocks.Clear();
m_fatBlocks.Clear();
m_tmpOccupiedCockpits.Clear();
for (int i = 0; i 0.0f, "Block is in inconsistent state in grid initialization");
var block = AddBlock(cubeBlock, false);
//Debug.Assert(block != null, "Block was not added");
if (block != null)
{
if (block.FatBlock is MyCompoundCubeBlock)
{
foreach (var b in (block.FatBlock as MyCompoundCubeBlock).GetBlocks())
if (!lst.Contains(b.BlockDefinition.Id))
lst.Add(b.BlockDefinition.Id);
}
else
if (!lst.Contains(block.BlockDefinition.Id))
lst.Add(block.BlockDefinition.Id);
if (block.FatBlock is MyCockpit)
{
var cockpit = block.FatBlock as MyCockpit;
if (cockpit.Pilot != null)
m_tmpOccupiedCockpits.Add(cockpit);
}
}
}
GridSystems.AfterBlockDeserialization();
if (builder.Skeleton != null)
{
// After adding blocks!
Skeleton.Deserialize(builder.Skeleton, GridSize, GridSize);
}
Render.RenderData.SetBasePositionHint(Min * GridSize - GridSize);
if (rebuildGrid)
RebuildGrid();
ProfilerShort.Begin("Block groups");
foreach (var groupBuilder in builder.BlockGroups)
AddGroup(groupBuilder);
ProfilerShort.End();
if (Physics != null)
{
Physics.LinearVelocity = builder.LinearVelocity;
Physics.AngularVelocity = builder.AngularVelocity;
if (!IsStatic)
Physics.Shape.BlocksConnectedToWorld.Clear();
if (MyPerGameSettings.InventoryMass)
m_inventoryMassDirty = true;
}
XSymmetryPlane = builder.XMirroxPlane;
YSymmetryPlane = builder.YMirroxPlane;
ZSymmetryPlane = builder.ZMirroxPlane;
XSymmetryOdd = builder.XMirroxOdd;
YSymmetryOdd = builder.YMirroxOdd;
ZSymmetryOdd = builder.ZMirroxOdd;
GridSystems.Init(builder);
if (builder.DisplayName == null)
DisplayName = MakeCustomName();
else
DisplayName = builder.DisplayName;
if (MyFakes.ENABLE_TERMINAL_PROPERTIES)
{
m_ownershipManager = new MyCubeGridOwnershipManager();
m_ownershipManager.Init(this);
}
if (Hierarchy != null)
Hierarchy.OnChildRemoved += Hierarchy_OnChildRemoved;
}
Render.CastShadows = true;
Render.NeedsResolveCastShadow = false;
if (MyStructuralIntegrity.Enabled)
{
// Requires scene information (checking fixed blocks) so wait until scene is created.
NeedsUpdate |= MyEntityUpdateEnum.BEFORE_NEXT_FRAME;
}
// This is deffered here so that the pilot takes control of the full grid
foreach (var cockpit in m_tmpOccupiedCockpits)
{
cockpit.GiveControlToPilot();
}
m_tmpOccupiedCockpits.Clear();
//UpdateDirty();
if (MyFakes.ENABLE_MULTIBLOCK_PART_IDS)
{
PrepareMultiBlockInfos();
}
IsRespawnGrid = builder.IsRespawnGrid;
m_playedTime = builder.playedTime;
GridGeneralDamageModifier = builder.GridGeneralDamageModifier;
LocalCoordSystem = builder.LocalCoordSys;
}
void Hierarchy_OnChildRemoved(IMyEntity obj)
{
m_fatBlocks.Remove(obj as MyCubeBlock);
}
private static MyCubeGrid CreateForSplit(MyCubeGrid originalGrid, long newEntityId)
{
var builder = MyObjectBuilderSerializer.CreateNewObject(typeof(MyObjectBuilder_CubeGrid)) as MyObjectBuilder_CubeGrid;
if (builder == null)
{
Debug.Fail("CreateForSplit builder shouldn't be null! Original Grid info: " + originalGrid.ToString());
MyLog.Default.WriteLine("CreateForSplit builder shouldn't be null! Original Grid info: " + originalGrid.ToString());
return null;
}
builder.EntityId = newEntityId;
builder.GridSizeEnum = originalGrid.GridSizeEnum;
builder.IsStatic = originalGrid.IsStatic;
builder.PersistentFlags = originalGrid.Render.PersistentFlags;
builder.PositionAndOrientation = new MyPositionAndOrientation(originalGrid.WorldMatrix);
// We have to call init manually to prevent the grid from being rebuilt with zero blocks, which would immediately mark it for close
var newGrid = MyEntities.CreateFromObjectBuilderNoinit(builder) as MyCubeGrid;
if (newGrid == null) return null;
newGrid.InitInternal(builder, rebuildGrid: false);
return newGrid;
}
// RemoveSplit and CreateSplit should behave the same for the old grid. They only differ in the fact that
// CreateSplit creates a new grid from the removed blocks, whereas the first method only removes the blocks
// from the old grid (this is used for splits without physics - e.g. interior lights)
public static void RemoveSplit(MyCubeGrid originalGrid, List blocks, int offset, int count, bool sync = true)
{
ProfilerShort.Begin("RemoveBlockInternal");
for (int i = offset; i < offset + count; i++)
{
if (blocks.Count <= i)
{
continue;
}
var block = blocks[i];
if (block == null)
continue;
if (block.FatBlock != null)
originalGrid.Hierarchy.RemoveChild(block.FatBlock);
bool oldEnabled = originalGrid.EnableGenerators(false, true);
originalGrid.RemoveBlockInternal(block, close: true, markDirtyDisconnects: false);
originalGrid.EnableGenerators(oldEnabled, true);
originalGrid.Physics.AddDirtyBlock(block);
}
ProfilerShort.End();
originalGrid.RemoveEmptyBlockGroups();
if (sync == true)
{
Debug.Assert(Sync.IsServer);
if (!Sync.IsServer) return;
originalGrid.AnnounceRemoveSplit(blocks);
return;
}
}
public MyCubeGrid SplitByPlane(PlaneD plane)
{
m_tmpSlimBlocks.Clear();
MyCubeGrid grid = null;
PlaneD localPlane = PlaneD.Transform(plane, PositionComp.WorldMatrixNormalizedInv);
foreach (var block in GetBlocks())
{
BoundingBoxD box = new BoundingBoxD(block.Min * GridSize, block.Max * GridSize);
box.Inflate(GridSize/2);
if (box.Intersects(localPlane) == PlaneIntersectionType.Back)
m_tmpSlimBlocks.Add(block);
}
if (m_tmpSlimBlocks.Count != 0)
{
grid = CreateSplit(this, m_tmpSlimBlocks);
m_tmpSlimBlocks.Clear();
}
return grid;
}
public static MyCubeGrid CreateSplit(MyCubeGrid originalGrid, List blocks, bool sync = true, long newEntityId = 0)
{
ProfilerShort.Begin("Init grid");
var newGrid = MyCubeGrid.CreateForSplit(originalGrid, newEntityId);
ProfilerShort.End();
if (newGrid == null) return null;
Vector3 oldCenterOfMass = originalGrid.Physics.CenterOfMassWorld;
MyEntities.Add(newGrid);
MyCubeGrid.MoveBlocks(originalGrid, newGrid, blocks, 0, blocks.Count);
newGrid.RebuildGrid();
if (!newGrid.IsStatic)
{
newGrid.SetInventoryMassDirty();
newGrid.Physics.UpdateMass();
}
if (originalGrid.IsStatic)
{
newGrid.TestDynamic = MyCubeGrid.MyTestDynamicReason.GridSplit;
//GR: Always testing dynamic for original grid (can be any of the 2 grids)
//if (!MySession.Static.EnableConvertToStation)
originalGrid.TestDynamic = MyCubeGrid.MyTestDynamicReason.GridSplit;
}
newGrid.Physics.AngularVelocity = originalGrid.Physics.AngularVelocity;
newGrid.Physics.LinearVelocity = originalGrid.Physics.GetVelocityAtPoint(newGrid.Physics.CenterOfMassWorld);
// CH: TODO: (Optimization) recalculate the original grid only when all splits are done. This will have to be synced by extra message
originalGrid.UpdatePhysicsShape();
if (!originalGrid.IsStatic)
{
originalGrid.SetInventoryMassDirty();
originalGrid.Physics.UpdateMass();
}
Vector3 velocityAtNewCOM = Vector3.Cross(originalGrid.Physics.AngularVelocity, originalGrid.Physics.CenterOfMassWorld - oldCenterOfMass);
originalGrid.Physics.LinearVelocity = originalGrid.Physics.LinearVelocity + velocityAtNewCOM;
if (originalGrid.OnGridSplit != null)
{
originalGrid.OnGridSplit(originalGrid, newGrid);
}
if (sync == true)
{
Debug.Assert(Sync.IsServer);
if (!Sync.IsServer) return newGrid;
m_tmpBlockPositions.Clear();
foreach (var block in blocks)
{
m_tmpBlockPositions.Add(block.Position);
}
MyMultiplayer.RemoveForClientIfIncomplete(originalGrid);
MyMultiplayer.RaiseEvent(originalGrid, x => x.CreateSplit_Implementation, m_tmpBlockPositions, newGrid.EntityId);
}
return newGrid;
}
[Event, Reliable, Broadcast]
public void CreateSplit_Implementation(List blocks, long newEntityId)
{
m_tmpBlockListReceive.Clear();
foreach (var position in blocks)
{
var block = GetCubeBlock(position);
Debug.Assert(block != null, "Block was null when trying to create a grid split. Desync?");
if (block == null)
{
MySandboxGame.Log.WriteLine("Block was null when trying to create a grid split. Desync?");
continue;
}
m_tmpBlockListReceive.Add(block);
}
MyCubeGrid.CreateSplit(this, m_tmpBlockListReceive, sync: false, newEntityId: newEntityId);
m_tmpBlockListReceive.Clear();
}
///
/// SplitBlocks list can contain null when received from network
///
public static void CreateSplits(MyCubeGrid originalGrid, List splitBlocks, List groups, bool sync = true)
{
if (originalGrid == null || originalGrid.Physics == null || groups == null || splitBlocks == null)
{
return;
}
Vector3 oldCenterOfMass = originalGrid.Physics.CenterOfMassWorld;
try
{
if (MyCubeGridSmallToLargeConnection.Static != null)
{
ProfilerShort.Begin("BeforeGridSplit_SmallToLargeGridConnetivity");
MyCubeGridSmallToLargeConnection.Static.BeforeGridSplit_SmallToLargeGridConnectivity(originalGrid);
ProfilerShort.End();
}
// Create new grids, move blocks
ProfilerShort.Begin("Create grids and move");
var array = groups.GetInternalArray();
for (int i = 0; i < groups.Count; i++)
{
CreateSplitForGroup(originalGrid, splitBlocks, ref array[i]);
}
ProfilerShort.End();
// Update old grid shape
ProfilerShort.Begin("Update original grid shape");
originalGrid.UpdatePhysicsShape();
ProfilerShort.End();
// Rebuild new grids
foreach (var newGrid in originalGrid.m_tmpGrids)
{
ProfilerShort.Begin("Update new grid shape");
newGrid.RebuildGrid();
if (originalGrid.IsStatic)
{
if (!MySession.Static.Settings.StationVoxelSupport)
{
newGrid.TestDynamic = MyCubeGrid.MyTestDynamicReason.GridSplit;
originalGrid.TestDynamic = MyCubeGrid.MyTestDynamicReason.GridSplit;
}
}
newGrid.Physics.AngularVelocity = originalGrid.Physics.AngularVelocity;
newGrid.Physics.LinearVelocity = originalGrid.Physics.GetVelocityAtPoint(newGrid.Physics.CenterOfMassWorld);
ProfilerShort.End();
}
// Update old grid velocity
Vector3 velocityAtNewCOM = Vector3.Cross(originalGrid.Physics.AngularVelocity, originalGrid.Physics.CenterOfMassWorld - oldCenterOfMass);
originalGrid.Physics.LinearVelocity = originalGrid.Physics.LinearVelocity + velocityAtNewCOM;
if (MyCubeGridSmallToLargeConnection.Static != null)
{
ProfilerShort.Begin("AfterGridSplit_SmallToLargeGridConnetivity");
MyCubeGridSmallToLargeConnection.Static.AfterGridSplit_SmallToLargeGridConnectivity(originalGrid, originalGrid.m_tmpGrids);
ProfilerShort.End();
}
var handler = originalGrid.OnGridSplit;
if (handler != null)
{
ProfilerShort.Begin("Handle OnGridSplit");
foreach (var newGrid in originalGrid.m_tmpGrids)
{
handler(originalGrid, newGrid);
}
ProfilerShort.End();
}
foreach (var newGrid in originalGrid.m_tmpGrids)
{
newGrid.GridSystems.UpdatePower();
}
if (sync)
{
Debug.Assert(Sync.IsServer);
if (!Sync.IsServer) return;
MyMultiplayer.RemoveForClientIfIncomplete(originalGrid);
m_tmpBlockPositions.Clear();
foreach (var block in splitBlocks)
{
m_tmpBlockPositions.Add(block.Position);
}
MyMultiplayer.RaiseEvent(originalGrid, x => x.CreateSplits_Implementation, m_tmpBlockPositions, groups);
foreach (var newGrid in originalGrid.m_tmpGrids)
{
newGrid.IsSplit = true;
MyMultiplayer.ReplicateImmediatelly(MyExternalReplicable.FindByObject(newGrid), MyExternalReplicable.FindByObject(originalGrid));
newGrid.IsSplit = false;
}
}
}
finally
{
originalGrid.m_tmpGrids.Clear();
}
}
[Event, Reliable, Broadcast]
public void CreateSplits_Implementation(List blocks, List groups)
{
// Its already to late. Server send info he wants this to be closed so there is no need to process.
// Its not perfect solution, but due to the fact that close can come before split, there is no other choice for now.
if (this.MarkedForClose)
return;
m_tmpBlockListReceive.Clear();
// groups should start from index 0 and they should not have any holes in indexes
Debug.Assert(groups[0].FirstBlockIndex == 0 && blocks.Count == groups[groups.Count - 1].FirstBlockIndex + groups[groups.Count - 1].BlockCount);
for (int gr = 0; gr < groups.Count; gr++)
{
var group = groups[gr];
int groupBlockCount = group.BlockCount;
for (int i = group.FirstBlockIndex; i < group.FirstBlockIndex + group.BlockCount; i++)
{
var block = GetCubeBlock(blocks[i]);
Debug.Assert(block != null, "Block was null when trying to create a grid split. Desync?");
if (block == null)
{
MySandboxGame.Log.WriteLine("Block was null when trying to create a grid split. Desync?");
// block is missing - some desync
--groupBlockCount;
if (groupBlockCount == 0)
group.IsValid = false;
}
// Note null block can be added (to avoid changing of group's FirstBlockIndex and BlockCount when block is not found)!
m_tmpBlockListReceive.Add(block);
}
groups[gr] = group;
}
Debug.Assert(blocks.Count == m_tmpBlockListReceive.Count);
//foreach (var position in blocks)
//{
// var block = GetCubeBlock(position);
// Debug.Assert(block != null, "Block was null when trying to create a grid split. Desync?");
// if (block == null)
// {
// MySandboxGame.Log.WriteLine("Block was null when trying to create a grid split. Desync?");
// continue;
// }
// m_tmpBlockListReceive.Add(block);
//}
MyCubeGrid.CreateSplits(this, m_tmpBlockListReceive, groups, false);
m_tmpBlockListReceive.Clear();
}
private static void CreateSplitForGroup(MyCubeGrid originalGrid, List splitBlocks, ref MyDisconnectHelper.Group group)
{
// In voxels test
if (!originalGrid.IsStatic && Sync.IsServer && group.IsValid)
{
ProfilerShort.Begin("IsInVoxels");
int inVoxels = 0;
for (int bi = group.FirstBlockIndex; bi < group.FirstBlockIndex + group.BlockCount; bi++)
{
if (MyDisconnectHelper.IsDestroyedInVoxels(splitBlocks[bi]))
{
inVoxels++;
if ((inVoxels / (float)group.BlockCount) > 0.4f) // 40% of blocks in voxels
{
group.IsValid = false;
break;
}
}
}
ProfilerShort.End();
}
// Can have physics test
group.IsValid = group.IsValid && CanHavePhysics(splitBlocks, group.FirstBlockIndex, group.BlockCount);
Debug.Assert(Sync.IsServer || !(group.IsValid && group.EntityId == 0), "Invalid split entity id");
if (group.BlockCount == 1 && splitBlocks.Count > group.FirstBlockIndex && splitBlocks[group.FirstBlockIndex] != null)
{
var firstBlock = splitBlocks[group.FirstBlockIndex];
if (firstBlock.FatBlock is MyFracturedBlock)
{
group.IsValid = false;
if (Sync.IsServer)
MyDestructionHelper.CreateFracturePiece(firstBlock.FatBlock as MyFracturedBlock, true);
}
else if (firstBlock.FatBlock != null && firstBlock.FatBlock.Components.Has())
{
group.IsValid = false;
if (Sync.IsServer)
{
var component = firstBlock.GetFractureComponent();
if (component != null)
MyDestructionHelper.CreateFracturePiece(component, true);
}
}
else if (firstBlock.FatBlock is MyCompoundCubeBlock)
{
var compound = firstBlock.FatBlock as MyCompoundCubeBlock;
bool allFractureComponent = true;
foreach (var blockInCompound in compound.GetBlocks())
{
allFractureComponent &= blockInCompound.FatBlock.Components.Has();
if (!allFractureComponent)
break;
}
if (allFractureComponent)
{
group.IsValid = false;
if (Sync.IsServer)
{
foreach (var blockInCompound in compound.GetBlocks())
{
var component = blockInCompound.GetFractureComponent();
if (component != null)
MyDestructionHelper.CreateFracturePiece(component, true);
}
}
}
}
}
if (group.IsValid)
{
ProfilerShort.Begin("Init grid");
var newGrid = MyCubeGrid.CreateForSplit(originalGrid, group.EntityId);
newGrid.DebugCreatedBy = MyEventContext.Current.IsLocallyInvoked ? DebugCreatedBy.LocalSplit : DebugCreatedBy.ServerSplit;
ProfilerShort.End();
if (newGrid != null)
{
ProfilerShort.Begin("Move blocks");
originalGrid.m_tmpGrids.Add(newGrid);
MyEntities.Add(newGrid);
MyCubeGrid.MoveBlocks(originalGrid, newGrid, splitBlocks, group.FirstBlockIndex, group.BlockCount);
group.EntityId = newGrid.EntityId;
ProfilerShort.End();
if (newGrid.IsStatic && Sync.IsServer)
{
MatrixD worldMat = newGrid.WorldMatrix;
bool result = MyCoordinateSystem.Static.IsLocalCoordSysExist(ref worldMat, newGrid.GridSize);
if (newGrid.GridSizeEnum == MyCubeSize.Large)
{
if (result)
{
MyCoordinateSystem.Static.RegisterCubeGrid(newGrid);
}
else
{
MyCoordinateSystem.Static.CreateCoordSys(newGrid, MyCubeBuilder.CubeBuilderDefinition.BuildingSettings.StaticGridAlignToCenter, true);
}
}
}
}
else
{
Debug.Fail("Split grid not created for unknown reason");
group.IsValid = false;
}
}
if (!group.IsValid)
{
RemoveSplit(originalGrid, splitBlocks, group.FirstBlockIndex, group.BlockCount, false);
}
Debug.Assert(!(group.IsValid && group.EntityId == 0), "Entity id is 0, but grid is marked as valid");
}
private void AddGroup(MyObjectBuilder_BlockGroup groupBuilder)
{
if (groupBuilder.Blocks.Count == 0)
return;
MyBlockGroup group = new MyBlockGroup(this);
group.Init(groupBuilder);
BlockGroups.Add(group);
}
public override MyObjectBuilder_EntityBase GetObjectBuilder(bool copy = false)
{
MyObjectBuilder_CubeGrid ob = (MyObjectBuilder_CubeGrid)base.GetObjectBuilder(copy);
GetObjectBuilderInternal(ob, copy);
return ob;
}
private void GetObjectBuilderInternal(MyObjectBuilder_CubeGrid ob, bool copy)
{
ob.GridSizeEnum = GridSizeEnum;
if (ob.Skeleton == null)
ob.Skeleton = new List();
ob.Skeleton.Clear();
Skeleton.Serialize(ob.Skeleton, GridSize, this);
ob.IsStatic = IsStatic;
ob.Editable = Editable;
ob.CubeBlocks.Clear();
foreach (var block in m_cubeBlocks)
{
MyObjectBuilder_CubeBlock objectBuilder = null;
if (copy)
objectBuilder = (MyObjectBuilder_CubeBlock)block.GetCopyObjectBuilder();
else
objectBuilder = (MyObjectBuilder_CubeBlock)block.GetObjectBuilder();
Debug.Assert(objectBuilder != null, "Could not get object builder for cube block");
if (objectBuilder != null)
ob.CubeBlocks.Add(objectBuilder);
}
System.Diagnostics.Debug.Assert(ob.CubeBlocks.Count > 0);
ob.PersistentFlags = this.Render.PersistentFlags;
if (Physics != null)
{
ob.LinearVelocity = Physics.LinearVelocity;
ob.AngularVelocity = Physics.AngularVelocity;
}
else
{
ob.LinearVelocity = Vector3.Zero;
ob.AngularVelocity = Vector3.Zero;
}
ob.XMirroxPlane = XSymmetryPlane;
ob.YMirroxPlane = YSymmetryPlane;
ob.ZMirroxPlane = ZSymmetryPlane;
ob.XMirroxOdd = XSymmetryOdd;
ob.YMirroxOdd = YSymmetryOdd;
ob.ZMirroxOdd = ZSymmetryOdd;
if(copy)
ob.Name = null;
ob.BlockGroups.Clear();
foreach (var group in BlockGroups)
{
ob.BlockGroups.Add(group.GetObjectBuilder());
}
ob.DisplayName = DisplayName;
ob.DestructibleBlocks = DestructibleBlocks;
ob.IsRespawnGrid = IsRespawnGrid;
ob.playedTime = m_playedTime;
ob.GridGeneralDamageModifier = GridGeneralDamageModifier;
ob.LocalCoordSys = LocalCoordSystem;
GridSystems.GetObjectBuilder(ob);
}
internal void HavokSystemIDChanged(int id)
{
if (OnHavokSystemIDChanged != null)
OnHavokSystemIDChanged(id);
}
private void UpdatePhysicsShape()
{
Physics.UpdateShape();
}
private void UpdateGravity()
{
ProfilerShort.Begin("Gravity");
if (IsStatic == false && Physics != null && Physics.Enabled && !Physics.IsWelded)
{
if (Physics.DisableGravity <= 0)
{
RecalculateGravity();
}
else
Physics.DisableGravity--;
}
ProfilerShort.End();
}
public override void UpdateOnceBeforeFrame()
{
if (MyStructuralIntegrity.Enabled)
{
CreateStructuralIntegrity();
}
base.UpdateOnceBeforeFrame();
if (MyFakes.ENABLE_GRID_SYSTEM_UPDATE || MyFakes.ENABLE_GRID_SYSTEM_ONCE_BEFORE_FRAME_UPDATE)
{
GridSystems.UpdateOnceBeforeFrame();
}
ActivatePhysics();
}
public void CreateStructuralIntegrity()
{
if (m_gridSizeEnum == MyCubeSize.Large)
{
StructuralIntegrity = new MyStructuralIntegrity(this);
}
}
public void CloseStructuralIntegrity()
{
if (StructuralIntegrity != null)
{
StructuralIntegrity.Close();
StructuralIntegrity = null;
}
}
public override void UpdateBeforeSimulation()
{
MySimpleProfiler.Begin("Grid");
if (MyFakes.ENABLE_GRID_SYSTEM_UPDATE)
{
ProfilerShort.Begin("Grid systems");
GridSystems.UpdateBeforeSimulation();
ProfilerShort.End();
}
ProfilerShort.Begin("Update generators");
if (m_hasAdditionalModelGenerators)
{
// Generators must be updated before grid splits.
foreach (var generator in AdditionalModelGenerators)
generator.UpdateBeforeSimulation();
}
ProfilerShort.BeginNextBlock("Lazy updates");
DoLazyUpdates();
ProfilerShort.BeginNextBlock("Base update");
base.UpdateBeforeSimulation();
if (Physics != null)
{
UpdatePhysicsShape();
}
//MyRenderProxy.DebugDrawAxis(PositionComp.WorldMatrix, 500, false);
//MyRenderProxy.DebugDrawText3D(PositionComp.WorldMatrix.Translation, (PositionComp.WorldMatrix.Translation - MySector.MainCamera.Position).Length().ToString(), Color.White, 0.7f, false);
ProfilerShort.End();
MySimpleProfiler.End("Grid");
}
protected static float GetLineWidthForGizmo(IMyGizmoDrawableObject block, BoundingBox box)
{
//we don't want to gizmo line be smaller in distance, so we need to adjust line width according to distance from camera
float minDistance = m_gizmoMaxDistanceFromCamera;
foreach (var corner in box.Corners)
{
minDistance = (float)Math.Min(minDistance, Math.Abs(World.MySector.MainCamera.GetDistanceFromPoint(Vector3.Transform(block.GetPositionInGrid() + corner, block.GetWorldMatrix()))));
}
Vector3 FieldSize = box.Max - box.Min;
//draw transparent box adjusts witdh based on box size, we don't want this, so we need to inverse this operation
float minFieldSize = MathHelper.Max(1, MathHelper.Min(MathHelper.Min(FieldSize.X, FieldSize.Y), FieldSize.Z));
return (minDistance * m_gizmoDrawLineScale) / minFieldSize;
}
public bool IsGizmoDrawingEnabled()
{
return ShowSenzorGizmos || ShowGravityGizmos || ShowAntennaGizmos;
}
public override void PrepareForDraw()
{
ProfilerShort.Begin("Base");
base.PrepareForDraw();
ProfilerShort.End();
if (m_dirtyRegion.IsDirty)
{
ProfilerShort.Begin("Update dirty");
// Never update more often than every 10th frame. Otherwise gatling guns can force update every frame causing performance issues. Tweak if necessary
if (m_updateDirtyCounter <= 0)
{
UpdateDirty();
m_updateDirtyCounter = 10;
}
ProfilerShort.End();
}
if (m_updateDirtyCounter > 0)
{
m_updateDirtyCounter--;
}
ProfilerShort.Begin("Grid systems - PrepareForDraw");
GridSystems.PrepareForDraw();
ProfilerShort.End();
//gravity and senzor gizmos needs to be drawn even when object / parent grid is not visible
ProfilerShort.Begin("Gizmo");
if (IsGizmoDrawingEnabled())
{
foreach (var block in m_cubeBlocks)
{
if ((block.FatBlock is IMyGizmoDrawableObject))
{
DrawObjectGizmo(block);
}
}
}
ProfilerShort.End();
}
private static void DrawObjectGizmo(MySlimBlock block)
{
IMyGizmoDrawableObject drawObject = block.FatBlock as IMyGizmoDrawableObject;
if (drawObject.CanBeDrawed())
{
Color gizmoColor = drawObject.GetGizmoColor();
MatrixD worldMatrix = drawObject.GetWorldMatrix();
BoundingBox? gizmoBoundingBox = drawObject.GetBoundingBox();
if (null != gizmoBoundingBox)
{
float gizmoLineWidth = GetLineWidthForGizmo(drawObject, gizmoBoundingBox.Value);
BoundingBoxD box = (BoundingBoxD)gizmoBoundingBox.Value;
MySimpleObjectDraw.DrawTransparentBox(ref worldMatrix, ref box, ref gizmoColor, MySimpleObjectRasterizer.SolidAndWireframe, 1, gizmoLineWidth);
}
else
{
float radius = drawObject.GetRadius();
float distanceToObject = (float)MySector.MainCamera.GetDistanceFromPoint(worldMatrix.Translation);
float gizmoDistanceToCamera = (float)(radius - MySector.MainCamera.GetDistanceFromPoint(worldMatrix.Translation));
float thickness = m_gizmoDrawLineScale * Math.Min(m_gizmoMaxDistanceFromCamera, Math.Abs(gizmoDistanceToCamera));
int projectionId = -1;
MySimpleObjectDraw.DrawTransparentSphere(ref worldMatrix, radius, ref gizmoColor, MySimpleObjectRasterizer.SolidAndWireframe, 20, null, null, thickness, projectionId);
if (drawObject.EnableLongDrawDistance() && MyFakes.ENABLE_LONG_DISTANCE_GIZMO_DRAWING)
{
MyBillboardViewProjection viewProjection;
viewProjection.CameraPosition = MySector.MainCamera.Position;
viewProjection.View = MySector.MainCamera.ViewMatrix;
viewProjection.ViewAtZero = default(Matrix);
viewProjection.Viewport = MySector.MainCamera.Viewport;
viewProjection.DepthRead = true;
float aspectRatio = viewProjection.Viewport.Width / viewProjection.Viewport.Height;
viewProjection.Projection = Matrix.CreatePerspectiveFieldOfView(MySector.MainCamera.FieldOfView, aspectRatio, 1, 100);
viewProjection.Projection.M33 = -1;
viewProjection.Projection.M34 = -1;
viewProjection.Projection.M43 = 0;
viewProjection.Projection.M44 = 0;
projectionId = 10;
VRageRender.MyRenderProxy.AddBillboardViewProjection(projectionId, viewProjection);
MySimpleObjectDraw.DrawTransparentSphere(ref worldMatrix, radius, ref gizmoColor, MySimpleObjectRasterizer.SolidAndWireframe, 20, null, null, thickness, projectionId);
}
}
}
}
public override void UpdateBeforeSimulation10()
{
MySimpleProfiler.Begin("Grid");
base.UpdateBeforeSimulation10();
if (MyFakes.ENABLE_GRID_SYSTEM_UPDATE)
{
GridSystems.UpdateBeforeSimulation10();
}
MySimpleProfiler.End("Grid");
}
public override void UpdateBeforeSimulation100()
{
MySimpleProfiler.Begin("Grid");
base.UpdateBeforeSimulation100();
if (MyFakes.ENABLE_GRID_SYSTEM_UPDATE)
{
GridSystems.UpdateBeforeSimulation100();
}
MySimpleProfiler.End("Grid");
}
bool m_inventoryMassDirty;
internal void SetInventoryMassDirty()
{
m_inventoryMassDirty = true;
}
public int GetCurrentMass(MyCharacter pilot = null)
{
int baseMass;
return GetCurrentMass(out baseMass, pilot);
}
public int GetCurrentMass(out int baseMass, MyCharacter pilot = null)
{
baseMass = 0;
int currentMass = 0;
var physicalGroup = MyCubeGridGroups.Static.Physical.GetGroup(this);
if (physicalGroup != null)
{
foreach (var node in physicalGroup.Nodes)
{
var cubeGrid = node.NodeData;
if (cubeGrid == null || cubeGrid.Physics == null || cubeGrid.Physics.Shape == null)
continue;
var massProperties = cubeGrid.Physics.Shape.MassProperties;
var baseMassProperties = cubeGrid.Physics.Shape.BaseMassProperties;
if (IsStatic || !massProperties.HasValue || !baseMassProperties.HasValue)
continue;
float gridTotalMass = massProperties.Value.Mass;
float gridBaseMass = baseMassProperties.Value.Mass;
float inventoryMass = gridTotalMass - gridBaseMass;
baseMass += (int)gridBaseMass;
currentMass += (int)(gridBaseMass + inventoryMass * MySession.Static.Settings.InventorySizeMultiplier - (pilot != null ? pilot.BaseMass : 0.0f));
}
}
return currentMass;
}
public override void UpdateAfterSimulation100()
{
MySimpleProfiler.Begin("Grid");
base.UpdateAfterSimulation100();
//trash removal outside world doesnt work in update before simulation becaouse m_worldPositionChanged is set during simulation and unset
//in update after so it was allways false in update before
UpdateGravity();
if (MyFakes.ENABLE_BOUNDINGBOX_SHRINKING && m_boundsDirty)
{
var elapsedTime = MySandboxGame.TotalTimeInMilliseconds - m_lastUpdatedDirtyBounds;
if (elapsedTime > 30000)
{
ProfilerShort.Begin("Recalculate bounds lazily");
RecalcBounds();
m_boundsDirty = false;
m_lastUpdatedDirtyBounds = MySandboxGame.TotalTimeInMilliseconds;
if (GridSystems.GasSystem != null)
GridSystems.GasSystem.OnCubeGridShrinked();
ProfilerShort.End();
}
}
if (MyFakes.ENABLE_GRID_SYSTEM_UPDATE)
{
ProfilerShort.Begin("GridSystems");
GridSystems.UpdateAfterSimulation100();
ProfilerShort.End();
}
MySimpleProfiler.End("Grid");
}
public override void UpdateAfterSimulation()
{
MySimpleProfiler.Begin("Grid");
base.UpdateAfterSimulation();
ProfilerShort.Begin("Update Shape");
//welding mass props debug
//if(MySession.Static.ControlledEntity.Entity.GetTopMostParent() == this)
//{
// MyRenderProxy.DebugDrawText2D(new Vector2(300, 300), Physics.RigidBody.InverseInertiaTensor.Scale.ToString(), Color.White, 0.75f);
// MyRenderProxy.DebugDrawText2D(new Vector2(300, 330), Physics.RigidBody.InertiaTensor.Scale.ToString(), Color.White, 0.75f);
// MyRenderProxy.DebugDrawText2D(new Vector2(300, 360), Physics.RigidBody.Mass.ToString(), Color.White, 0.75f);
// var pos = new Vector2(300, 380);
// MyRenderProxy.DebugDrawText2D(pos, Physics.WeldInfo.MassElement.Properties.InertiaTensor.Scale.ToString(), Color.White, 0.75f);
// pos.Y += 20;
// foreach(var child in Physics.WeldInfo.Children)
// {
// MyRenderProxy.DebugDrawText2D(pos, child.WeldInfo.MassElement.Properties.InertiaTensor.Scale.ToString(), Color.White, 0.75f);
// pos.Y += 20;
// }
//}
if (m_hasAdditionalModelGenerators)
{
foreach (var generator in AdditionalModelGenerators)
generator.UpdateAfterSimulation();
}
SendRemovedBlocks();
SendRemovedBlocksWithIds();
if (!CanHavePhysics())
{
m_worldPositionChanged = false;
if (Sync.IsServer)
Close();
ProfilerShort.End();
MySimpleProfiler.End("Grid");
return;
}
if (Sync.IsServer)
{
if (MyFakes.ENABLE_FRACTURE_COMPONENT)
{
if (Physics != null)
{
if (Physics.GetFractureBlockComponents().Count > 0)
{
try
{
foreach (var info in Physics.GetFractureBlockComponents())
CreateFractureBlockComponent(info);
}
finally
{
Physics.ClearFractureBlockComponents();
}
}
Physics.CheckLastDestroyedBlockFracturePieces();
}
}
else
{
if (Physics != null && Physics.GetFracturedBlocks().Count > 0)
{
bool oldEnabled = EnableGenerators(false);
foreach (var info in Physics.GetFracturedBlocks())
{
CreateFracturedBlock(info);
}
EnableGenerators(oldEnabled);
}
}
}
StepStructuralIntegrity();
if (TestDynamic != MyTestDynamicReason.NoReason)
{
if (!MyCubeGrid.ShouldBeStatic(this, TestDynamic) && IsStatic)
{
ConvertToDynamic();
}
TestDynamic = MyCubeGrid.MyTestDynamicReason.NoReason;
}
DoLazyUpdates();
if (Physics != null && Physics.Enabled)
{
if (!Physics.IsWelded)
{
Physics.RigidBody.Gravity = m_gravity;
}
if (m_inventoryMassDirty)
{
m_inventoryMassDirty = false;
Physics.Shape.UpdateMassFromInventories(m_cubeBlocks, Physics); // TODO: MK: Store inventory blocks in cubegrid and only check those
}
if (Physics.RigidBody2 != null)
{
/*
//No need, we use ApplyHardKeyFrame utility
if (Physics.RigidBody2.LinearVelocity != Physics.RigidBody.LinearVelocity)
Physics.RigidBody2.LinearVelocity = Physics.RigidBody.LinearVelocity;
if (Physics.RigidBody2.AngularVelocity != Physics.RigidBody.AngularVelocity)
Physics.RigidBody2.AngularVelocity = Physics.RigidBody.AngularVelocity;
*/
if (Physics.RigidBody2.CenterOfMassLocal != Physics.RigidBody.CenterOfMassLocal)
Physics.RigidBody2.CenterOfMassLocal = Physics.RigidBody.CenterOfMassLocal;
}
}
m_worldPositionChanged = false;
ProfilerShort.End();
MySimpleProfiler.End("Grid");
}
private void CreateFractureBlockComponent(MyFractureComponentBase.Info info)
{
if (info.Entity.MarkedForClose)
return;
Debug.Assert(!info.Entity.Components.Has());
MyFractureComponentCubeBlock fractureComponent = new MyFractureComponentCubeBlock();
info.Entity.Components.Add(fractureComponent);
fractureComponent.SetShape(info.Shape, info.Compound);
if (Sync.IsServer)
{
MyCubeBlock cubeBlock = info.Entity as MyCubeBlock;
if (cubeBlock != null)
{
MyCubeGridSmallToLargeConnection.Static.RemoveBlockSmallToLargeConnection(cubeBlock.SlimBlock);
var existingSlimBlock = cubeBlock.CubeGrid.GetCubeBlock(cubeBlock.Position);
MyCompoundCubeBlock compoundBlock = existingSlimBlock != null ? existingSlimBlock.FatBlock as MyCompoundCubeBlock : null;
if (compoundBlock != null)
{
ushort? compoundId = compoundBlock.GetBlockId(cubeBlock.SlimBlock);
if (compoundId != null)
{
var builder = (MyObjectBuilder_FractureComponentBase)fractureComponent.Serialize();
MySyncDestructions.CreateFractureComponent(cubeBlock.CubeGrid.EntityId, cubeBlock.Position, compoundId.Value, builder);
}
}
else
{
var builder = (MyObjectBuilder_FractureComponentBase)fractureComponent.Serialize();
MySyncDestructions.CreateFractureComponent(cubeBlock.CubeGrid.EntityId, cubeBlock.Position, 0xFFFF, builder);
}
cubeBlock.SlimBlock.ApplyDestructionDamage(fractureComponent.GetIntegrityRatioFromFracturedPieceCounts());
}
}
}
private void StepStructuralIntegrity()
{
if (!MyStructuralIntegrity.Enabled || Physics == null || Physics.HavokWorld == null)
{
return;
}
ProfilerShort.Begin("MyCubeGrid.StructuralIntegrity.Update");
if (StructuralIntegrity == null)
CreateStructuralIntegrity();
if (StructuralIntegrity != null)
{
StructuralIntegrity.Update(VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS);
}
ProfilerShort.End();
}
public void ResetStructuralIntegrity()
{
if (StructuralIntegrity != null)
{
StructuralIntegrity = null;
}
}
internal void RemoveGroup(MyBlockGroup group)
{
BlockGroups.Remove(group);
GridSystems.RemoveGroup(group);
}
internal void RemoveGroupByName(string name)
{
int index = BlockGroups.FindIndex(g => g.Name.CompareTo(name) == 0);
if (index == -1)
return;
MyBlockGroup group = BlockGroups[index];
BlockGroups.RemoveAt(index);
GridSystems.RemoveGroup(group);
}
internal void AddGroup(MyBlockGroup group)
{
foreach (var g in BlockGroups)
if (g.Name.CompareTo(group.Name) == 0)
{
BlockGroups.Remove(g);
if (group.CubeGrid != this)
group.Blocks.AddList(g.Blocks);
break;
}
group.CubeGrid = this;
BlockGroups.Add(group);
GridSystems.AddGroup(group);
}
internal void OnAddedToGroup(MyGridLogicalGroupData group)
{
ProfilerShort.Begin("MyCubeGrid.OnAddedToGroup");
GridSystems.OnAddedToGroup(group);
if (AddedToLogicalGroup != null)
AddedToLogicalGroup(group);
ProfilerShort.End();
}
internal void OnRemovedFromGroup(MyGridLogicalGroupData group)
{
ProfilerShort.Begin("MyCubeGride.OnRemovedFromGroup");
GridSystems.OnRemovedFromGroup(group);
if (RemovedFromLogicalGroup != null)
RemovedFromLogicalGroup();
ProfilerShort.End();
}
internal void OnAddedToGroup(MyGridPhysicalGroupData groupData)
{
GridSystems.OnAddedToGroup(groupData);
}
internal void OnRemovedFromGroup(MyGridPhysicalGroupData group)
{
GridSystems.OnRemovedFromGroup(group);
}
///
/// Reduces the control of the current group if the current grid is the one that the player is sitting in
///
private void TryReduceGroupControl()
{
var controller = Sync.Players.GetEntityController(this);
if (controller != null && controller.ControlledEntity is MyCockpit)
{
var cockpit = controller.ControlledEntity as MyCockpit;
if (cockpit.CubeGrid == this)
{
var logicalGroups = MyCubeGridGroups.Static.Logical;
var group = logicalGroups.GetGroup(this);
if (group != null) // CH:TODO: During exitting to the main menu, the group can be null according to some crash logs. Find out why
foreach (var node in group.Nodes)
{
if (node.NodeData == this) continue;
// CH: This is to catch a nullref (unrelated to the previous comment)
if (MySession.Static == null) MyLog.Default.WriteLine("MySession.Static was null");
else if (MySession.Static.SyncLayer == null) MyLog.Default.WriteLine("MySession.Static.SyncLayer was null");
else if (Sync.Clients == null) MyLog.Default.WriteLine("Sync.Clients was null");
Sync.Players.TryReduceControl(cockpit, node.NodeData);
}
}
}
}
public override void OnAddedToScene(object source)
{
// On clients grids can be replicated only by replication
//if (!Sync.IsServer)
//{
//Debug.Assert(DebugCreatedBy == DebugCreatedBy.FromServer || DebugCreatedBy == DebugCreatedBy.Clipboard || DebugCreatedBy == DebugCreatedBy.ServerSplit, "Creating grid locally on client, invalid in MP!");
//}
base.OnAddedToScene(source);
MyCubeGridGroups.Static.AddNode(GridLinkTypeEnum.Logical, this);
MyCubeGridGroups.Static.AddNode(GridLinkTypeEnum.Physical, this);
RecalculateGravity();
}
public override void OnRemovedFromScene(object source)
{
base.OnRemovedFromScene(source);
MyCubeGridGroups.Static.RemoveNode(GridLinkTypeEnum.Physical, this);
MyCubeGridGroups.Static.RemoveNode(GridLinkTypeEnum.Logical, this);
}
protected override void BeforeDelete()
{
SendRemovedBlocks();
SendRemovedBlocksWithIds();
m_cubes.Clear();
m_targetingList.Clear();
if (MyFakes.ENABLE_NEW_SOUNDS && MySession.Static.Settings.RealisticSound && MyFakes.ENABLE_NEW_SOUNDS_QUICK_UPDATE)
MyEntity3DSoundEmitter.UpdateEntityEmitters(true, false, false);
// This is here because of groups, not better way to do it
// also network sync is disabled for terminal systems and groups are unlinked here
MyEntities.Remove(this);
UnregisterBlocksBeforeClose();
Render.CloseModelGenerators();
base.BeforeDelete();
GridCounter--;
}
private void UnregisterBlocks(IEnumerable cubeBlocks)
{
foreach (var block in cubeBlocks)
{
if (block.FatBlock != null)
{
GridSystems.UnregisterFromSystems(block.FatBlock);
}
}
}
private void UnregisterBlocksBeforeClose()
{
GridSystems.BeforeGridClose();
UnregisterBlocks(m_cubeBlocks);
GridSystems.AfterGridClose();
}
public override bool GetIntersectionWithLine(ref LineD line, out MyIntersectionResultLineTriangleEx? tri, IntersectionFlags flags = IntersectionFlags.ALL_TRIANGLES)
{
bool ret = GetIntersectionWithLine(ref line, ref m_hitInfoTmp, flags);
tri = m_hitInfoTmp.Triangle;
return ret;
}
public bool GetIntersectionWithLine(ref LineD line, ref MyCubeGridHitInfo info, IntersectionFlags flags = IntersectionFlags.ALL_TRIANGLES)
{
if (info == null)
info = new MyCubeGridHitInfo();
info.Reset();
RayCastCells(line.From, line.To, m_cacheRayCastCells);
if (m_cacheRayCastCells.Count == 0)
return false;
foreach (Vector3I hit in m_cacheRayCastCells)
{
if (m_cubes.ContainsKey(hit))
{
var cube = m_cubes[hit];
MyIntersectionResultLineTriangleEx? tri;
int cubePartIndex;
GetBlockIntersection(cube, ref line, flags, out tri, out cubePartIndex);
if (tri.HasValue)
{
info.Position = cube.CubeBlock.Position;
info.Triangle = tri.Value;
info.CubePartIndex = cubePartIndex;
return true;
}
}
}
return false;
}
internal bool GetIntersectionWithLine(ref LineD line, out MyIntersectionResultLineTriangleEx? t, out MySlimBlock slimBlock, IntersectionFlags flags = IntersectionFlags.ALL_TRIANGLES)
{
t = null;
slimBlock = null;
RayCastCells(line.From, line.To, m_cacheRayCastCells);
if (m_cacheRayCastCells.Count == 0)
return false;
foreach (Vector3I hit in m_cacheRayCastCells)
{
if (m_cubes.ContainsKey(hit))
{
var cube = m_cubes[hit];
int cubePartIndex;
GetBlockIntersection(cube, ref line, flags, out t, out cubePartIndex);
if (t.HasValue)
{
slimBlock = cube.CubeBlock;
break;
}
}
}
if (slimBlock != null)
{
if (slimBlock.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock compoundBlock = slimBlock.FatBlock as MyCompoundCubeBlock;
ListReader slimBlocksInCompound = compoundBlock.GetBlocks();
double distanceSquaredInCompound = double.MaxValue;
MySlimBlock slimBlockInCompound = null;
for (int i = 0; i < slimBlocksInCompound.Count; ++i)
{
MySlimBlock cmpSlimBlock = slimBlocksInCompound.ItemAt(i);
VRage.Game.Models.MyIntersectionResultLineTriangleEx? intersectionTriResult;
if (cmpSlimBlock.FatBlock.GetIntersectionWithLine(ref line, out intersectionTriResult) && intersectionTriResult != null)
{
Vector3D startToIntersection = intersectionTriResult.Value.IntersectionPointInWorldSpace - line.From;
double instrDistanceSq = startToIntersection.LengthSquared();
if (instrDistanceSq < distanceSquaredInCompound)
{
distanceSquaredInCompound = instrDistanceSq;
slimBlockInCompound = cmpSlimBlock;
}
}
}
slimBlock = slimBlockInCompound;
}
}
return t.HasValue;
}
public override bool GetIntersectionWithSphere(ref BoundingSphereD sphere)
{
ProfilerShort.Begin("CubeGrid.GetIntersectionWithSphere()");
try
{
BoundingBoxD box = new BoundingBoxD(sphere.Center - new Vector3D(sphere.Radius), sphere.Center + new Vector3D(sphere.Radius));
var invee = MatrixD.Invert(WorldMatrix);
box = box.TransformFast(ref invee);
Vector3 min = box.Min;
Vector3 max = box.Max;
Vector3I start = new Vector3I((int)Math.Round(min.X * GridSizeR), (int)Math.Round(min.Y * GridSizeR), (int)Math.Round(min.Z * GridSizeR));
Vector3I end = new Vector3I((int)Math.Round(max.X * GridSizeR), (int)Math.Round(max.Y * GridSizeR), (int)Math.Round(max.Z * GridSizeR));
Vector3I startIt = Vector3I.Min(start, end);
Vector3I endIt = Vector3I.Max(start, end);
for (int i = startIt.X; i <= endIt.X; i++)
{
for (int j = startIt.Y; j <= endIt.Y; j++)
{
for (int k = startIt.Z; k <= endIt.Z; k++)
{
if (m_cubes.ContainsKey(new Vector3I(i, j, k)))
{
var cube = m_cubes[new Vector3I(i, j, k)];
if (cube.CubeBlock.FatBlock == null || cube.CubeBlock.FatBlock.Model == null)
{
if (cube.CubeBlock.BlockDefinition.CubeDefinition.CubeTopology == MyCubeTopology.Box)
return true;
foreach (var part in cube.Parts)
{
MatrixD worldMatrix = part.InstanceData.LocalMatrix * WorldMatrix;
MatrixD inv = Matrix.Invert(worldMatrix);
Vector3D transformedPos = Vector3D.Transform(sphere.Center, inv);
BoundingSphereD bs = new BoundingSphereD(transformedPos, sphere.Radius);
foreach (var triangleIndices in part.Model.Triangles)
{
MyTriangle_Vertices triangle = new MyTriangle_Vertices();
triangle.Vertex0 = part.Model.GetVertex(triangleIndices.I0);
triangle.Vertex1 = part.Model.GetVertex(triangleIndices.I1);
triangle.Vertex2 = part.Model.GetVertex(triangleIndices.I2);
MyPlane plane = new MyPlane(ref triangle);
if (MyUtils.GetSphereTriangleIntersection(ref bs, ref plane, ref triangle).HasValue)
{
return true;
}
}
}
//Cannot return because more cubes can be in the line..
// return false;
}
else
{
MatrixD worldMatrix = cube.CubeBlock.FatBlock.WorldMatrix;
MatrixD inv = Matrix.Invert(worldMatrix);
Vector3D transformedPos = Vector3D.Transform(sphere.Center, inv);
BoundingSphereD bs = new BoundingSphere(transformedPos, (float)sphere.Radius);
bool intersected = cube.CubeBlock.FatBlock.Model.GetTrianglePruningStructure().GetIntersectionWithSphere(cube.CubeBlock.FatBlock, ref sphere);
if (intersected)
return intersected;
}
//Cannot return because more cubes can be in the line..
// return true;
}
}
}
}
return false;
}
finally
{
ProfilerShort.End();
}
}
public override string ToString()
{
var stat = IsStatic ? "S" : "D";
var size = GridSizeEnum.ToString();
return "Grid_" + stat + "_" + size + "_" + m_cubeBlocks.Count + " {" + EntityId.ToString("X8") + "}";
}
#endregion
public bool IsTrash()
{
var keepReason = MyTrashRemoval.GetTrashState(this, MyTrashRemoval.PreviewSettings);
return keepReason == MyTrashRemovalFlags.None;
}
public Vector3I WorldToGridInteger(Vector3D coords)
{
Vector3D localCoords = Vector3D.Transform(coords, PositionComp.WorldMatrixNormalizedInv);
localCoords *= GridSizeR;
return Vector3I.Round(localCoords);
}
public Vector3D WorldToGridScaledLocal(Vector3D coords)
{
Vector3D localCoords = Vector3D.Transform(coords, PositionComp.WorldMatrixNormalizedInv);
localCoords *= GridSizeR;
return localCoords;
}
public static Vector3D GridIntegerToWorld(float gridSize, Vector3I gridCoords, MatrixD worldMatrix)
{
Vector3D retval = (Vector3D)(Vector3)gridCoords;
retval *= gridSize;
return Vector3D.Transform(retval, worldMatrix);
}
public Vector3D GridIntegerToWorld(Vector3I gridCoords)
{
return GridIntegerToWorld(GridSize, gridCoords, WorldMatrix);
}
public Vector3D GridIntegerToWorld(Vector3D gridCoords)
{
Vector3D retval = gridCoords;
retval *= GridSize;
return Vector3D.Transform(retval, WorldMatrix);
}
public Vector3I LocalToGridInteger(Vector3 localCoords)
{
localCoords *= GridSizeR;
return Vector3I.Round(localCoords);
}
public bool CanAddCubes(Vector3I min, Vector3I max)
{
Vector3I current = min;
for (var it = new Vector3I_RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out current))
{
if (m_cubes.ContainsKey(current))
return false;
}
return true;
}
public bool CanAddCubes(Vector3I min, Vector3I max, MyBlockOrientation? orientation, MyCubeBlockDefinition definition)
{
if (MyFakes.ENABLE_COMPOUND_BLOCKS && definition != null)
{
Vector3I current = min;
for (var it = new Vector3I_RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out current))
{
if (!CanAddCube(current, orientation, definition))
return false;
}
return true;
}
return CanAddCubes(min, max);
}
public bool CanAddCube(Vector3I pos, MyBlockOrientation? orientation, MyCubeBlockDefinition definition, bool ignoreSame = false)
{
if (MyFakes.ENABLE_COMPOUND_BLOCKS && definition != null)
{
if (!CubeExists(pos))
return true;
MySlimBlock block = GetCubeBlock(pos);
if (block != null)
{
var cmpBlock = block.FatBlock as MyCompoundCubeBlock;
if (cmpBlock != null)
return cmpBlock.CanAddBlock(definition, orientation, ignoreSame: ignoreSame);
}
return false;
}
else
{
return !CubeExists(pos);
}
}
public void ClearSymmetries()
{
XSymmetryPlane = null;
YSymmetryPlane = null;
ZSymmetryPlane = null;
}
public bool IsTouchingAnyNeighbor(Vector3I min, Vector3I max)
{
{
var minMinusX = min; minMinusX.X -= 1;
var maxMinusX = max; maxMinusX.X = minMinusX.X;
if (!CanAddCubes(minMinusX, maxMinusX))
return true;
}
{
var minMinusY = min; minMinusY.Y -= 1;
var maxMinusY = max; maxMinusY.Y = minMinusY.Y;
if (!CanAddCubes(minMinusY, maxMinusY))
return true;
}
{
var minMinusZ = min; minMinusZ.Z -= 1;
var maxMinusZ = max; maxMinusZ.Z = minMinusZ.Z;
if (!CanAddCubes(minMinusZ, maxMinusZ))
return true;
}
{
var maxPlusX = max; maxPlusX.X += 1;
var minPlusX = min; minPlusX.X = maxPlusX.X;
if (!CanAddCubes(minPlusX, maxPlusX))
return true;
}
{
var maxPlusY = max; maxPlusY.Y += 1;
var minPlusY = min; minPlusY.Y = maxPlusY.Y;
if (!CanAddCubes(minPlusY, maxPlusY))
return true;
}
{
var maxPlusZ = max; maxPlusZ.Z += 1;
var minPlusZ = min; minPlusZ.Z = maxPlusZ.Z;
if (!CanAddCubes(minPlusZ, maxPlusZ))
return true;
}
return false;
}
public bool CanPlaceBlock(Vector3I min, Vector3I max, MyBlockOrientation orientation, MyCubeBlockDefinition definition, int? ignoreMultiblockId = null, bool ignoreFracturedPieces = false)
{
var gridSettings = MyCubeBuilder.CubeBuilderDefinition.BuildingSettings.GetGridPlacementSettings(GridSizeEnum, this.IsStatic);
return CanPlaceBlock(min, max, orientation, definition, ref gridSettings, ignoreMultiblockId: ignoreMultiblockId, ignoreFracturedPieces: ignoreFracturedPieces);
}
public bool CanPlaceBlock(Vector3I min, Vector3I max, MyBlockOrientation orientation, MyCubeBlockDefinition definition, ref MyGridPlacementSettings gridSettings,
int? ignoreMultiblockId = null, bool ignoreFracturedPieces = false)
{
ProfilerShort.Begin("CanAddCubes");
if (!CanAddCubes(min, max, orientation, definition))
{
ProfilerShort.End();
return false;
}
if (MyFakes.ENABLE_MULTIBLOCKS && MyFakes.ENABLE_MULTIBLOCK_CONSTRUCTION)
{
ProfilerShort.BeginNextBlock("CanAddOtherBlockInMultiBlock");
if (!CanAddOtherBlockInMultiBlock(min, max, orientation, definition, ignoreMultiblockId))
{
ProfilerShort.End();
return false;
}
}
ProfilerShort.BeginNextBlock("TestPlacementAreaCube");
bool canPlaceBlock = TestPlacementAreaCube(this, ref gridSettings, min, max, orientation, definition, ignoredEntity: this, ignoreFracturedPieces: ignoreFracturedPieces);
ProfilerShort.End();
return canPlaceBlock;
}
///
/// Determines whether newly placed blocks still fit within block limits set by server
///
private bool IsWithinWorldLimits(long ownerID, int blocksToBuild, string name)
{
if (!MySession.Static.EnableBlockLimits) return true;
var identity = MySession.Static.Players.TryGetIdentity(ownerID);
bool withinLimits = true;
withinLimits &= MySession.Static.MaxGridSize == 0 || BlocksCount + blocksToBuild <= MySession.Static.MaxGridSize;
if (MySession.Static.MaxBlocksPerPlayer != 0 && identity != null) {
withinLimits &= identity.BlocksBuilt + blocksToBuild <= MySession.Static.MaxBlocksPerPlayer + identity.BlockLimitModifier;
}
short typeLimit = MySession.Static.GetBlockTypeLimit(name);
int typeBuilt;
if (identity != null && typeLimit > 0)
{
withinLimits &= (identity.BlockTypeBuilt.TryGetValue(name, out typeBuilt) ? typeBuilt : 0) + blocksToBuild <= typeLimit;
}
return withinLimits;
}
public void SetCubeDirty(Vector3I pos)
{
m_dirtyRegion.AddCube(pos);
var block = GetCubeBlock(pos);
if (block != null)
{
Physics.AddDirtyBlock(block);
}
}
public void SetBlockDirty(MySlimBlock cubeBlock)
{
Vector3I cube = cubeBlock.Min;
for (var it = new Vector3I_RangeIterator(ref cubeBlock.Min, ref cubeBlock.Max); it.IsValid(); it.GetNext(out cube))
{
m_dirtyRegion.AddCube(cube);
}
}
public void DebugDrawRange(Vector3I min, Vector3I max)
{
Vector3I currentMin = min;
for (var it = new Vector3I_RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out currentMin))
{
var currentMax = currentMin + 1;
var obb = new MyOrientedBoundingBoxD(
currentMin * GridSize,
GridSizeHalfVector,
Quaternion.Identity);
obb.Transform(WorldMatrix);
VRageRender.MyRenderProxy.DebugDrawOBB(obb, Color.White, 0.5f, true, false);
}
}
public void DebugDrawPositions(List positions)
{
foreach (var currentMin in positions)
{
var currentMax = currentMin + 1;
var obb = new MyOrientedBoundingBoxD(
currentMin * GridSize,
GridSizeHalfVector,
Quaternion.Identity);
obb.Transform(WorldMatrix);
VRageRender.MyRenderProxy.DebugDrawOBB(obb, Color.White.ToVector3(), 0.5f, true, false);
}
}
private MyObjectBuilder_CubeBlock UpgradeCubeBlock(MyObjectBuilder_CubeBlock block, out MyCubeBlockDefinition blockDefinition)
{
var defId = block.GetId();
if (MyFakes.ENABLE_COMPOUND_BLOCKS)
{
if (block is MyObjectBuilder_CompoundCubeBlock)
{
MyObjectBuilder_CompoundCubeBlock compoundBock = block as MyObjectBuilder_CompoundCubeBlock;
blockDefinition = MyCompoundCubeBlock.GetCompoundCubeBlockDefinition();
if (blockDefinition == null)
return null;
// Check if compound template was removed from definition (then create block without compound shell).
if (compoundBock.Blocks.Length == 1)
{
MyObjectBuilder_CubeBlock innerBlock = compoundBock.Blocks[0];
MyCubeBlockDefinition innerBlockDefinition;
if (MyDefinitionManager.Static.TryGetCubeBlockDefinition(innerBlock.GetId(), out innerBlockDefinition))
{
if (!MyCompoundCubeBlock.IsCompoundEnabled(innerBlockDefinition))
{
blockDefinition = innerBlockDefinition;
return innerBlock;
}
}
}
return block;
}
else
{
// Check added compound to definition (means that compound must be created and block set inside)
if (MyDefinitionManager.Static.TryGetCubeBlockDefinition(defId, out blockDefinition))
{
if (MyCompoundCubeBlock.IsCompoundEnabled(blockDefinition))
{
MyObjectBuilder_CompoundCubeBlock compoundCBBuilder = MyCompoundCubeBlock.CreateBuilder(block);
MyCubeBlockDefinition compoundBlockDefinition = MyCompoundCubeBlock.GetCompoundCubeBlockDefinition();
if (compoundBlockDefinition != null)
{
blockDefinition = compoundBlockDefinition;
return compoundCBBuilder;
}
}
}
}
}
// Upgrading manually as ChangeType causes stack overflow in protobuf-net.
if (block is MyObjectBuilder_Ladder)
{
var passage = MyObjectBuilderSerializer.CreateNewObject(block.SubtypeName);
passage.BlockOrientation = block.BlockOrientation;
passage.BuildPercent = block.BuildPercent;
passage.EntityId = block.EntityId;
passage.IntegrityPercent = block.IntegrityPercent;
passage.Min = block.Min;
blockDefinition = MyDefinitionManager.Static.GetCubeBlockDefinition(new MyDefinitionId(typeof(MyObjectBuilder_Passage), block.SubtypeId));
block = passage;
return block;
}
MyObjectBuilder_CubeBlock newBlock = block;
//defId = new MyDefinitionId(defId.TypeId, "LargeBlockArmorSlope");
string[] variants = new string[] { "Red", "Yellow", "Blue", "Green", "Black", "White", "Gray" };
Vector3[] colors = new Vector3[] { MyRenderComponentBase.OldRedToHSV, MyRenderComponentBase.OldYellowToHSV, MyRenderComponentBase.OldBlueToHSV, MyRenderComponentBase.OldGreenToHSV, MyRenderComponentBase.OldBlackToHSV, MyRenderComponentBase.OldWhiteToHSV, MyRenderComponentBase.OldGrayToHSV };
// Upgrade object builders which were block and now are specific
if (!MyDefinitionManager.Static.TryGetCubeBlockDefinition(defId, out blockDefinition))
{
newBlock = FindDefinitionUpgrade(block, out blockDefinition);
if (newBlock == null)
{
// Try convert old variants
for (int i = 0; i < variants.Length; i++)
{
if (defId.SubtypeName.EndsWith(variants[i], StringComparison.InvariantCultureIgnoreCase))
{
string shortName = defId.SubtypeName.Substring(0, defId.SubtypeName.Length - variants[i].Length);
var newDefId = new MyDefinitionId(defId.TypeId, shortName);
if (MyDefinitionManager.Static.TryGetCubeBlockDefinition(newDefId, out blockDefinition))
{
newBlock = block;
newBlock.ColorMaskHSV = colors[i];
newBlock.SubtypeName = shortName;
return newBlock;
}
}
}
}
if (newBlock == null)
{
return null;
}
}
return newBlock;
}
private MySlimBlock AddBlock(MyObjectBuilder_CubeBlock objectBuilder, bool testMerge)
{
ProfilerShort.Begin("MyCubeGrid.AddBlock(...)");
try
{
if (Skeleton == null)
Skeleton = new MyGridSkeleton();
MyCubeBlockDefinition blockDefinition;
ProfilerShort.Begin("UpgradeCubeBlock");
objectBuilder = UpgradeCubeBlock(objectBuilder, out blockDefinition);
ProfilerShort.End();
if (objectBuilder == null)
{
return null;
}
if (MyFakes.THROW_LOADING_ERRORS)
{
ProfilerShort.Begin("AddCubeBlock 1");
MySlimBlock block = AddCubeBlock(objectBuilder, testMerge, blockDefinition);
ProfilerShort.End();
return block;
}
else
{
try
{
ProfilerShort.Begin("AddCubeBlock 2");
MySlimBlock block = AddCubeBlock(objectBuilder, testMerge, blockDefinition);
ProfilerShort.End();
return block;
}
catch (Exception e)
{
MyLog.Default.WriteLine("ERROR while adding cube " + blockDefinition.DisplayNameText.ToString() + ": " + e.ToString());
return null;
}
}
}
finally
{
ProfilerShort.End();
}
}
private MySlimBlock AddCubeBlock(MyObjectBuilder_CubeBlock objectBuilder, bool testMerge, MyCubeBlockDefinition blockDefinition)
{
//Debug.Assert(false, "AddCubeBlock");
ProfilerShort.Begin("CanAddCubes");
Vector3I min = objectBuilder.Min, max;
MySlimBlock.ComputeMax(blockDefinition, objectBuilder.BlockOrientation, ref min, out max);
if (!CanAddCubes(min, max))
{
ProfilerShort.End();
return null;
}
ProfilerShort.BeginNextBlock("CreateCubeBlock");
var objectBlock = MyCubeBlockFactory.CreateCubeBlock(objectBuilder);
MySlimBlock cubeBlock = objectBlock as MySlimBlock;
if (cubeBlock == null)
cubeBlock = new MySlimBlock();
ProfilerShort.BeginNextBlock("Init");
cubeBlock.Init(objectBuilder, this, objectBlock as MyCubeBlock);
ProfilerShort.BeginNextBlock("MyCompoundCubeBlock");
if (cubeBlock.FatBlock is MyCompoundCubeBlock)
{
if ((cubeBlock.FatBlock as MyCompoundCubeBlock).GetBlocksCount() == 0)
{
ProfilerShort.End();
return null;
}
}
ProfilerShort.BeginNextBlock("HookMultiplayer");
cubeBlock.FatBlock.HookMultiplayer();
ProfilerShort.BeginNextBlock("AddNeighbours");
cubeBlock.AddNeighbours();
ProfilerShort.BeginNextBlock("BoundsInclude");
BoundsInclude(cubeBlock);
ProfilerShort.BeginNextBlock("Fatblock");
if (cubeBlock.FatBlock != null)
{
ProfilerShort.Begin("Hierarchy.AddChild");
Hierarchy.AddChild(cubeBlock.FatBlock);
ProfilerShort.BeginNextBlock("GridSystems.RegisterInSystems");
GridSystems.RegisterInSystems(cubeBlock.FatBlock);
ProfilerShort.BeginNextBlock("NeedsDrawFromParent");
if (cubeBlock.FatBlock.Render.NeedsDrawFromParent)
{
m_blocksForDraw.Add(cubeBlock.FatBlock);
cubeBlock.FatBlock.Render.NeedsDraw = false; //blocks shouldnt be drawn on their own at all?
}
ProfilerShort.BeginNextBlock("Misc");
MyObjectBuilderType blockType = cubeBlock.BlockDefinition.Id.TypeId;
if (blockType != typeof(MyObjectBuilder_CubeBlock))
{
if (!BlocksCounters.ContainsKey(blockType))
BlocksCounters.Add(blockType, 0);
BlocksCounters[blockType]++;
}
ProfilerShort.End();
}
ProfilerShort.BeginNextBlock("Add");
m_cubeBlocks.Add(cubeBlock);
if (cubeBlock.FatBlock != null)
m_fatBlocks.Add(cubeBlock.FatBlock);
ProfilerShort.BeginNextBlock("Misc 1");
MyBlockOrientation blockOrientation = objectBuilder.BlockOrientation;
Matrix rotationMatrix;
blockOrientation.GetMatrix(out rotationMatrix);
//Add cubes
Vector3I rotatedBlockSize;
MyCubeGridDefinitions.GetRotatedBlockSize(blockDefinition, ref rotationMatrix, out rotatedBlockSize);
//integer local center of the cube
Vector3I center = blockDefinition.Center;
//integer rotated/world center of the cube
Vector3I rotatedCenter;
Vector3I.TransformNormal(ref center, ref rotationMatrix, out rotatedCenter);
bool blockAddSuccessfull = true;
Vector3I temp = cubeBlock.Min;
ProfilerShort.BeginNextBlock("AddCube");
for (var it = new Vector3I_RangeIterator(ref cubeBlock.Min, ref cubeBlock.Max); it.IsValid(); it.GetNext(out temp))
{
blockAddSuccessfull &= AddCube(cubeBlock, ref temp, rotationMatrix, blockDefinition);
}
Debug.Assert(blockAddSuccessfull, "Cannot add cube block!");
ProfilerShort.BeginNextBlock("Physics.AddBlock");
if (Physics != null)
{
Physics.AddBlock(cubeBlock);
}
ProfilerShort.BeginNextBlock("Update Skeleton");
float boneErrorSquared = MyGridSkeleton.GetMaxBoneError(GridSize);
boneErrorSquared *= boneErrorSquared;
Vector3I boneMax = (cubeBlock.Min + Vector3I.One) * MyGridSkeleton.BoneDensity;
Vector3I bonePos = cubeBlock.Min * MyGridSkeleton.BoneDensity;
for (var it = new Vector3I_RangeIterator(ref bonePos, ref boneMax); it.IsValid(); it.GetNext(out bonePos))
{
Vector3 boneOffset = Skeleton.GetDefinitionOffsetWithNeighbours(cubeBlock.Min, bonePos, this);
if (boneOffset.LengthSquared() < boneErrorSquared)
{
Skeleton.Bones.Remove(bonePos);
}
else
{
Skeleton.Bones[bonePos] = boneOffset;
}
}
ProfilerShort.BeginNextBlock("Make skeleton dirty");
if (cubeBlock.BlockDefinition.Skeleton != null && cubeBlock.BlockDefinition.Skeleton.Count > 0)
{
if (Physics != null)
{
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
for (int k = -1; k <= 1; k++)
SetCubeDirty(new Vector3I(i, j, k) + cubeBlock.Min);
}
}
if (MyFakes.ENABLE_MULTIBLOCK_PART_IDS)
{
ProfilerShort.BeginNextBlock("ENABLE_MULTIBLOCK_PART_IDS");
AddMultiBlockInfo(cubeBlock);
}
if (testMerge)
{
ProfilerShort.BeginNextBlock("testMerge");
MyCubeGrid mergedGrid = DetectMerge(cubeBlock, null);
if (mergedGrid != null && mergedGrid != this)
{
//RK is this really necessary? This should always return the same block (because merged grid uses the same slimblock).
cubeBlock = mergedGrid.GetCubeBlock(cubeBlock.Position);
// Refresh block generators with currently set block.
mergedGrid.AdditionalModelGenerators.ForEach(g => g.BlockAddedToMergedGrid(cubeBlock));
}
else
{
NotifyBlockAdded(cubeBlock);
}
}
else
{
NotifyBlockAdded(cubeBlock);
}
ProfilerShort.End();
cubeBlock.AddAuthorship();
if (cubeBlock.FatBlock is MyReactor)
NumberOfReactors++;
return cubeBlock;
}
public void EnqueueDestructionDeformationBlock(Vector3I position)
{
if (Sync.IsServer)
{
m_destructionDeformationQueue.Add(position);
}
}
public void EnqueueDestroyedBlock(Vector3I position)
{
if (Sync.IsServer)
{
m_destroyBlockQueue.Add(position);
}
}
public void EnqueueRemovedBlock(Vector3I position, bool generatorsEnabled)
{
if (Sync.IsServer)
{
if (generatorsEnabled)
m_removeBlockQueueWithGenerators.Add(position);
else
m_removeBlockQueueWithoutGenerators.Add(position);
}
}
public void SendRemovedBlocks()
{
if (m_removeBlockQueueWithGenerators.Count > 0 || m_destroyBlockQueue.Count > 0 || m_destructionDeformationQueue.Count > 0 || m_removeBlockQueueWithoutGenerators.Count > 0)
{
MyMultiplayer.RaiseEvent(this, x => x.RemovedBlocks, m_removeBlockQueueWithGenerators, m_destroyBlockQueue, m_destructionDeformationQueue, m_removeBlockQueueWithoutGenerators);
m_removeBlockQueueWithGenerators.Clear();
m_removeBlockQueueWithoutGenerators.Clear();
m_destroyBlockQueue.Clear();
m_destructionDeformationQueue.Clear();
}
}
[Event, Reliable, Broadcast]
void RemovedBlocks(List locationsWithGenerator, List destroyLocations, List DestructionDeformationLocation, List LocationsWithoutGenerator)
{
if (destroyLocations.Count > 0)
{
BlocksDestroyed(destroyLocations);
}
if (locationsWithGenerator.Count > 0)
{
BlocksRemovedWithGenerator(locationsWithGenerator);
}
if (LocationsWithoutGenerator.Count > 0)
{
BlocksRemovedWithoutGenerator(LocationsWithoutGenerator);
}
if (DestructionDeformationLocation.Count > 0)
{
BlocksDeformed(DestructionDeformationLocation);
}
}
///
/// Server method, adds removed block with compound id into queue
///
public void EnqueueRemovedBlockWithId(Vector3I position, ushort? compoundId, bool generatorsEnabled)
{
if (Sync.IsServer)
{
var blockPositionId = new MyCubeGrid.BlockPositionId() { Position = position, CompoundId = compoundId ?? 0xFFFFFFFF };
if (generatorsEnabled)
m_removeBlockWithIdQueueWithGenerators.Add(blockPositionId);
else
m_removeBlockWithIdQueueWithoutGenerators.Add(blockPositionId);
}
}
public void EnqueueDestroyedBlockWithId(Vector3I position, ushort? compoundId, bool generatorEnabled)
{
if (Sync.IsServer)
{
if (generatorEnabled)
m_destroyBlockWithIdQueueWithGenerators.Add(new MyCubeGrid.BlockPositionId() { Position = position, CompoundId = compoundId ?? 0xFFFFFFFF });
else
m_destroyBlockWithIdQueueWithoutGenerators.Add(new MyCubeGrid.BlockPositionId() { Position = position, CompoundId = compoundId ?? 0xFFFFFFFF });
}
}
public void SendRemovedBlocksWithIds()
{
if (m_removeBlockWithIdQueueWithGenerators.Count > 0 || m_removeBlockWithIdQueueWithoutGenerators.Count > 0 || m_destroyBlockWithIdQueueWithGenerators.Count > 0
|| m_destroyBlockWithIdQueueWithoutGenerators.Count > 0)
{
MyMultiplayer.RaiseEvent(this, x => x.RemovedBlocksWithIds, m_removeBlockWithIdQueueWithGenerators, m_destroyBlockWithIdQueueWithGenerators,
m_destroyBlockWithIdQueueWithoutGenerators, m_removeBlockWithIdQueueWithoutGenerators);
m_removeBlockWithIdQueueWithGenerators.Clear();
m_removeBlockWithIdQueueWithoutGenerators.Clear();
m_destroyBlockWithIdQueueWithGenerators.Clear();
m_destroyBlockWithIdQueueWithoutGenerators.Clear();
}
}
[Event, Reliable, Broadcast]
void RemovedBlocksWithIds(List removeBlockWithIdQueueWithGenerators, List destroyBlockWithIdQueueWithGenerators, List destroyBlockWithIdQueueWithoutGenerators, List removeBlockWithIdQueueWithoutGenerators)
{
if (destroyBlockWithIdQueueWithGenerators.Count > 0)
{
BlocksWithIdDestroyedWithGenerator(destroyBlockWithIdQueueWithGenerators);
}
if (destroyBlockWithIdQueueWithoutGenerators.Count > 0)
{
BlocksWithIdDestroyedWithoutGenerator(destroyBlockWithIdQueueWithoutGenerators);
}
if (removeBlockWithIdQueueWithGenerators.Count > 0)
{
BlocksWithIdRemovedWithGenerator(removeBlockWithIdQueueWithGenerators);
}
if (removeBlockWithIdQueueWithoutGenerators.Count > 0)
{
BlocksWithIdRemovedWithoutGenerator(removeBlockWithIdQueueWithoutGenerators);
}
}
///
/// Remove all blocks from the grid built by specific player
///
[Event, Reliable, Server, Broadcast]
public void RemoveBlocksBuiltByID(long identityID)
{
foreach (var block in FindBlocksBuiltByID(identityID))
{
RemoveBlock(block);
}
}
///
/// Transfer all blocks built by a specific player to another player
///
[Event, Reliable, Server, Broadcast]
public void TransferBlocksBuiltByID(long oldOwner, long newOwner)
{
foreach (var block in FindBlocksBuiltByID(oldOwner))
{
block.TransferAuthorship(newOwner);
}
if (OnAuthorshipChanged != null)
OnAuthorshipChanged(this);
}
///
/// Send a message to a player who is supposed to receive blocks built by a player
///
[Event, Reliable, Server]
public void SendTransferRequestMessage(long oldOwner, long newOwner, ulong newOwnerSteamId)
{
if (MyEventContext.Current.IsLocallyInvoked)
{
ReceiveTransferRequestMessage(oldOwner, newOwner);
}
else
{
MyMultiplayer.RaiseEvent(this, x => x.ReceiveTransferRequestMessage, oldOwner, newOwner, targetEndpoint: new EndpointId(newOwnerSteamId));
}
}
[Event, Client]
private void ReceiveTransferRequestMessage(long oldOwner, long newOwner)
{
var identity = MySession.Static.Players.TryGetIdentity(oldOwner);
var messageBox = Sandbox.Graphics.GUI.MyGuiSandbox.CreateMessageBox(
styleEnum: Graphics.GUI.MyMessageBoxStyleEnum.Info,
buttonType: Sandbox.Graphics.GUI.MyMessageBoxButtonsType.YES_NO,
messageText: new StringBuilder().AppendFormat(MyTexts.GetString(MyCommonTexts.MessageBoxTextConfirmAcceptTransferGrid), new object[] { identity.DisplayName, identity.BlocksBuiltByGrid[this].ToString(), DisplayName }),
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionPleaseConfirm),
canHideOthers: false,
callback: (result) =>
{
if (result == Sandbox.Graphics.GUI.MyGuiScreenMessageBox.ResultEnum.YES)
{
MyMultiplayer.RaiseEvent(this, x => x.TransferBlocksBuiltByID, oldOwner, newOwner);
}
});
Sandbox.Graphics.GUI.MyGuiSandbox.AddScreen(messageBox);
}
///
/// Find all blocks built by a specific player. May be public if really needed.
///
private HashSet FindBlocksBuiltByID(long identityID)
{
var builtBlocks = new HashSet();
foreach (var block in m_cubeBlocks)
{
if (block.BuiltBy == identityID)
builtBlocks.Add(block);
}
return builtBlocks;
}
///
/// Find out whether a player blocks are supposed to be trasfered to has enough free blocks to accept them
///
public bool IsTransferBlocksBuiltByIDPossible(long oldOwner, long newOwner)
{
var oldIdentity = MySession.Static.Players.TryGetIdentity(oldOwner);
var newIdentity = MySession.Static.Players.TryGetIdentity(newOwner);
if (oldIdentity == null || newIdentity == null)
return false;
int blocksNum;
if (!oldIdentity.BlocksBuiltByGrid.TryGetValue(this, out blocksNum))
return false;
if (blocksNum > MySession.Static.MaxBlocksPerPlayer + newIdentity.BlockLimitModifier)
return false;
Dictionary builtBlocksByType = new Dictionary(MySession.Static.BlockTypeLimits);
foreach (var block in FindBlocksBuiltByID(oldOwner))
{
if (builtBlocksByType.ContainsKey(block.BlockDefinition.BlockPairName))
{
builtBlocksByType[block.BlockDefinition.BlockPairName]--;
if (builtBlocksByType[block.BlockDefinition.BlockPairName] < 0)
return false;
}
}
return true;
}
public MySlimBlock BuildGeneratedBlock(MyBlockLocation location, Vector3 colorMaskHsv)
{
MyDefinitionId blockDefinitionId = location.BlockDefinition;
MyCubeBlockDefinition blockDefinition = MyDefinitionManager.Static.GetCubeBlockDefinition(blockDefinitionId);
Quaternion orientation;
location.Orientation.GetQuaternion(out orientation);
return BuildBlock(blockDefinition, colorMaskHsv, location.Min, orientation, location.Owner, location.EntityId, null);
}
[Event, Reliable, Server]
public void BuildBlockRequest(uint colorMaskHsv, MyBlockLocation location, [DynamicObjectBuilder] MyObjectBuilder_CubeBlock blockObjectBuilder, long builderEntityId, bool instantBuild, long ownerId)
{
MyEntity builder = null;
MyEntities.TryGetEntityById(builderEntityId, out builder);
bool isAdmin = (MyEventContext.Current.IsLocallyInvoked || MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value));
if (builder == null && isAdmin == false && MySession.Static.CreativeMode == false)
{
return;
}
using (MyMultiplayer.PauseReplication())
{
MyCubeGrid.MyBlockLocation? builtBlock = null;
MyCubeBlockDefinition blockDefinition;
MyDefinitionManager.Static.TryGetCubeBlockDefinition(location.BlockDefinition, out blockDefinition);
MyBlockOrientation ori = location.Orientation;
Quaternion orientation;
location.Orientation.GetQuaternion(out orientation);
var mountPoints = blockDefinition.GetBuildProgressModelMountPoints(MyComponentStack.NewBlockIntegrity);
int? ignoreMultiBlockId = blockObjectBuilder != null && blockObjectBuilder.MultiBlockId != 0 ? (int?)blockObjectBuilder.MultiBlockId : null;
var center = location.CenterPos;
if (CanPlaceBlock(location.Min, location.Max, ori, blockDefinition, ignoreMultiblockId: ignoreMultiBlockId)
&& MyCubeGrid.CheckConnectivity(this, blockDefinition, mountPoints, ref orientation, ref center))
{
BuildBlockSuccess(ColorExtensions.UnpackHSVFromUint(colorMaskHsv), location, blockObjectBuilder, ref builtBlock, builder, isAdmin && instantBuild, ownerId);
if (builtBlock.HasValue)
{
MyMultiplayer.RaiseEvent(this, x => x.BuildBlockSucess, colorMaskHsv, location, blockObjectBuilder, builderEntityId, isAdmin && instantBuild, ownerId);
AfterBuildBlockSuccess(builtBlock.Value);
}
}
}
}
[Event, Reliable, Broadcast]
public void BuildBlockSucess(uint colorMaskHsv, MyBlockLocation location, [DynamicObjectBuilder] MyObjectBuilder_CubeBlock blockObjectBuilder, long builderEntityId, bool instantBuild, long ownerId)
{
MyEntity builder = null;
MyEntities.TryGetEntityById(builderEntityId, out builder);
MyCubeGrid.MyBlockLocation? builtBlock = null;
BuildBlockSuccess(ColorExtensions.UnpackHSVFromUint(colorMaskHsv), location, blockObjectBuilder, ref builtBlock, builder, instantBuild, ownerId);
if (builtBlock.HasValue)
{
AfterBuildBlockSuccess(builtBlock.Value);
}
else
{
}
}
///
/// Network friendly alternative for building block
///
public void BuildBlocks(ref MyBlockBuildArea area, long builderEntityId, long ownerId)
{
if (!IsWithinWorldLimits(ownerId, area.BuildAreaSize.X * area.BuildAreaSize.Y * area.BuildAreaSize.Z, MyDefinitionManager.Static.GetCubeBlockDefinition(area.DefinitionId).BlockPairName))
{
MyGuiAudio.PlaySound(MyGuiSounds.HudUnable);
MyHud.Notifications.Add(MyNotificationSingletons.ShipOverLimits);
return;
}
bool isAdmin = MySession.Static.CreativeToolsEnabled(Sync.MyId);
MyMultiplayer.RaiseEvent(this, x => x.BuildBlocksAreaRequest, area, builderEntityId, isAdmin, ownerId);
}
///
/// Builds many same blocks, used when building lines or planes.
///
public void BuildBlocks(Vector3 colorMaskHsv, HashSet locations, long builderEntityId, long ownerId)
{
string name = MyDefinitionManager.Static.GetCubeBlockDefinition(locations.First().BlockDefinition).BlockPairName;
if (!IsWithinWorldLimits(ownerId, locations.Count, name))
{
MyGuiAudio.PlaySound(MyGuiSounds.HudUnable);
MyHud.Notifications.Add(MyNotificationSingletons.ShipOverLimits);
return;
}
bool isAdmin = MySession.Static.CreativeToolsEnabled(Sync.MyId);
MyMultiplayer.RaiseEvent(this, x => x.BuildBlocksRequest, colorMaskHsv.PackHSVToUint(), locations, builderEntityId, isAdmin, ownerId);
}
[Event, Reliable, Server]
void BuildBlocksRequest(uint colorMaskHsv, HashSet locations, long builderEntityId, bool instantBuild, long ownerId)
{
if (!MySession.Static.CreativeMode && !MyEventContext.Current.IsLocallyInvoked && !MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value))
{
instantBuild = false;
}
m_tmpBuildList.Clear();
Debug.Assert(m_tmpBuildList != locations, "The build block message was received via loopback using the temporary build list. This causes erasing ot the message.");
MyEntity builder = null;
MyEntities.TryGetEntityById(builderEntityId, out builder);
MyCubeBuilder.BuildComponent.GetBlocksPlacementMaterials(locations, this);
bool isAdmin = MyEventContext.Current.IsLocallyInvoked || MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value);
if (builder == null && isAdmin == false && MySession.Static.CreativeMode == false && MyFinalBuildConstants.IS_OFFICIAL)
{
return;
}
if (!MyCubeBuilder.BuildComponent.HasBuildingMaterials(builder) && isAdmin == false)
{
return;
}
string name = MyDefinitionManager.Static.GetCubeBlockDefinition(locations.First().BlockDefinition).BlockPairName;
if (!IsWithinWorldLimits(ownerId, locations.Count, name))
{
return;
}
using (MyMultiplayer.PauseReplication())
{
Vector3 unpackedColorMask = ColorExtensions.UnpackHSVFromUint(colorMaskHsv);
BuildBlocksSuccess(unpackedColorMask, locations, m_tmpBuildList, builder, isAdmin && instantBuild, ownerId);
if (m_tmpBuildList.Count > 0)
{
MyMultiplayer.RaiseEvent(this, x => x.BuildBlocksClient, colorMaskHsv, m_tmpBuildList, builderEntityId, isAdmin && instantBuild, ownerId);
}
}
AfterBuildBlocksSuccess(m_tmpBuildList);
}
[Event, Reliable, Broadcast]
public void BuildBlocksClient(uint colorMaskHsv, HashSet locations, long builderEntityId, bool instantBuilt, long ownerId)
{
m_tmpBuildList.Clear();
Debug.Assert(m_tmpBuildList != locations, "The build block message was received via loopback using the temporary build list. This causes erasing ot the message.");
MyEntity builder = null;
MyEntities.TryGetEntityById(builderEntityId, out builder);
BuildBlocksSuccess(ColorExtensions.UnpackHSVFromUint(colorMaskHsv), locations, m_tmpBuildList, builder, instantBuilt, ownerId);
AfterBuildBlocksSuccess(m_tmpBuildList);
}
[Event, Reliable, Server]
private void BuildBlocksAreaRequest(MyCubeGrid.MyBlockBuildArea area, long builderEntityId, bool instantBuild, long ownerId)
{
if (!MySession.Static.CreativeMode && !MyEventContext.Current.IsLocallyInvoked && !MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value))
{
instantBuild = false;
}
try
{
bool isAdmin = MyEventContext.Current.IsLocallyInvoked || MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value);
if (ownerId == 0 && isAdmin == false && MySession.Static.CreativeMode == false && MyFinalBuildConstants.IS_OFFICIAL)
{
return;
}
if (!IsWithinWorldLimits(ownerId, area.BuildAreaSize.X * area.BuildAreaSize.Y * area.BuildAreaSize.Z, MyDefinitionManager.Static.GetCubeBlockDefinition(area.DefinitionId).BlockPairName))
{
return;
}
var definition = MyDefinitionManager.Static.GetCubeBlockDefinition(area.DefinitionId) as MyCubeBlockDefinition;
if (definition == null)
{
Debug.Fail("Block definition not found");
return;
}
int amount = area.BuildAreaSize.X * area.BuildAreaSize.Y * area.BuildAreaSize.Z;
MyCubeBuilder.BuildComponent.GetBlockAmountPlacementMaterials(definition, amount);
MyEntity builder = null;
MyEntities.TryGetEntityById(builderEntityId, out builder);
if (!MyCubeBuilder.BuildComponent.HasBuildingMaterials(builder, true) && isAdmin == false)
{
return;
}
long owner = isAdmin && instantBuild ? 0 : ownerId;
GetValidBuildOffsets(ref area, m_tmpBuildOffsets, m_tmpBuildFailList);
MyCubeGrid.CheckAreaConnectivity(this, ref area, m_tmpBuildOffsets, m_tmpBuildFailList);
int entityIdSeed = MyRandom.Instance.CreateRandomSeed();
MyMultiplayer.RaiseEvent(this, x => x.BuildBlocksAreaClient, area, entityIdSeed, m_tmpBuildFailList, builderEntityId, isAdmin, owner);
BuildBlocksArea(ref area, m_tmpBuildOffsets, builderEntityId, isAdmin, owner, entityIdSeed);
}
finally
{
m_tmpBuildOffsets.Clear();
m_tmpBuildFailList.Clear();
}
}
[Event, Reliable, Broadcast]
private void BuildBlocksAreaClient(MyCubeGrid.MyBlockBuildArea area, int entityIdSeed, HashSet failList, long builderEntityId, bool isAdmin, long ownerId)
{
try
{
GetAllBuildOffsetsExcept(ref area, failList, m_tmpBuildOffsets);
BuildBlocksArea(ref area, m_tmpBuildOffsets, builderEntityId, isAdmin, ownerId, entityIdSeed);
}
finally
{
m_tmpBuildOffsets.Clear();
}
}
private void BuildBlocksArea(ref MyCubeGrid.MyBlockBuildArea area, List validOffsets, long builderEntityId, bool isAdmin, long ownerId, int entityIdSeed)
{
var definition = MyDefinitionManager.Static.GetCubeBlockDefinition(area.DefinitionId) as MyCubeBlockDefinition;
if (definition == null)
{
Debug.Fail("Block definition not found");
return;
}
ProfilerShort.Begin("BuildBlocksArea");
Quaternion orientation = Base6Directions.GetOrientation(area.OrientationForward, area.OrientationUp);
Vector3I stepDir = area.StepDelta;
MyEntity builderEntity = null;
MyEntities.TryGetEntityById(builderEntityId, out builderEntity);
try
{
bool successSound = false;
// This must be here to maintain determinism, on server this goes through hashset which changes order
validOffsets.Sort(Vector3UByte.Comparer);
using (MyRandom.Instance.PushSeed(entityIdSeed))
{
foreach (var offset in validOffsets)
{
Vector3I center = area.PosInGrid + offset * stepDir;
var block = BuildBlock(definition, ColorExtensions.UnpackHSVFromUint(area.ColorMaskHSV), center + area.BlockMin, orientation, ownerId, MyEntityIdentifier.AllocateId(), builderEntity, null, false, false, isAdmin);
if (block != null)
{
successSound = true;
m_tmpBuildSuccessBlocks.Add(block);
if (ownerId == MySession.Static.LocalPlayerId)
MySession.Static.TotalBlocksCreated++;
}
}
}
var worldBB = BoundingBoxD.CreateInvalid();
foreach (var b in m_tmpBuildSuccessBlocks)
{
BoundingBoxD blockWorldAAABB;
b.GetWorldBoundingBox(out blockWorldAAABB);
worldBB.Include(blockWorldAAABB);
if (b.FatBlock == null)
continue;
ProfilerShort.Begin("OnBuildSuccess");
b.FatBlock.OnBuildSuccess(ownerId);
ProfilerShort.End();
}
if (m_tmpBuildSuccessBlocks.Count > 0)
{
if (IsStatic && Sync.IsServer)
{
var entities = MyEntities.GetEntitiesInAABB(ref worldBB);
foreach (var b in m_tmpBuildSuccessBlocks)
DetectMerge(b, null, entities);
entities.Clear();
}
m_tmpBuildSuccessBlocks[0].PlayConstructionSound(MyIntegrityChangeEnum.ConstructionBegin);
UpdateGridAABB();
}
if (MySession.Static.LocalPlayerId == ownerId)
if (successSound)
MyGuiAudio.PlaySound(MyGuiSounds.HudPlaceBlock);
else
MyGuiAudio.PlaySound(MyGuiSounds.HudUnable);
}
finally
{
m_tmpBuildSuccessBlocks.Clear();
ProfilerShort.End();
}
}
private void GetAllBuildOffsetsExcept(ref MyCubeGrid.MyBlockBuildArea area, HashSet exceptList, List resultOffsets)
{
Vector3UByte offset;
for (offset.X = 0; offset.X < area.BuildAreaSize.X; offset.X++)
{
for (offset.Y = 0; offset.Y < area.BuildAreaSize.Y; offset.Y++)
{
for (offset.Z = 0; offset.Z < area.BuildAreaSize.Z; offset.Z++)
{
if (!exceptList.Contains(offset))
{
resultOffsets.Add(offset);
}
}
}
}
}
private void GetValidBuildOffsets(ref MyCubeGrid.MyBlockBuildArea area, List resultOffsets, HashSet resultFailList)
{
Vector3I stepDir = area.StepDelta;
MyBlockOrientation ori = new MyBlockOrientation(area.OrientationForward, area.OrientationUp);
var blockDefinition = MyDefinitionManager.Static.GetCubeBlockDefinition(area.DefinitionId);
Vector3UByte offset;
for (offset.X = 0; offset.X < area.BuildAreaSize.X; offset.X++)
{
for (offset.Y = 0; offset.Y < area.BuildAreaSize.Y; offset.Y++)
{
for (offset.Z = 0; offset.Z < area.BuildAreaSize.Z; offset.Z++)
{
Vector3I pos = area.PosInGrid + offset * stepDir;
if (CanPlaceBlock(pos + area.BlockMin, pos + area.BlockMax, ori, blockDefinition))
{
resultOffsets.Add(offset);
}
else
{
resultFailList.Add(offset);
}
}
}
}
}
private void BuildBlocksSuccess(Vector3 colorMaskHsv, HashSet locations, HashSet resultBlocks, MyEntity builder, bool instantBuilt, long ownerId)
{
bool cubeProcessed = true;
while (locations.Count > 0 && cubeProcessed)
{
cubeProcessed = false;
foreach (MyBlockLocation location in locations)
{
Quaternion orientation;
location.Orientation.GetQuaternion(out orientation);
var center = location.CenterPos;
MyCubeBlockDefinition blockDefinition;
MyDefinitionManager.Static.TryGetCubeBlockDefinition(location.BlockDefinition, out blockDefinition);
if (blockDefinition == null)
{
Debug.Fail("Invalid block definition");
return;
}
var mountPoints = blockDefinition.GetBuildProgressModelMountPoints(MyComponentStack.NewBlockIntegrity);
// If we are on the server, we perform various checks. Clients on the other hand just build the blocks
// TODO: Refactor
if (!Sync.IsServer || CanPlaceWithConnectivity(location, ref orientation, ref center, blockDefinition, mountPoints))
{
var block = BuildBlock(blockDefinition, colorMaskHsv, location.Min, orientation, location.Owner, location.EntityId, builder, testMerge: false, buildAsAdmin: instantBuilt);
if (block != null)
{
ChangeBlockOwner(instantBuilt, block, ownerId);
var resultLocation = location;
resultBlocks.Add(resultLocation);
}
cubeProcessed = true;
locations.Remove(location);
break;
}
}
}
}
private bool CanPlaceWithConnectivity(MyBlockLocation location, ref Quaternion orientation, ref Vector3I center, MyCubeBlockDefinition blockDefinition, MyCubeBlockDefinition.MountPoint[] mountPoints)
{
return CanPlaceBlock(location.Min, location.Max, location.Orientation, blockDefinition)
&& MyCubeGrid.CheckConnectivity(this, blockDefinition, mountPoints, ref orientation, ref center);
}
private void BuildBlockSuccess(Vector3 colorMaskHsv, MyBlockLocation location, MyObjectBuilder_CubeBlock objectBuilder, ref MyBlockLocation? resultBlock, MyEntity builder, bool instantBuilt, long ownerId)
{
Quaternion orientation;
location.Orientation.GetQuaternion(out orientation);
MyCubeBlockDefinition blockDefinition;
MyDefinitionManager.Static.TryGetCubeBlockDefinition(location.BlockDefinition, out blockDefinition);
if (blockDefinition == null)
{
Debug.Fail("Invalid block definition");
return;
}
var block = BuildBlock(blockDefinition, colorMaskHsv, location.Min, orientation, location.Owner, location.EntityId, instantBuilt ? null: builder, objectBuilder);
if (block != null)
{
ChangeBlockOwner(instantBuilt, block, ownerId);
resultBlock = location;
block.PlayConstructionSound(MyIntegrityChangeEnum.ConstructionBegin);
}
else
{
resultBlock = null;
}
}
private static void ChangeBlockOwner(bool instantBuilt, MySlimBlock block, long ownerId)
{
if (block.FatBlock != null)
{
block.FatBlock.ChangeOwner(ownerId, MyOwnershipShareModeEnum.Faction);
}
}
private void AfterBuildBlocksSuccess(HashSet builtBlocks)
{
foreach (var location in builtBlocks)
{
AfterBuildBlockSuccess(location);
MySlimBlock block = this.GetCubeBlock(location.CenterPos);
// Detect merge after blocks are built.
MyCubeGrid mergedGrid = DetectMerge(block, null);
}
}
private void AfterBuildBlockSuccess(MyBlockLocation builtBlock)
{
var block = GetCubeBlock(builtBlock.CenterPos);
if (block != null && block.FatBlock != null)
{
block.FatBlock.OnBuildSuccess(builtBlock.Owner);
}
}
public void RazeBlocks(ref Vector3I pos, ref Vector3UByte size)
{
MyMultiplayer.RaiseEvent(this, x => x.RazeBlocksAreaRequest, pos, size);
}
[Event, Reliable, Server]
void RazeBlocksAreaRequest(Vector3I pos, Vector3UByte size)
{
if (!MySession.Static.CreativeMode && !MyEventContext.Current.IsLocallyInvoked && !MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value))
{
MyEventContext.ValidationFailed();
return;
}
try
{
Vector3UByte offset;
for (offset.X = 0; offset.X <= size.X; offset.X++)
for (offset.Y = 0; offset.Y <= size.Y; offset.Y++)
for (offset.Z = 0; offset.Z <= size.Z; offset.Z++)
{
var loc = pos + (Vector3I)offset;
MySlimBlock slimBlock = GetCubeBlock(loc);
if (slimBlock == null || (slimBlock.FatBlock != null && slimBlock.FatBlock.IsSubBlock))
m_tmpBuildFailList.Add(offset);
}
MyMultiplayer.RaiseEvent(this, x => x.RazeBlocksAreaSuccess, pos, size, m_tmpBuildFailList);
RazeBlocksAreaSuccess(pos, size, m_tmpBuildFailList);
}
finally
{
m_tmpBuildFailList.Clear();
}
}
[Event, Reliable, Broadcast]
void RazeBlocksAreaSuccess(Vector3I pos, Vector3UByte size, HashSet resultFailList)
{
Vector3I min = Vector3I.MaxValue;
Vector3I max = Vector3I.MinValue;
Vector3UByte offset;
if (MyFakes.ENABLE_MULTIBLOCKS)
{
Debug.Assert(m_tmpSlimBlocks.Count == 0);
for (offset.X = 0; offset.X <= size.X; offset.X++)
for (offset.Y = 0; offset.Y <= size.Y; offset.Y++)
for (offset.Z = 0; offset.Z <= size.Z; offset.Z++)
{
if (!resultFailList.Contains(offset))
{
var loc = pos + (Vector3I)offset;
var block = GetCubeBlock(loc);
if (block != null)
{
MyCompoundCubeBlock compound = block.FatBlock as MyCompoundCubeBlock;
if (compound != null)
{
m_tmpSlimBlocks.Clear();
m_tmpSlimBlocks.AddRange(compound.GetBlocks());
foreach (var blockInCompound in m_tmpSlimBlocks)
{
if (blockInCompound.IsMultiBlockPart)
{
m_tmpBlocksInMultiBlock.Clear();
GetBlocksInMultiBlock(blockInCompound.MultiBlockId, m_tmpBlocksInMultiBlock);
RemoveMultiBlocks(ref min, ref max, m_tmpBlocksInMultiBlock);
m_tmpBlocksInMultiBlock.Clear();
}
else
{
ushort? compoundId = compound.GetBlockId(blockInCompound);
if (compoundId != null)
RemoveBlockInCompound(blockInCompound.Position, compoundId.Value, ref min, ref max);
}
}
m_tmpSlimBlocks.Clear();
}
else
{
if (block.IsMultiBlockPart)
{
m_tmpBlocksInMultiBlock.Clear();
GetBlocksInMultiBlock(block.MultiBlockId, m_tmpBlocksInMultiBlock);
RemoveMultiBlocks(ref min, ref max, m_tmpBlocksInMultiBlock);
m_tmpBlocksInMultiBlock.Clear();
}
else
{
MyFracturedBlock fracturedBlock = block.FatBlock as MyFracturedBlock;
if (fracturedBlock != null && fracturedBlock.MultiBlocks != null && fracturedBlock.MultiBlocks.Count > 0)
{
foreach (var mbpart in fracturedBlock.MultiBlocks)
{
if (mbpart != null)
{
m_tmpBlocksInMultiBlock.Clear();
MyMultiBlockDefinition mbDef = MyDefinitionManager.Static.TryGetMultiBlockDefinition(mbpart.MultiBlockDefinition);
Debug.Assert(mbDef != null);
if (mbDef != null)
{
GetBlocksInMultiBlock(mbpart.MultiBlockId, m_tmpBlocksInMultiBlock);
RemoveMultiBlocks(ref min, ref max, m_tmpBlocksInMultiBlock);
}
m_tmpBlocksInMultiBlock.Clear();
}
}
}
else
{
min = Vector3I.Min(min, block.Min);
max = Vector3I.Max(max, block.Max);
RemoveBlockByCubeBuilder(block);
}
}
}
}
}
}
}
else
{
for (offset.X = 0; offset.X <= size.X; offset.X++)
for (offset.Y = 0; offset.Y <= size.Y; offset.Y++)
for (offset.Z = 0; offset.Z <= size.Z; offset.Z++)
{
if (!resultFailList.Contains(offset))
{
var loc = pos + (Vector3I)offset;
var block = GetCubeBlock(loc);
if (block != null)
{
min = Vector3I.Min(min, block.Min);
max = Vector3I.Max(max, block.Max);
RemoveBlockByCubeBuilder(block);
}
}
}
}
if (Physics != null)
Physics.AddDirtyArea(min, max);
}
private void RemoveMultiBlocks(ref Vector3I min, ref Vector3I max, HashSet> tmpBlocksInMultiBlock)
{
foreach (var blockInMultiBlock in tmpBlocksInMultiBlock)
{
if (blockInMultiBlock.Item2 != null)
{
RemoveBlockInCompound(blockInMultiBlock.Item1.Position, blockInMultiBlock.Item2.Value, ref min, ref max);
}
else
{
min = Vector3I.Min(min, blockInMultiBlock.Item1.Min);
max = Vector3I.Max(max, blockInMultiBlock.Item1.Max);
RemoveBlockByCubeBuilder(blockInMultiBlock.Item1);
}
}
}
public void RazeBlock(Vector3I position)
{
m_tmpPositionListSend.Clear();
m_tmpPositionListSend.Add(position);
RazeBlocks(m_tmpPositionListSend);
}
///
/// Razes blocks (unbuild)
///
public void RazeBlocks(List locations)
{
MyMultiplayer.RaiseEvent(this, x => x.RazeBlocksRequest, locations);
}
[Event, Reliable, Server]
public void RazeBlocksRequest(List locations)
{
m_tmpPositionListReceive.Clear();
Debug.Assert(m_tmpPositionListReceive != locations, "The raze block message was received via loopback using the same list. This causes erasing of the message.");
RazeBlocksSuccess(locations, m_tmpPositionListReceive);
MyMultiplayer.RaiseEvent(this, x => x.RazeBlocksClient, m_tmpPositionListReceive);
}
[Event, Reliable, Broadcast]
public void RazeBlocksClient(List locations)
{
m_tmpPositionListReceive.Clear();
Debug.Assert(m_tmpPositionListReceive != locations, "The raze block message was received via loopback using the same list. This causes erasing of the message.");
RazeBlocksSuccess(locations, m_tmpPositionListReceive);
}
private void RazeBlocksSuccess(List locations, List removedBlocks)
{
Vector3I min = Vector3I.MaxValue;
Vector3I max = Vector3I.MinValue;
foreach (var loc in locations)
{
var block = GetCubeBlock(loc);
if (block != null)
{
removedBlocks.Add(loc);
min = Vector3I.Min(min, block.Min);
max = Vector3I.Max(max, block.Max);
RemoveBlockByCubeBuilder(block);
}
}
if (Physics != null)
Physics.AddDirtyArea(min, max);
}
public void RazeGeneratedBlocks(List locations)
{
Vector3I min = Vector3I.MaxValue;
Vector3I max = Vector3I.MinValue;
foreach (var loc in locations)
{
var block = GetCubeBlock(loc);
if (block != null)
{
min = Vector3I.Min(min, block.Min);
max = Vector3I.Max(max, block.Max);
RemoveBlockByCubeBuilder(block);
}
}
if (Physics != null)
Physics.AddDirtyArea(min, max);
}
private void RazeBlockInCompoundBlockSuccess(List locationsAndIds, List> removedBlocks)
{
Vector3I min = Vector3I.MaxValue;
Vector3I max = Vector3I.MinValue;
foreach (var identity in locationsAndIds)
{
RemoveBlockInCompound(identity.Location, identity.Id, ref min, ref max, removedBlocks);
}
m_dirtyRegion.AddCubeRegion(min, max);
if (Physics != null)
Physics.AddDirtyArea(min, max);
}
private void RemoveBlockInCompound(Vector3I position, ushort compoundBlockId, ref Vector3I min, ref Vector3I max, List> removedBlocks = null)
{
var block = GetCubeBlock(position);
if (block != null && block.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock compoundBlock = block.FatBlock as MyCompoundCubeBlock;
RemoveBlockInCompoundInternal(position, compoundBlockId, ref min, ref max, removedBlocks, block, compoundBlock);
}
}
public void RazeGeneratedBlocksInCompoundBlock(List> locationsAndIds)
{
Vector3I min = Vector3I.MaxValue;
Vector3I max = Vector3I.MinValue;
foreach (var tuple in locationsAndIds)
{
var block = GetCubeBlock(tuple.Item1);
if (block != null && block.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock compoundBlock = block.FatBlock as MyCompoundCubeBlock;
RemoveBlockInCompoundInternal(tuple.Item1, tuple.Item2, ref min, ref max, null, block, compoundBlock);
}
}
m_dirtyRegion.AddCubeRegion(min, max);
if (Physics != null)
Physics.AddDirtyArea(min, max);
}
private void RemoveBlockInCompoundInternal(Vector3I position, ushort compoundBlockId, ref Vector3I min, ref Vector3I max, List> removedBlocks, MySlimBlock block, MyCompoundCubeBlock compoundBlock)
{
// Remove block in compound block
MySlimBlock blockToRemove = compoundBlock.GetBlock(compoundBlockId);
if (blockToRemove != null)
{
if (compoundBlock.Remove(blockToRemove))
{
if (removedBlocks != null)
removedBlocks.Add(new Tuple(position, compoundBlockId));
min = Vector3I.Min(min, block.Min);
max = Vector3I.Max(max, block.Max);
if (MyCubeGridSmallToLargeConnection.Static != null && m_enableSmallToLargeConnections)
{
MyCubeGridSmallToLargeConnection.Static.RemoveBlockSmallToLargeConnection(blockToRemove);
}
NotifyBlockRemoved(blockToRemove);
}
}
// Remove compound if empty
if (compoundBlock.GetBlocksCount() == 0)
{
RemoveBlockByCubeBuilder(block);
}
}
public void RazeGeneratedBlocks(List generatedBlocks)
{
ProfilerShort.Begin("MyCubeGrid.RazeGeneratedBlocks");
m_tmpRazeList.Clear();
m_tmpLocations.Clear();
foreach (var generatedBlock in generatedBlocks)
{
Debug.Assert(generatedBlock.BlockDefinition.IsGeneratedBlock);
var parentBlock = GetCubeBlock(generatedBlock.Position);
if (parentBlock != null)
{
if (parentBlock.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock cb = parentBlock.FatBlock as MyCompoundCubeBlock;
ushort? blockId = cb.GetBlockId(generatedBlock);
Debug.Assert(blockId != null);
if (blockId != null)
m_tmpRazeList.Add(new Tuple(generatedBlock.Position, blockId.Value));
}
else
{
m_tmpLocations.Add(generatedBlock.Position);
}
}
}
if (m_tmpLocations.Count > 0)
RazeGeneratedBlocks(m_tmpLocations);
if (m_tmpRazeList.Count > 0)
RazeGeneratedBlocksInCompoundBlock(m_tmpRazeList);
m_tmpRazeList.Clear();
m_tmpLocations.Clear();
ProfilerShort.End();
}
///
/// Color block in area. Verry slow.
///
///
///
///
///
public void ColorBlocks(Vector3I min, Vector3I max, Vector3 newHSV, bool playSound)
{
ProfilerShort.Begin("MyCubeGrid.ColorBlocks");
MyMultiplayer.RaiseEvent(this, x => x.ColorBlockRequest, min, max, newHSV, playSound);
ProfilerShort.End();
}
public void ColorGrid(Vector3 newHSV, bool playSound)
{
ProfilerShort.Begin("MyCubeGrid.ColorBlocks");
MyMultiplayer.RaiseEvent(this, x => x.ColorGridFriendlyRequest, newHSV, playSound);
ProfilerShort.End();
}
[Event, Reliable, Server, Broadcast]
private void ColorGridFriendlyRequest(Vector3 newHSV,bool playSound)
{
ProfilerShort.Begin("MyCubeGrid.ColorBlockRequest");
bool sound = false;
foreach (var block in CubeBlocks)
{
sound |= ChangeColor(block, newHSV);
}
if (playSound && sound)
{
MyGuiAudio.PlaySound(MyGuiSounds.HudColorBlock);
}
ProfilerShort.End();
}
[Event, Reliable, Server, Broadcast]
private void ColorBlockRequest(Vector3I min, Vector3I max, Vector3 newHSV, bool playSound)
{
ProfilerShort.Begin("MyCubeGrid.ColorBlockRequest");
Vector3I temp;
bool sound = false;
for (temp.X = min.X; temp.X <= max.X; temp.X++)
for (temp.Y = min.Y; temp.Y <= max.Y; temp.Y++)
for (temp.Z = min.Z; temp.Z <= max.Z; temp.Z++)
{
var block = GetCubeBlock(temp);
if (block != null)
{
sound |= ChangeColor(block, newHSV);
}
}
if (playSound && sound && Vector3D.Distance(MySector.MainCamera.Position, Vector3D.Transform(min * GridSize, WorldMatrix)) < 200)
{
MyGuiAudio.PlaySound(MyGuiSounds.HudColorBlock);
}
ProfilerShort.End();
}
///
/// Builds block without checking connectivity
///
private MySlimBlock BuildBlock(MyCubeBlockDefinition blockDefinition, Vector3 colorMaskHsv, Vector3I min, Quaternion orientation, long owner, long entityId, MyEntity builderEntity, MyObjectBuilder_CubeBlock blockObjectBuilder = null, bool updateVolume = true, bool testMerge = true, bool buildAsAdmin = false)
{
ProfilerShort.Begin("BuildBlock");
MyBlockOrientation blockOrientation = new MyBlockOrientation(ref orientation);
if (blockObjectBuilder == null)
{
blockObjectBuilder = MyCubeGrid.CreateBlockObjectBuilder(blockDefinition, min, blockOrientation, entityId, owner, fullyBuilt: builderEntity == null || !MySession.Static.SurvivalMode || buildAsAdmin);
blockObjectBuilder.ColorMaskHSV = colorMaskHsv;
}
else
{
blockObjectBuilder.Min = min;
blockObjectBuilder.Orientation = orientation;
}
MyCubeBuilder.BuildComponent.BeforeCreateBlock(blockDefinition, builderEntity, blockObjectBuilder, buildAsAdmin: buildAsAdmin);
MySlimBlock block = null;
if (Sync.IsServer)
{
Vector3I position = MySlimBlock.ComputePositionInGrid(new MatrixI(blockOrientation), blockDefinition, min);
MyCubeBuilder.BuildComponent.GetBlockPlacementMaterials(blockDefinition, position, blockObjectBuilder.BlockOrientation, this);
}
if (MyFakes.ENABLE_COMPOUND_BLOCKS && MyCompoundCubeBlock.IsCompoundEnabled(blockDefinition))
{
MySlimBlock existingSlimBlock = GetCubeBlock(min);
MyCompoundCubeBlock compoundBlock = existingSlimBlock != null ? existingSlimBlock.FatBlock as MyCompoundCubeBlock : null;
if (compoundBlock != null)
{
if (compoundBlock.CanAddBlock(blockDefinition, new MyBlockOrientation(ref orientation)))
{
// Existing compound block in position
var objectBlock = MyCubeBlockFactory.CreateCubeBlock(blockObjectBuilder);
block = objectBlock as MySlimBlock;
if (block == null)
block = new MySlimBlock();
block.Init(blockObjectBuilder, this, objectBlock as MyCubeBlock);
block.FatBlock.HookMultiplayer();
ushort id;
if (compoundBlock.Add(block, out id))
{
BoundsInclude(block);
m_dirtyRegion.AddCube(min);
if (Physics != null)
Physics.AddDirtyBlock(existingSlimBlock);
NotifyBlockAdded(block);
}
}
}
else
{
// Create new compound block in position
MyObjectBuilder_CompoundCubeBlock compoundCBBuilder = MyCompoundCubeBlock.CreateBuilder(blockObjectBuilder);
block = AddBlock(compoundCBBuilder, testMerge);
}
}
else
{
block = AddBlock(blockObjectBuilder, testMerge);
}
if (block != null)
{
// We have to use block.CubeGrid instead of this because adding the block could have caused a grid merge
block.CubeGrid.BoundsInclude(block);
if (updateVolume)
block.CubeGrid.UpdateGridAABB();
if (MyCubeGridSmallToLargeConnection.Static != null && m_enableSmallToLargeConnections)
MyCubeGridSmallToLargeConnection.Static.AddBlockSmallToLargeConnection(block);
if (Sync.IsServer)
{
MyCubeBuilder.BuildComponent.AfterSuccessfulBuild(builderEntity, buildAsAdmin);
}
MyCubeGrids.NotifyBlockBuilt(this, block);
}
ProfilerShort.End();
return block;
}
internal bool AddExplosion(Vector3 pos, MyExplosionTypeEnum type, float radius, bool createParticles, bool createDebris)
{
// We don't want explosion on same cube too soon
if (!m_explosions.AddInstance(TimeSpan.FromMilliseconds(700), pos, GridSize * 2))
return false;
MyExplosionFlags flags = MyExplosionFlags.AFFECT_VOXELS;
if (createParticles)
{
flags |= MyExplosionFlags.CREATE_PARTICLE_EFFECT;
}
if (createDebris)
{
flags |= MyExplosionFlags.CREATE_DEBRIS | MyExplosionFlags.CREATE_DECALS;
}
var explosionSphere = new BoundingSphere(pos, radius);
MyExplosionInfo explosionInfo = new MyExplosionInfo()
{
PlayerDamage = 0,
Damage = 1,
ExplosionSphere = explosionSphere,
ExcludedEntity = type == MyExplosionTypeEnum.GRID_DESTRUCTION ? this : null,
ExplosionFlags = flags,
ExplosionType = type,
LifespanMiliseconds = MyExplosionsConstants.EXPLOSION_LIFESPAN,
ParticleScale = Math.Max(GridSizeEnum == MyCubeSize.Large ? 1 : 0.4f, Math.Min(radius / 4, 6)),
VoxelCutoutScale = 1.0f,
PlaySound = false,
CheckIntersections = false,
VoxelExplosionCenter = explosionSphere.Center,
Velocity = Physics.RigidBody.LinearVelocity,
ObjectsRemoveDelayInMiliseconds = MyRandom.Instance.Next(250),
};
MyExplosions.AddExplosion(ref explosionInfo);
return true;
}
public void ResetBlockSkeleton(MySlimBlock block, bool updateSync = false)
{
MultiplyBlockSkeleton(block, 0.0f, updateSync);
}
public void MultiplyBlockSkeleton(MySlimBlock block, float factor, bool updateSync = false)
{
Debug.Assert(Skeleton != null, "Skeleton null in MultiplyBlockSkeleton!");
Debug.Assert(Physics != null, "Physics null in MultiplyBlockSkeleton!");
if (Skeleton == null)
{
MyLog.Default.WriteLine("Skeleton null in MultiplyBlockSkeleton!" + this);
}
if (Physics == null)
{
MyLog.Default.WriteLine("Physics null in MultiplyBlockSkeleton!" + this);
}
if (block == null || Skeleton == null || Physics == null)
return;
var min = block.Min * MyGridSkeleton.BoneDensity;
var max = block.Max * MyGridSkeleton.BoneDensity + MyGridSkeleton.BoneDensity;
bool bonesChanged = false;
Vector3I pos;
for (pos.Z = min.Z; pos.Z <= max.Z; pos.Z++)
{
for (pos.Y = min.Y; pos.Y <= max.Y; pos.Y++)
{
for (pos.X = min.X; pos.X <= max.X; pos.X++)
{
bonesChanged |= Skeleton.MultiplyBone(ref pos, factor, ref block.Min, this);
}
}
}
if (bonesChanged)
{
if (Sync.IsServer && updateSync)
{
MyMultiplayer.RaiseEvent(this, x => x.OnBonesMultiplied, block.Position, factor);
}
min = block.Min - Vector3I.One;
max = block.Max + Vector3I.One;
for (pos.Z = min.Z; pos.Z <= max.Z; pos.Z++)
{
for (pos.Y = min.Y; pos.Y <= max.Y; pos.Y++)
{
for (pos.X = min.X; pos.X <= max.X; pos.X++)
{
m_dirtyRegion.AddCube(pos);
}
}
}
Physics.AddDirtyArea(min, max);
}
}
public void AddDirtyBone(Vector3I gridPosition, Vector3I boneOffset)
{
Skeleton.Wrap(ref gridPosition, ref boneOffset);
var cubeOffset = boneOffset - new Vector3I(1, 1, 1);
Vector3I min = Vector3I.Min(cubeOffset, new Vector3I(0, 0, 0));
Vector3I max = Vector3I.Max(cubeOffset, new Vector3I(0, 0, 0));
Vector3I temp = min;
for (Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref min, ref max); it.IsValid(); it.GetNext(out temp))
m_dirtyRegion.AddCube(gridPosition + temp);
}
public MySlimBlock GetCubeBlock(Vector3I pos)
{
MyCube result;
if (m_cubes.TryGetValue(pos, out result))
{
Debug.Assert(result.CubeBlock.FatBlock == null || !result.CubeBlock.FatBlock.Closed);
return result.CubeBlock;
}
return null;
}
public MySlimBlock GetCubeBlock(Vector3I pos, ushort? compoundId)
{
if (compoundId == null)
return GetCubeBlock(pos);
MyCube result;
if (m_cubes.TryGetValue(pos, out result))
{
Debug.Assert(result.CubeBlock.FatBlock != null && !result.CubeBlock.FatBlock.Closed);
MyCompoundCubeBlock compound = result.CubeBlock.FatBlock as MyCompoundCubeBlock;
if (compound != null)
{
return compound.GetBlock(compoundId.Value);
}
}
return null;
}
public T GetFirstBlockOfType()
where T : MyCubeBlock
{
foreach (var cube in m_cubeBlocks)
{
if (cube.FatBlock != null && (cube.FatBlock is T))
return (cube.FatBlock as T);
}
return null;
}
///
/// Iterate over all the neighbors of the cube and return when one of them exists.
///
public void FixTargetCube(out Vector3I cube, Vector3 fractionalGridPosition)
{
cube = Vector3I.Round(fractionalGridPosition);
fractionalGridPosition += new Vector3(0.5f);
if (m_cubes.ContainsKey(cube)) return;
// Calculate distances to all 26 neighbors of the cube
Vector3 dDown = fractionalGridPosition - cube;
Vector3 dUp = new Vector3(1.0f) - dDown;
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN] = dDown.X;
m_neighborDistances[(int)NeighborOffsetIndex.XUP] = dUp.X;
m_neighborDistances[(int)NeighborOffsetIndex.YDOWN] = dDown.Y;
m_neighborDistances[(int)NeighborOffsetIndex.YUP] = dUp.Y;
m_neighborDistances[(int)NeighborOffsetIndex.ZDOWN] = dDown.Z;
m_neighborDistances[(int)NeighborOffsetIndex.ZUP] = dUp.Z;
Vector3 dDown2 = dDown * dDown;
Vector3 dUp2 = dUp * dUp;
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_YDOWN] = (float)Math.Sqrt(dDown2.X + dDown2.Y);
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_YUP] = (float)Math.Sqrt(dDown2.X + dUp2.Y);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_YDOWN] = (float)Math.Sqrt(dUp2.X + dDown2.Y);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_YUP] = (float)Math.Sqrt(dUp2.X + dUp2.Y);
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_ZDOWN] = (float)Math.Sqrt(dDown2.X + dDown2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_ZUP] = (float)Math.Sqrt(dDown2.X + dUp2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_ZDOWN] = (float)Math.Sqrt(dUp2.X + dDown2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_ZUP] = (float)Math.Sqrt(dUp2.X + dUp2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.YDOWN_ZDOWN] = (float)Math.Sqrt(dDown2.Y + dDown2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.YDOWN_ZUP] = (float)Math.Sqrt(dDown2.Y + dUp2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.YUP_ZDOWN] = (float)Math.Sqrt(dUp2.Y + dDown2.Z);
m_neighborDistances[(int)NeighborOffsetIndex.YUP_ZUP] = (float)Math.Sqrt(dUp2.Y + dUp2.Z);
Vector3 dDown3 = dDown2 * dDown;
Vector3 dUp3 = dUp2 * dUp;
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_YDOWN_ZDOWN] = (float)Math.Pow(dDown3.X + dDown3.Y + dDown3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_YDOWN_ZUP] = (float)Math.Pow(dDown3.X + dDown3.Y + dUp3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_YUP_ZDOWN] = (float)Math.Pow(dDown3.X + dUp3.Y + dDown3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XDOWN_YUP_ZUP] = (float)Math.Pow(dDown3.X + dUp3.Y + dUp3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_YDOWN_ZDOWN] = (float)Math.Pow(dUp3.X + dDown3.Y + dDown3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_YDOWN_ZUP] = (float)Math.Pow(dUp3.X + dDown3.Y + dUp3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_YUP_ZDOWN] = (float)Math.Pow(dUp3.X + dUp3.Y + dDown3.Z, 1.0 / 3.0);
m_neighborDistances[(int)NeighborOffsetIndex.XUP_YUP_ZUP] = (float)Math.Pow(dUp3.X + dUp3.Y + dUp3.Z, 1.0 / 3.0);
// Bubble sort the face neighbors by distance
for (int i = 0; i < 25; ++i)
{
for (int j = 0; j < 25 - i; ++j)
{
float distFirst = m_neighborDistances[(int)m_neighborOffsetIndices[j]];
float distSecond = m_neighborDistances[(int)m_neighborOffsetIndices[j + 1]];
if (distFirst > distSecond)
{
NeighborOffsetIndex swap = m_neighborOffsetIndices[j];
m_neighborOffsetIndices[j] = m_neighborOffsetIndices[j + 1];
m_neighborOffsetIndices[j + 1] = swap;
}
}
}
// Find the first existing neighbor by distance
Vector3I offset = new Vector3I();
for (int i = 0; i < m_neighborOffsets.Count; ++i)
{
offset = m_neighborOffsets[(int)m_neighborOffsetIndices[i]];
if (m_cubes.ContainsKey(cube + offset))
{
cube = cube + offset;
//break;
return;
}
}
}
public HashSet GetBlocks()
{
return m_cubeBlocks;
}
public ListReader GetFatBlocks()
{
return m_fatBlocks;
}
public MyFatBlockReader GetFatBlocks()
where T : MyCubeBlock
{
return new MyFatBlockReader(this);
}
///
/// Returns true when grid have at least one block which has physics (e.g. interior lights have no physics)
///
public bool CanHavePhysics()
{
if (m_canHavePhysics)
{
//TODO Temporary performance improvement - HasPhysics should be cached somehow
if (MyPerGameSettings.Game == GameEnum.SE_GAME || MyPerGameSettings.Game == GameEnum.VRS_GAME)
{
foreach (var block in m_cubeBlocks)
{
if (block.HasPhysics)
return true;
}
m_canHavePhysics = false;
}
else
{
m_canHavePhysics = m_cubeBlocks.Count > 0;
}
}
return m_canHavePhysics;
}
///
/// Returns true when grid have at least one block which has physics (lights has no physics)
///
public static bool CanHavePhysics(List blocks, int offset, int count)
{
if (offset < 0)
{
Debug.Fail(String.Format("Negative offset in CanHavePhysics - {0}", offset));
MySandboxGame.Log.WriteLine(String.Format("Negative offset in CanHavePhysics - {0}", offset));
// zxc throw here exception or not?
return false;
}
for (int i = offset; i < offset + count; i++)
{
if (i >= blocks.Count)
{
break;
}
var block = blocks[i];
if (block != null && block.HasPhysics)
return true;
}
return false;
}
private void RebuildGrid()
{
// No physical cubes (closed in UpdateAfterSimulation)
if (!CanHavePhysics())
{
return;
}
ProfilerShort.Begin("Rebuild grid");
ProfilerShort.Begin("Recalc bounds");
RecalcBounds();
ProfilerShort.End();
ProfilerShort.Begin("Remove redundant parts");
RemoveRedundantParts();
ProfilerShort.End();
ProfilerShort.Begin("Close grid physics");
if (this.Physics != null)
{
this.Physics.Close();
this.Physics = null;
}
ProfilerShort.End();
ProfilerShort.Begin("Create grid physics");
if (CreatePhysics)
{
Physics = new MyGridPhysics(this);
RaisePhysicsChanged();
if (!Sync.IsServer && MyPerGameSettings.GridRBFlagOnClients == RigidBodyFlag.RBF_KINEMATIC)
Physics.RigidBody.UpdateMotionType(HkMotionType.Keyframed);
}
ProfilerShort.End();
ProfilerShort.End();
}
[Event,Reliable,Broadcast]
public void ConvertToDynamic()
{
Debug.Assert(IsStatic);
if (!IsStatic || Physics == null || BlocksCount == 0) return;
if (MyCubeGridSmallToLargeConnection.Static != null && m_enableSmallToLargeConnections)
{
MyCubeGridSmallToLargeConnection.Static.GridConvertedToDynamic(this);
}
IsStatic = false;
MyCubeGridGroups.Static.UpdateDynamicState(this);
SetInventoryMassDirty();
Physics.ConvertToDynamic(GridSizeEnum == MyCubeSize.Large);
RaisePhysicsChanged();
Physics.RigidBody.AddGravity();
RecalculateGravity();
}
[Event, Reliable, Broadcast]
public void ConvertToStatic()
{
if (Physics.AngularVelocity.LengthSquared() > 0.01 * 0.01 || Physics.LinearVelocity.LengthSquared() > 0.01 * 0.01)
return;
IsStatic = true;
Physics.ConvertToStatic();
RaisePhysicsChanged();
}
public void DoDamage(float damage, MyHitInfo hitInfo, Vector3? localPos = null, long attackerId = 0)
{
Debug.Assert(Sync.IsServer);
if (Sync.IsServer == false)
{
return;
}
Vector3I cubePos;
if (localPos.HasValue)
FixTargetCube(out cubePos, localPos.Value * GridSizeR);
else
FixTargetCube(out cubePos, Vector3D.Transform(hitInfo.Position, PositionComp.WorldMatrixInvScaled) * GridSizeR);
var cube = GetCubeBlock(cubePos);
//Debug.Assert(cube != null, "Cannot find block for damage!");
if (cube != null)
{
if (MyFakes.ENABLE_FRACTURE_COMPONENT)
{
ushort? compoundId = null;
MyCompoundCubeBlock compound = cube.FatBlock as MyCompoundCubeBlock;
if (compound != null)
{
compoundId = Physics.GetContactCompoundId(cube.Position, hitInfo.Position);
if (compoundId == null)
return;
var blockInCompound = compound.GetBlock(compoundId.Value);
if (blockInCompound != null)
cube = blockInCompound;
else
return;
}
}
ApplyDestructionDeformation(cube, damage, hitInfo, attackerId);
}
}
public void ApplyDestructionDeformation(MySlimBlock block, float damage = 1f, MyHitInfo? hitInfo = null, long attackerId = 0)
{
if (MyPerGameSettings.Destruction)
{
Debug.Assert(hitInfo.HasValue, "Destruction needs additional info");
(block as IMyDestroyableObject).DoDamage(damage, MyDamageType.Unknown, true, hitInfo, attackerId);
}
else
{
Debug.Assert(Sandbox.Game.Multiplayer.Sync.IsServer, "ApplyDestructionDeformation is supposed to be only server method");
EnqueueDestructionDeformationBlock(block.Position);
ApplyDestructionDeformationInternal(block, true, damage, attackerId);
}
}
private float ApplyDestructionDeformationInternal(MySlimBlock block, bool sync, float damage = 1f, long attackerId = 0)
{
if (!BlocksDestructionEnabled)
return 0;
// Allow mods to stop deformation
if (block.UseDamageSystem)
{
MyDamageInformation damageInfo = new MyDamageInformation(true, 1f, MyDamageType.Deformation, attackerId);
MyDamageSystem.Static.RaiseBeforeDamageApplied(block, ref damageInfo);
if (damageInfo.Amount == 0f)
return 0;
}
m_totalBoneDisplacement = 0.0f;
// TODO: Optimization. Cache bone changes (moves) and apply them only at the end
ProfilerShort.Begin("Update corner bones");
Vector3I minCube = Vector3I.MaxValue;
Vector3I maxCube = Vector3I.MinValue;
bool changed = false;
for (int x = -1; x <= 1; x += 2)
{
for (int z = -1; z <= 1; z += 2)
{
changed = changed | MoveCornerBones(block.Min, new Vector3I(x, 0, z), ref minCube, ref maxCube);
changed = changed | MoveCornerBones(block.Min, new Vector3I(x, z, 0), ref minCube, ref maxCube);
changed = changed | MoveCornerBones(block.Min, new Vector3I(0, x, z), ref minCube, ref maxCube);
}
}
if (changed)
{
m_dirtyRegion.AddCubeRegion(minCube, maxCube);
}
ProfilerShort.End();
m_deformationRng.SetSeed(block.Position.GetHashCode());
float angleDeviation = MathHelper.PiOver4 / 2;
ProfilerShort.Begin("Update thin bones");
float maxLinearDeviation = GridSizeHalf;
var cubePos = block.Min;
Vector3I min, max;
for (int i = 0; i < 3; i++) // Three axes
{
min = Vector3I.MaxValue;
max = Vector3I.MinValue;
changed = false;
changed |= ApplyTable(cubePos, MyCubeGridDeformationTables.ThinUpper[i], ref min, ref max, m_deformationRng, maxLinearDeviation, angleDeviation);
changed |= ApplyTable(cubePos, MyCubeGridDeformationTables.ThinLower[i], ref min, ref max, m_deformationRng, maxLinearDeviation, angleDeviation);
if (changed)
{
// One more bone to make sure to hit incident cubes
min -= Vector3I.One;
max += Vector3I.One;
minCube = cubePos;
maxCube = cubePos;
Skeleton.Wrap(ref minCube, ref min);
Skeleton.Wrap(ref maxCube, ref max);
m_dirtyRegion.AddCubeRegion(minCube, maxCube);
}
}
ProfilerShort.End();
if (sync)
{
float damageAmount = m_totalBoneDisplacement * GridSize * 10.0f * damage;
MyDamageInformation damageInfo = new MyDamageInformation(false, damageAmount, MyDamageType.Deformation, attackerId);
if (block.UseDamageSystem)
MyDamageSystem.Static.RaiseBeforeDamageApplied(block, ref damageInfo);
if (damageAmount > 0f)
(block as IMyDestroyableObject).DoDamage(damageInfo.Amount, MyDamageType.Deformation, true, attackerId: attackerId);
}
return m_totalBoneDisplacement;
}
///
/// Removes destroyed block, applies damage and deformation to close blocks
/// Won't update physics!
///
public void RemoveDestroyedBlock(MySlimBlock block, long attackerId = 0)
{
if (!Sync.IsServer)
return;
if (MyFakes.ENABLE_FRACTURE_COMPONENT)
{
bool enableGenerators = attackerId != 0;
bool oldEnabled = EnableGenerators(enableGenerators);
var existingBlock = GetCubeBlock(block.Position);
Debug.Assert(existingBlock != null);
if (existingBlock == null)
return;
if (existingBlock == block)
{
EnqueueDestroyedBlockWithId(block.Position, null, enableGenerators);
RemoveDestroyedBlockInternal(block);
Physics.AddDirtyBlock(block);
}
else
{
var compound = existingBlock.FatBlock as MyCompoundCubeBlock;
if (compound != null)
{
var compoundId = compound.GetBlockId(block);
if (compoundId != null)
{
EnqueueDestroyedBlockWithId(block.Position, compoundId, enableGenerators);
RemoveDestroyedBlockInternal(block);
Physics.AddDirtyBlock(block);
}
}
}
EnableGenerators(oldEnabled);
// Change fractures in block into pieces
var fractureComponent = block.GetFractureComponent();
if (fractureComponent != null)
{
MyDestructionHelper.CreateFracturePiece(fractureComponent, true);
}
}
else
{
EnqueueDestroyedBlock(block.Position);
RemoveDestroyedBlockInternal(block);
Physics.AddDirtyBlock(block);
}
}
private void RemoveDestroyedBlockInternal(MySlimBlock block)
{
ApplyDestructionDeformationInternal(block, false);
(block as IMyDestroyableObject).OnDestroy();
var existingBlock = GetCubeBlock(block.Position);
if (existingBlock == block)
{
RemoveBlockInternal(block, close: true);
}
else if (existingBlock != null)
{
var compound = existingBlock.FatBlock as MyCompoundCubeBlock;
if (compound != null)
{
var compoundId = compound.GetBlockId(block);
if (compoundId != null)
{
Vector3I min = Vector3I.MaxValue, max = Vector3I.MinValue;
RemoveBlockInCompound(block.Position, compoundId.Value, ref min, ref max);
}
}
}
}
private bool ApplyTable(Vector3I cubePos, MyCubeGridDeformationTables.DeformationTable table, ref Vector3I dirtyMin, ref Vector3I dirtyMax, MyRandom random, float maxLinearDeviation, float angleDeviation)
{
ProfilerShort.Begin("ApplyTable");
if (!m_cubes.ContainsKey(cubePos + table.Normal))
{
Vector3I boneOffset;
Vector3 clamp;
float gridSizeTenth = GridSize / 10;
m_tmpBoneSet.Clear();
ProfilerShort.Begin("GetExistingBones");
GetExistingBones(cubePos * MyGridSkeleton.BoneDensity + table.MinOffset, cubePos * MyGridSkeleton.BoneDensity + table.MaxOffset, m_tmpBoneSet);
ProfilerShort.BeginNextBlock("table.OffsetTable");
foreach (var offset in table.OffsetTable)
{
if (m_tmpBoneSet.ContainsKey(cubePos * MyGridSkeleton.BoneDensity + offset.Key))
{
boneOffset = offset.Key;
clamp = new Vector3(GridSizeHalf - random.NextFloat(0, gridSizeTenth));
Vector3 moveDirection = random.NextDeviatingVector(offset.Value, angleDeviation) * random.NextFloat(1, maxLinearDeviation);
float length = moveDirection.Length();
MoveBone(ref cubePos, ref boneOffset, ref moveDirection, ref length, ref clamp);
}
}
ProfilerShort.End();
dirtyMin = Vector3I.Min(dirtyMin, table.MinOffset);
dirtyMax = Vector3I.Max(dirtyMax, table.MaxOffset);
ProfilerShort.End();
return true;
}
ProfilerShort.End();
return false;
}
private void BlocksRemovedWithGenerator(List blocksToRemove)
{
bool oldEnabled = EnableGenerators(true, true);
BlocksRemoved(blocksToRemove);
EnableGenerators(oldEnabled, true);
}
private void BlocksRemovedWithoutGenerator(List blocksToRemove)
{
bool oldEnabled = EnableGenerators(false, true);
BlocksRemoved(blocksToRemove);
EnableGenerators(oldEnabled, true);
}
void BlocksWithIdRemovedWithGenerator(List blocksToRemove)
{
bool oldEnabled = EnableGenerators(true, true);
BlocksWithIdRemoved(blocksToRemove);
EnableGenerators(oldEnabled, true);
}
void BlocksWithIdRemovedWithoutGenerator(List blocksToRemove)
{
bool oldEnabled = EnableGenerators(false, true);
BlocksWithIdRemoved(blocksToRemove);
EnableGenerators(oldEnabled, true);
}
///
/// Client only method, not called on server
///
private void BlocksRemoved(List blocksToRemove)
{
foreach (var pos in blocksToRemove)
{
var block = GetCubeBlock(pos);
if (block != null)
{
RemoveBlockInternal(block, close: true);
Physics.AddDirtyBlock(block);
}
}
}
private void BlocksWithIdRemoved(List blocksToRemove)
{
foreach (var posAndId in blocksToRemove)
{
if (posAndId.CompoundId > ushort.MaxValue)
{
var block = GetCubeBlock(posAndId.Position);
if (block != null)
{
RemoveBlockInternal(block, close: true);
Physics.AddDirtyBlock(block);
}
}
else
{
Vector3I min = Vector3I.MaxValue;
Vector3I max = Vector3I.MinValue;
RemoveBlockInCompound(posAndId.Position, (ushort)posAndId.CompoundId, ref min, ref max);
if (min != Vector3I.MaxValue)
Physics.AddDirtyArea(min, max);
}
}
}
private void BlocksDestroyed(List blockToDestroy)
{
foreach (var pos in blockToDestroy)
{
var block = GetCubeBlock(pos);
if (block != null)
{
RemoveDestroyedBlockInternal(block);
Physics.AddDirtyBlock(block);
}
}
}
private void BlocksWithIdDestroyedWithGenerator(List blocksToRemove)
{
bool oldEnabled = EnableGenerators(true, true);
BlocksWithIdRemoved(blocksToRemove);
EnableGenerators(oldEnabled, true);
}
private void BlocksWithIdDestroyedWithoutGenerator(List blocksToRemove)
{
bool oldEnabled = EnableGenerators(false, true);
BlocksWithIdRemoved(blocksToRemove);
EnableGenerators(oldEnabled, true);
}
private void BlocksDeformed(List blockToDestroy)
{
foreach (var pos in blockToDestroy)
{
var block = GetCubeBlock(pos);
if (block != null)
{
ApplyDestructionDeformationInternal(block, false);
Physics.AddDirtyBlock(block);
}
}
}
[Event,Reliable,Broadcast]
private void BlockIntegrityChanged(Vector3I pos, ushort subBlockId, float buildIntegrity, float integrity, MyIntegrityChangeEnum integrityChangeType, long grinderOwner)
{
MyCompoundCubeBlock compoundBlock = null;
var block = GetCubeBlock(pos);
if (block != null)
compoundBlock = block.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
block = compoundBlock.GetBlock(subBlockId);
//Debug.Assert(block != null, "Attempting to change integrity of a non-existent block!");
if (block != null)
{
block.SetIntegrity(buildIntegrity, integrity, integrityChangeType, grinderOwner);
}
}
[Event, Reliable, Broadcast]
private void BlockStockpileChanged(Vector3I pos, ushort subBlockId, List items)
{
var block = GetCubeBlock(pos);
MyCompoundCubeBlock compoundBlock = null;
if (block != null)
compoundBlock = block.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
block = compoundBlock.GetBlock(subBlockId);
Debug.Assert(block != null, "Attempting to change stockpile of a non-existent block!");
if (block != null)
{
block.ChangeStockpile(items);
}
}
[Event, Reliable, Broadcast]
private void FractureComponentRepaired(Vector3I pos, ushort subBlockId, long toolOwner)
{
Debug.Assert(!Sync.IsServer);
MyCompoundCubeBlock compoundBlock = null;
var block = GetCubeBlock(pos);
if (block != null)
compoundBlock = block.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
block = compoundBlock.GetBlock(subBlockId);
if (block != null && block.FatBlock != null)
{
block.RepairFracturedBlock(toolOwner);
}
}
private void RemoveBlockByCubeBuilder(MySlimBlock block)
{
RemoveBlockInternal(block, true);
if (block.FatBlock != null)
{
block.FatBlock.OnRemovedByCubeBuilder();
}
}
///
/// Removes block, should be used only by server or on server request
///
private void RemoveBlockInternal(MySlimBlock block, bool close, bool markDirtyDisconnects = true)
{
if (!m_cubeBlocks.Contains(block))
{
Debug.Fail("Block being removed twice");
return;
}
if (MyFakes.ENABLE_MULTIBLOCK_PART_IDS)
{
RemoveMultiBlockInfo(block);
}
RenderData.RemoveDecals(block.Position);
ProfilerShort.Begin("Remove terminal block");
var terminalBlock = block.FatBlock as MyTerminalBlock;
if (terminalBlock != null)
{
for (int i = 0; i < BlockGroups.Count; i++)
{
var group = BlockGroups[i];
if (group.Blocks.Contains(terminalBlock))
group.Blocks.Remove(terminalBlock);
if (group.Blocks.Count == 0)
{
RemoveGroup(group);
i--;
}
}
}
ProfilerShort.End();
bool removed;
ProfilerShort.Begin("Remove cubes");
Vector3I temp = block.Min;
for (Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref block.Min, ref block.Max); it.IsValid(); it.GetNext(out temp))
{
removed = RemoveCube(temp);
Debug.Assert(removed, "Cube to remove was not found");
}
ProfilerShort.End();
ProfilerShort.Begin("RemoveBlockEdges");
RemoveBlockEdges(block);
ProfilerShort.End();
if (block.FatBlock != null)
{
if (BlocksCounters.ContainsKey(block.BlockDefinition.Id.TypeId))
BlocksCounters[block.BlockDefinition.Id.TypeId]--;
ProfilerShort.Begin("Unregister");
block.FatBlock.IsBeingRemoved = true;
GridSystems.UnregisterFromSystems(block.FatBlock);
ProfilerShort.End();
if (close)
{
ProfilerShort.Begin("Mark for close");
block.FatBlock.Close();
ProfilerShort.End();
}
else
{
ProfilerShort.Begin("Remove child");
Hierarchy.RemoveChild(block.FatBlock);
ProfilerShort.End();
}
ProfilerShort.Begin("UnregisterForDraw");
if (block.FatBlock.Render.NeedsDrawFromParent)
m_blocksForDraw.Remove(block.FatBlock);
ProfilerShort.End();
}
ProfilerShort.Begin("Remove Neighbours");
block.RemoveNeighbours();
ProfilerShort.End();
ProfilerShort.Begin("Remove Ownership");
block.RemoveAuthorship();
if (OnAuthorshipChanged != null)
OnAuthorshipChanged(this);
ProfilerShort.End();
ProfilerShort.Begin("Remove");
m_cubeBlocks.Remove(block);
if (block.FatBlock != null)
{
if (block.FatBlock is MyReactor)
NumberOfReactors--;
m_fatBlocks.Remove(block.FatBlock);
block.FatBlock.IsBeingRemoved = false;
}
ProfilerShort.End();
if (markDirtyDisconnects)
m_disconnectsDirty = true;
Vector3I cube = block.Min;
for (Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref block.Min, ref block.Max); it.IsValid(); it.GetNext(out cube))
Skeleton.MarkCubeRemoved(ref cube);
ProfilerShort.Begin("OnBlockRemoved");
if (block.FatBlock != null && block.FatBlock.IDModule != null)
ChangeOwner(block.FatBlock, block.FatBlock.IDModule.Owner, 0);
if (MyCubeGridSmallToLargeConnection.Static != null && m_enableSmallToLargeConnections)
{
ProfilerShort.Begin("CheckRemovedBlockSmallToLargeConnection");
MyCubeGridSmallToLargeConnection.Static.RemoveBlockSmallToLargeConnection(block);
ProfilerShort.End();
}
NotifyBlockRemoved(block);
if (close)
NotifyBlockClosed(block);
ProfilerShort.End();
m_boundsDirty = true;
}
public void RemoveBlock(MySlimBlock block, bool updatePhysics = false)
{
// Client cannot remove blocks, only server
if (!Sync.IsServer)
return;
if (!m_cubeBlocks.Contains(block))
{
Debug.Fail("Block being removed twice");
return;
}
EnqueueRemovedBlock(block.Min, m_generatorsEnabled);
RemoveBlockInternal(block, close: true);
if (updatePhysics)
{
this.Physics.AddDirtyBlock(block);
}
}
public void RemoveBlockWithId(MySlimBlock block, bool updatePhysics = false)
{
var cb = GetCubeBlock(block.Min);
if (cb == null)
return;
// Prepare block from compound
MyCompoundCubeBlock compoundBlock = cb.FatBlock as MyCompoundCubeBlock;
ushort? compoundId = null;
if (compoundBlock != null)
{
compoundId = compoundBlock.GetBlockId(block);
Debug.Assert(compoundId != null, "Block not found in compound");
if (compoundId == null)
return;
}
RemoveBlockWithId(block.Min, compoundId, updatePhysics: updatePhysics);
}
public void RemoveBlockWithId(Vector3I position, ushort? compoundId, bool updatePhysics = false)
{
// Client cannot remove blocks, only server
if (!Sync.IsServer)
return;
var block = GetCubeBlock(position);
if (block == null)
return;
EnqueueRemovedBlockWithId(block.Min, compoundId, m_generatorsEnabled);
if (compoundId != null)
{
Vector3I min = Vector3I.Zero, max = Vector3I.Zero;
RemoveBlockInCompound(block.Min, compoundId.Value, ref min, ref max);
}
else
{
RemoveBlockInternal(block, close: true);
}
if (updatePhysics)
{
this.Physics.AddDirtyBlock(block);
}
}
public void UpdateBlockNeighbours(MySlimBlock block)
{
if (!m_cubeBlocks.Contains(block))
{
Debug.Assert(false, "Grid does not contain the given block!");
return;
}
block.RemoveNeighbours();
block.AddNeighbours();
m_disconnectsDirty = true;
}
///
/// Returns cube corner which is closest to position
///
public Vector3 GetClosestCorner(Vector3I gridPos, Vector3 position)
{
return gridPos * GridSize - Vector3.SignNonZero(gridPos * GridSize - position) * GridSizeHalf;
}
public void DetectDisconnectsAfterFrame()
{
m_disconnectsDirty = true;
}
// Test method to detect disconnects
// OPTIMIZE: Create tree from cubes, tree node will be cube or cube cycle (cubes circularly connected cubes)
// Branch detachment = disconnect
// When cycle is broken, it will be expanded to branch/subtree
private void DetectDisconnects()
{
Debug.Assert(m_disconnectsDirty, "Do not call unless needed to (after block was removed).");
if (!MyFakes.DETECT_DISCONNECTS)
return;
if (m_cubes.Count == 0)
return;
if (!Sync.IsServer)
return;
MyPerformanceCounter.PerCameraDrawRead.CustomTimers.Remove("Mount points");
MyPerformanceCounter.PerCameraDrawWrite.CustomTimers.Remove("Mount points");
MyPerformanceCounter.PerCameraDrawRead.CustomTimers.Remove("Disconnect");
MyPerformanceCounter.PerCameraDrawWrite.CustomTimers.Remove("Disconnect");
MyPerformanceCounter.PerCameraDrawRead.StartTimer("Disconnect");
MyPerformanceCounter.PerCameraDrawWrite.StartTimer("Disconnect");
ProfilerShort.Begin("Detect disconnects");
m_disconnectHelper.Disconnect(this);
m_disconnectsDirty = false;
ProfilerShort.End();
MyPerformanceCounter.PerCameraDrawRead.StopTimer("Disconnect");
MyPerformanceCounter.PerCameraDrawWrite.StopTimer("Disconnect");
}
public bool CubeExists(Vector3I pos)
{
return m_cubes.ContainsKey(pos);
}
public void UpdateDirty()
{
ProfilerShort.Begin("Update dirty");
ProfilerShort.Begin("Update parts");
foreach (var pos in m_dirtyRegion.Cubes)
{
UpdateParts(pos);
}
ProfilerShort.End();
ProfilerShort.Begin("Update edges");
foreach (var pos in m_dirtyRegion.Cubes)
{
var block = GetCubeBlock(pos);
//Don't add edges for projections (dithering < 0)
if (block != null && block.ShowParts && MyFakes.ENABLE_EDGES)
{
if (block.Dithering >= 0f)
{
// Edges are filtered (to remove duplicates) in render data
AddBlockEdges(block);
}
else
{
RemoveBlockEdges(block);
}
block.UpdateMaxDeformation();
}
if (block != null && block.FatBlock != null && block.FatBlock.Render != null)
{
if (block.FatBlock.Render.NeedsDrawFromParent)
{
m_blocksForDraw.Add(block.FatBlock);
block.FatBlock.Render.NeedsDraw = false; //blocks shouldnt be drawn on their own at all?
}
}
}
ProfilerShort.End();
ProfilerShort.Begin("Update groups");
UpdateInstanceData();
ProfilerShort.End();
m_dirtyRegion.Clear();
ProfilerShort.End();
}
public bool IsDirty()
{
return m_dirtyRegion.IsDirty;
}
public void UpdateInstanceData()
{
ProfilerShort.Begin("Rebuild dirty cells");
Render.RebuildDirtyCells();
//no changes to volume actually made
//if they were it would be marked dirty
//PositionComp.UpdateWorldVolume();
ProfilerShort.End();
}
public bool TryGetCube(Vector3I position, out MyCube cube)
{
return m_cubes.TryGetValue(position, out cube);
}
///
/// Add new cube in the grid
///
///
///
///
///
/// false if add failed (can be caused be block structure change during the development
private bool AddCube(MySlimBlock block, ref Vector3I pos, Matrix rotation, MyCubeBlockDefinition cubeBlockDefinition)
{
//Debug.Assert(false, "AddCube");
MyCube c = new MyCube();
c.Parts = MyCubeGrid.GetCubeParts(cubeBlockDefinition, pos, rotation, GridSize, GridScale);
c.CubeBlock = block;
if (!m_cubes.ContainsKey(pos))
m_cubes.Add(pos, c);
else
return false; //whole block will be removed
m_dirtyRegion.AddCube(pos);
return true;
}
private MyCube CreateCube(MySlimBlock block, Vector3I pos, Matrix rotation, MyCubeBlockDefinition cubeBlockDefinition)
{
MyCube c = new MyCube();
c.Parts = MyCubeGrid.GetCubeParts(cubeBlockDefinition, pos, rotation, GridSize, GridScale);
c.CubeBlock = block;
return c;
}
//Temp public for debugging
public bool ChangeColor(MySlimBlock block, Vector3 newHSV)
{
ProfilerShort.Begin("MyCubeGrid.ChangeColor");
try
{
if (block.ColorMaskHSV == newHSV)
return false;
block.ColorMaskHSV = newHSV;
block.UpdateVisual(false); // Don't trigger physics updates for color changes
return true;
}
finally
{
ProfilerShort.End();
}
}
private void UpdatePartInstanceData(MyCubePart part, Vector3I cubePos)
{
MyCube cube;
m_cubes.TryGetValue(cubePos, out cube);
MySlimBlock block = cube.CubeBlock as MySlimBlock;
if (block != null)
{
part.InstanceData.SetColorMaskHSV(new Vector4(block.ColorMaskHSV, block.Dithering));
}
{
Debug.Assert(part.Model.BoneMapping != null, String.Format("Bones not set for model '{0}', add BoneGridSize tag to XML and rebuild model", part.Model.AssetName));
if (part.Model.BoneMapping != null)
{
var orientation = part.InstanceData.LocalMatrix.GetOrientation();
bool enableSkinning = false;
part.InstanceData.BoneRange = GridSize;
for (int index = 0; index < Math.Min(part.Model.BoneMapping.Length, 9); index++)
{
// Bone offset, from 0,0,0 to 2,2,2 inclusive
var boneOffset = part.Model.BoneMapping[index];
// -1,-1,-1 to 1,1,1
Vector3 centered = boneOffset * 1.0f - Vector3.One;
var transformedOffset = Vector3I.Round(Vector3.Transform(centered * 1.0f, orientation) + Vector3.One);
Vector3 bonePos = Skeleton.GetBone(cubePos, transformedOffset);
var byteBone = Vector3UByte.Normalize(bonePos, GridSize);
if (!Vector3UByte.IsMiddle(byteBone))
enableSkinning = true;
// We need to copy bone anyway, because even when this bone is zero, other bones may not be
part.InstanceData[index] = byteBone;
}
part.InstanceData.EnableSkinning = enableSkinning;
}
}
}
private void UpdateParts(Vector3I pos)
{
MyCube cube;
bool exists = m_cubes.TryGetValue(pos, out cube);
if (exists && cube.CubeBlock.ShowParts) // Cube exists
{
var gridDefinition = cube.CubeBlock.BlockDefinition;
MyTileDefinition[] tiles = MyCubeGridDefinitions.GetCubeTiles(gridDefinition);
Matrix orientation;
cube.CubeBlock.Orientation.GetMatrix(out orientation);
if (Skeleton.IsDeformed(pos, 0.004f * GridSize, this, false))
{
RemoveBlockEdges(cube.CubeBlock);
}
for (int i = 0; i < cube.Parts.Length; i++)
{
UpdatePartInstanceData(cube.Parts[i], pos);
// When part is already present, it doesn't mind, hashset can handle that
Render.RenderData.AddCubePart(cube.Parts[i]);
var tile = tiles[i];
if (tile.IsEmpty)
continue;
var normal = Vector3.TransformNormal(tile.Normal, orientation);
var up = Vector3.TransformNormal(tile.Up, orientation);
// Only axis aligned direction apply
if (!Base6Directions.IsBaseDirection(ref normal))
continue;
// Find neighbour in direction of normal
var neighbour = pos + Vector3I.Round(normal);
MyCube neighbourCube;
if (m_cubes.TryGetValue(neighbour, out neighbourCube) && neighbourCube.CubeBlock.ShowParts)
{
Matrix neighbourOrientation;
neighbourCube.CubeBlock.Orientation.GetMatrix(out neighbourOrientation);
var neighbourDefinition = neighbourCube.CubeBlock.BlockDefinition;
MyTileDefinition[] neighbourTiles = MyCubeGridDefinitions.GetCubeTiles(neighbourDefinition);
for (int j = 0; j < neighbourCube.Parts.Length; j++)
{
var neighbourTile = neighbourTiles[j];
if (neighbourTile.IsEmpty)
continue;
var neighbourNormal = Vector3.TransformNormal(neighbourTile.Normal, neighbourOrientation);
if ((normal + neighbourNormal).LengthSquared() < 0.001f)
{
// Different dithering = add tile, same dithering = remove tile
if (neighbourCube.CubeBlock.Dithering != cube.CubeBlock.Dithering)
{
// Doesn't matter if already added, hashset handle that
Render.RenderData.AddCubePart(neighbourCube.Parts[j]);
}
else
{
bool isAnyFull = false;
if (neighbourTile.FullQuad && !tile.IsRounded)
{
Render.RenderData.RemoveCubePart(cube.Parts[i]);
isAnyFull = true;
}
if (tile.FullQuad && !neighbourTile.IsRounded)
{
Render.RenderData.RemoveCubePart(neighbourCube.Parts[j]);
isAnyFull = true;
}
if (!isAnyFull && (neighbourTile.Up * tile.Up).LengthSquared() > 0.001f)
{
var neighbourUp = Vector3.TransformNormal(neighbourTile.Up, neighbourOrientation);
if ((neighbourUp - up).LengthSquared() < 0.001f)
{
if (!tile.IsRounded || neighbourTile.IsRounded)
{
Render.RenderData.RemoveCubePart(cube.Parts[i]);
}
if (tile.IsRounded || !neighbourTile.IsRounded)
{
Render.RenderData.RemoveCubePart(neighbourCube.Parts[j]);
}
}
}
}
}
}
}
}
}
else // Cubes was probably removed
{
if (exists)
{
foreach (var part in cube.Parts)
{
Render.RenderData.RemoveCubePart(part);
}
}
// Foreach neighbour, add shared tile
foreach (var dir in Base6Directions.Directions)
{
var neighbour = pos + Vector3I.Round(dir);
MyCube neighbourCube;
if (m_cubes.TryGetValue(neighbour, out neighbourCube) && neighbourCube.CubeBlock.ShowParts)
{
Matrix neighbourOrientation;
neighbourCube.CubeBlock.Orientation.GetMatrix(out neighbourOrientation);
var neighbourDefinition = neighbourCube.CubeBlock.BlockDefinition;
MyTileDefinition[] neighbourTiles = MyCubeGridDefinitions.GetCubeTiles(neighbourDefinition);
for (int j = 0; j < neighbourCube.Parts.Length; j++)
{
var neighbourTile = neighbourTiles[j];
var neighbourNormal = Vector3.Normalize(Vector3.TransformNormal(neighbourTile.Normal, neighbourOrientation));
if ((dir + neighbourNormal).LengthSquared() < 0.001f)
{
// Doesn't matter if already added, hashset handle that
Render.RenderData.AddCubePart(neighbourCube.Parts[j]);
}
}
}
}
}
}
private void RemoveRedundantParts()
{
foreach (var pair in m_cubes)
{
UpdateParts(pair.Key);
}
}
private void BoundsInclude(MySlimBlock block)
{
//Debug.Assert(block != null, "Block cannot be null");
if (block != null)
{
m_min = Vector3I.Min(m_min, block.Min);
m_max = Vector3I.Max(m_max, block.Max);
}
}
private void BoundsIncludeUpdateAABB(MySlimBlock block)
{
BoundsInclude(block);
UpdateGridAABB();
}
private void RecalcBounds()
{
m_min = Vector3I.MaxValue;
m_max = Vector3I.MinValue;
if (m_cubes.Count > 0)
{
foreach (var c in m_cubes)
{
m_min = Vector3I.Min(m_min, c.Key);
m_max = Vector3I.Max(m_max, c.Key);
}
}
UpdateGridAABB();
}
private void UpdateGridAABB()
{
PositionComp.LocalAABB = new BoundingBox(m_min * GridSize - GridSizeHalfVector, m_max * GridSize + GridSizeHalfVector);
}
private void ResetSkeleton()
{
Skeleton = new MyGridSkeleton();
}
///
/// For moving bones in corner, offset must contain two one's (positive or negative) and one zero
///
private bool MoveCornerBones(Vector3I cubePos, Vector3I offset, ref Vector3I minCube, ref Vector3I maxCube)
{
var absOffset = Vector3I.Abs(offset);
var shift = Vector3I.Shift(absOffset);
Vector3I a = offset * shift;
Vector3I b = offset * Vector3I.Shift(shift);
Vector3 clamp = GridSizeHalfVector;
bool exists = m_cubes.ContainsKey(cubePos + offset);
exists &= m_cubes.ContainsKey(cubePos + a);
exists &= m_cubes.ContainsKey(cubePos + b);
// Three in corner exists, do bone transform
if (exists)
{
var normal = Vector3I.One - absOffset;
var centerBone = Vector3I.One;
var targetBone = centerBone + offset;
var upper = targetBone + normal;
var lower = targetBone - normal;
Vector3 moveDirection = -offset * MyGridConstants.CORNER_BONE_MOVE_DISTANCE; // 1m per axis in direction
// Performance update: Instead of moveDirection.Length() we have precomputed the lengths, as the moveDirection is always constant * GridSize because of the constraint that
// offset always has to contain 2x 1, and 1x 0. Since offset length will thus always be the same, we can treat it as a constant, and save the square root performance call.
if (m_precalculatedCornerBonesDisplacementDistance <= 0)
m_precalculatedCornerBonesDisplacementDistance = moveDirection.Length();
float displacementLength = m_precalculatedCornerBonesDisplacementDistance;
// Now accomodate for grid size
displacementLength *= GridSize;
moveDirection *= GridSize;
// The bones here exists
MoveBone(ref cubePos, ref targetBone, ref moveDirection, ref displacementLength, ref clamp);
MoveBone(ref cubePos, ref upper, ref moveDirection, ref displacementLength, ref clamp);
MoveBone(ref cubePos, ref lower, ref moveDirection, ref displacementLength, ref clamp);
minCube = Vector3I.Min(Vector3I.Min(cubePos, minCube), cubePos + offset - normal);
maxCube = Vector3I.Max(Vector3I.Max(cubePos, maxCube), cubePos + offset + normal);
}
return exists;
}
public void GetExistingBones(Vector3I boneMin, Vector3I boneMax, Dictionary resultSet, MyDamageInformation? damageInfo = null)
{
Vector3I cubeMin = Vector3I.Floor((boneMin - Vector3I.One) / (float)MyGridSkeleton.BoneDensity);
Vector3I cubeMax = Vector3I.Ceiling((boneMax - Vector3I.One) / (float)MyGridSkeleton.BoneDensity);
MyDamageInformation info = damageInfo.HasValue ? damageInfo.Value : default(MyDamageInformation);
resultSet.Clear();
Vector3I cube, boneBase;
MyCube cubeInst;
for (cube.X = cubeMin.X; cube.X <= cubeMax.X; cube.X++)
for (cube.Y = cubeMin.Y; cube.Y <= cubeMax.Y; cube.Y++)
for (cube.Z = cubeMin.Z; cube.Z <= cubeMax.Z; cube.Z++)
{
if (m_cubes.TryGetValue(cube, out cubeInst) && cubeInst.CubeBlock.UsesDeformation)
{
if (cubeInst.CubeBlock.UseDamageSystem && damageInfo.HasValue)
{
info.Amount = 1;
MyDamageSystem.Static.RaiseBeforeDamageApplied(cubeInst.CubeBlock, ref info);
if (info.Amount == 0)
continue;
}
//TODO:perf: in plane of armor this leads to lot of overrides on block edges
//3*3 = 9 bones are shared with neighbor - overriden in next iteration
boneBase = cube * MyGridSkeleton.BoneDensity;
foreach (var offset in Skeleton.BoneOffsets)
resultSet[boneBase + offset] = cubeInst.CubeBlock;
}
}
}
private void MoveBone(ref Vector3I cubePos, ref Vector3I boneOffset, ref Vector3 moveDirection, ref float displacementLength, ref Vector3 clamp)
{
m_totalBoneDisplacement += displacementLength;
var pos = cubePos * MyGridSkeleton.BoneDensity + boneOffset;
var boneValue = Skeleton[pos] + moveDirection;
Skeleton[pos] = Vector3.Clamp(boneValue, -clamp, clamp);
}
private bool RemoveCube(Vector3I pos)
{
MyCube cube;
if (m_cubes.TryGetValue(pos, out cube))
{
foreach (var p in cube.Parts)
{
Render.RenderData.RemoveCubePart(p);
}
m_cubes.Remove(pos);
m_dirtyRegion.AddCube(pos);
return true;
}
return false;
}
///
/// Tries to merge this grid with any other grid
/// Merges grids only when both are static
/// Returns the merged grid (which does not necessarily have to be this grid)
///
public MyCubeGrid DetectMerge(MySlimBlock block, MyCubeGrid ignore = null, List nearEntities = null)
{
if (!this.IsStatic)
return null;
if (!Sync.IsServer)
return null;
if (block == null)
return null;
MyCubeGrid retval = null;
ProfilerShort.Begin("Test merge");
BoundingBoxD aabb = (BoundingBoxD)new BoundingBox(block.Min * GridSize - GridSizeHalf, block.Max * GridSize + GridSizeHalf);
// Inflate by half cube, so it will intersect for sure when there's anything
aabb.Inflate(GridSizeHalf);
aabb = aabb.TransformFast(WorldMatrix);
bool clearNearEntities = false;
if (nearEntities == null)
{
clearNearEntities = true;
nearEntities = MyEntities.GetEntitiesInAABB(ref aabb);
}
for (int i = 0; i < nearEntities.Count; i++)
{
Vector3I gridOffset;
var grid = nearEntities[i] as MyCubeGrid;
var mergingGrid = retval != null ? retval : this;
if (grid != null && grid != this && grid.Physics != null && grid.Physics.Enabled && grid != ignore && grid.IsStatic && grid.GridSizeEnum == mergingGrid.GridSizeEnum
&& mergingGrid.IsMergePossible_Static(block, grid, out gridOffset))
{
MyCubeGrid mergeTo = mergingGrid;
MyCubeGrid mergeFrom = grid;
// Change the merge order if needed.
if (grid.BlocksCount > mergingGrid.BlocksCount)
{
mergeTo = grid;
mergeFrom = mergingGrid;
}
Vector3D gridPosition = mergeFrom.PositionComp.GetPosition();
gridPosition = Vector3D.Transform(gridPosition, mergeTo.PositionComp.WorldMatrixNormalizedInv);
Vector3I gridOffsetFoo = Vector3I.Round(gridPosition * GridSizeR);
MyCubeGrid localMergedGrid = mergeTo.MergeGrid_Static(mergeFrom, gridOffsetFoo, block);
if (localMergedGrid != null)
retval = localMergedGrid;
}
}
if (clearNearEntities)
nearEntities.Clear(); // We don't want to hold references to objects and keep them alive!
ProfilerShort.End();
return retval;
}
/// Offset of second grid
private bool IsMergePossible_Static(MySlimBlock block, MyCubeGrid gridToMerge, out Vector3I gridOffset)
{
//Debug.Assert(this.WorldMatrix.Up == Vector3D.Up && this.WorldMatrix.Forward == Vector3D.Forward, "This grid must have identity rotation");
//Debug.Assert(gridToMerge.WorldMatrix.Up == Vector3D.Up && gridToMerge.WorldMatrix.Forward == Vector3D.Forward, "Grid to merge must have identity rotation");
Vector3D thisMergePos = this.PositionComp.GetPosition();
thisMergePos = Vector3D.Transform(thisMergePos, gridToMerge.PositionComp.WorldMatrixNormalizedInv);
gridOffset = -Vector3I.Round((thisMergePos) * GridSizeR);
if (!IsOrientationsAligned(gridToMerge.WorldMatrix, this.WorldMatrix))
return false;
var transform = gridToMerge.CalculateMergeTransform(this, -gridOffset);
Vector3I blockPosInSecondGrid;
Vector3I.Transform(ref block.Position, ref transform, out blockPosInSecondGrid);
MyBlockOrientation newOrientation = MatrixI.Transform(ref block.Orientation, ref transform);
Quaternion blockOrientation;
newOrientation.GetQuaternion(out blockOrientation);
var mountPoints = block.BlockDefinition.GetBuildProgressModelMountPoints(block.BuildLevelRatio);
return CheckConnectivity(gridToMerge, block.BlockDefinition, mountPoints, ref blockOrientation, ref blockPosInSecondGrid);
}
public MatrixI CalculateMergeTransform(MyCubeGrid gridToMerge, Vector3I gridOffset)
{
Vector3 fw = (Vector3)Vector3D.TransformNormal(gridToMerge.WorldMatrix.Forward, this.PositionComp.WorldMatrixNormalizedInv);
Vector3 up = (Vector3)Vector3D.TransformNormal(gridToMerge.WorldMatrix.Up, this.PositionComp.WorldMatrixNormalizedInv);
Base6Directions.Direction fwDir = Base6Directions.GetClosestDirection(fw);
Base6Directions.Direction upDir = Base6Directions.GetClosestDirection(up);
if (upDir == fwDir) upDir = Base6Directions.GetPerpendicular(fwDir);
MatrixI transform = new MatrixI(ref gridOffset, fwDir, upDir);
return transform;
}
public bool CanMergeCubes(MyCubeGrid gridToMerge, Vector3I gridOffset)
{
MatrixI transform = CalculateMergeTransform(gridToMerge, gridOffset);
foreach (var cubesEntry in gridToMerge.m_cubes)
{
Vector3I position = Vector3I.Transform(cubesEntry.Key, transform);
if (this.m_cubes.ContainsKey(position))
{
MySlimBlock localBlock = GetCubeBlock(position);
if (localBlock != null && localBlock.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock localCompoundBlock = localBlock.FatBlock as MyCompoundCubeBlock;
MySlimBlock blockInMergeGrid = gridToMerge.GetCubeBlock(cubesEntry.Key);
if (blockInMergeGrid.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock compoundInMergeGrid = blockInMergeGrid.FatBlock as MyCompoundCubeBlock;
bool canMerge = true;
foreach (var blockInCompund in compoundInMergeGrid.GetBlocks())
{
MyBlockOrientation newOrientation = MatrixI.Transform(ref blockInCompund.Orientation, ref transform);
if (!localCompoundBlock.CanAddBlock(blockInCompund.BlockDefinition, newOrientation))
{
canMerge = false;
break;
}
}
if (canMerge)
continue;
}
else
{
MyBlockOrientation newOrientation = MatrixI.Transform(ref blockInMergeGrid.Orientation, ref transform);
if (localCompoundBlock.CanAddBlock(blockInMergeGrid.BlockDefinition, newOrientation))
continue;
}
}
return false;
}
}
return true;
}
private MyCubeGrid MergeGrid_Static(MyCubeGrid gridToMerge, Vector3I gridOffset, MySlimBlock triggeringMergeBlock)
{
Debug.Assert(this.IsStatic && gridToMerge.IsStatic, "Grids to merge must be static");
// We have to force replicate grids before merging them on client. Otherwise he won't have one of them and fails tragically.
MyMultiplayer.ReplicateImmediatelly(gridToMerge, this);
MyMultiplayer.ReplicateImmediatelly(this, gridToMerge);
MatrixI transform = CalculateMergeTransform(gridToMerge, gridOffset);
// Get transformed position of block that triggered merge.
Vector3I mergingBlockPos = triggeringMergeBlock.Position;
if (triggeringMergeBlock.CubeGrid != this)
mergingBlockPos = Vector3I.Transform(mergingBlockPos, transform);
MyMultiplayer.RaiseBlockingEvent(this, gridToMerge, x => x.MergeGrid_MergeClient, gridToMerge.EntityId, (SerializableVector3I)transform.Translation, transform.Forward, transform.Up, mergingBlockPos);
MyCubeGrid newGrid = MergeGridInternal(gridToMerge, ref transform);
newGrid.AdditionalModelGenerators.ForEach(g => g.BlockAddedToMergedGrid(triggeringMergeBlock));
return newGrid;
}
///
/// Merges grids on client side.
///
/// Grid id to be merge with.
/// Grid offset.
/// Grid forward.
/// Grid Up.
/// Position of block that triggered merge.
[Event, Reliable, Broadcast, Blocking]
void MergeGrid_MergeClient(long gridId, SerializableVector3I gridOffset, Base6Directions.Direction gridForward, Base6Directions.Direction gridUp, Vector3I mergingBlockPos)
{
MyCubeGrid grid = null;
bool found = MyEntities.TryGetEntityById(gridId, out grid);
Debug.Assert(found, "Grid not found ! please call Dusan");
if (found)
{
MatrixI transform = new MatrixI(gridOffset, gridForward, gridUp);
MyCubeGrid newGrid = MergeGridInternal(grid, ref transform);
// Get block that triggered merge and regenerate Model Generators.
MySlimBlock triggeringMergeBlock = newGrid.GetCubeBlock(mergingBlockPos);
newGrid.AdditionalModelGenerators.ForEach(g => g.BlockAddedToMergedGrid(triggeringMergeBlock));
}
}
public MyCubeGrid MergeGrid_MergeBlock(MyCubeGrid gridToMerge, Vector3I gridOffset)
{
Debug.Assert(Sync.IsServer);
// Always merge smaller grid to larger
if ((this.BlocksCount < gridToMerge.BlocksCount && !this.IsStatic) || (!this.IsStatic && gridToMerge.IsStatic))
{
return null;
}
MatrixI transform = CalculateMergeTransform(gridToMerge, gridOffset);
MyMultiplayer.ReplicateImmediatelly(gridToMerge, this);
MyMultiplayer.ReplicateImmediatelly(this, gridToMerge);
MyMultiplayer.RaiseBlockingEvent(this, gridToMerge, x => x.MergeGrid_MergeBlockClient, gridToMerge.EntityId, (SerializableVector3I)transform.Translation, transform.Forward, transform.Up);
return MergeGridInternal(gridToMerge, ref transform);
}
[Event, Reliable, Broadcast, Blocking]
void MergeGrid_MergeBlockClient(long gridId, SerializableVector3I gridOffset, Base6Directions.Direction gridForward, Base6Directions.Direction gridUp)
{
MyCubeGrid grid = null;
bool found = MyEntities.TryGetEntityById(gridId, out grid);
Debug.Assert(found, "Grid not found ! please call Dusan");
if (found)
{
MatrixI transform = new MatrixI(gridOffset, gridForward, gridUp);
MergeGridInternal(grid, ref transform);
}
}
MyCubeGrid MergeGridInternal(MyCubeGrid gridToMerge, ref MatrixI transform, bool disableBlockGenerators = true)
{
ProfilerShort.Begin("MergeGridInternal");
if (MyCubeGridSmallToLargeConnection.Static != null)
MyCubeGridSmallToLargeConnection.Static.BeforeGridMerge_SmallToLargeGridConnectivity(this, gridToMerge);
MoveBlocksAndClose(gridToMerge, this, transform, disableBlockGenerators: disableBlockGenerators);
// Update AABB, physics, dirty blocks
UpdateGridAABB();
if (Physics != null)
{
// We need to update physics immediatelly because of landing gears
UpdatePhysicsShape();
}
if (MyCubeGridSmallToLargeConnection.Static != null)
MyCubeGridSmallToLargeConnection.Static.AfterGridMerge_SmallToLargeGridConnectivity(this);
ProfilerShort.End();
return this;
}
public void ChangeGridOwnership(long playerId, MyOwnershipShareModeEnum shareMode)
{
Debug.Assert(Sync.IsServer, "Changing grid ownership from the client");
if (!Sync.IsServer) return;
ChangeGridOwner(playerId, shareMode);
}
private static void MoveBlocks(MyCubeGrid from, MyCubeGrid to, List cubeBlocks, int offset, int count)
{
ProfilerShort.Begin("MoveBlocks");
from.EnableGenerators(false, true);
to.EnableGenerators(false, true);
try
{
m_tmpBlockGroups.Clear();
foreach (var group in from.BlockGroups)
{
// CH: TODO: This is to catch a nullref. Remove when not needed
if (group == null) MySandboxGame.Log.WriteLine("group in from.BlockGroups was null");
m_tmpBlockGroups.Add(group.GetObjectBuilder());
}
ProfilerShort.Begin("RemoveBlockInternal");
for (int i = offset; i < offset + count; i++)
{
var block = cubeBlocks[i];
if (block == null) continue;
if (block.FatBlock != null)
from.Hierarchy.RemoveChild(block.FatBlock);
from.RemoveBlockInternal(block, close: false, markDirtyDisconnects: false);
}
ProfilerShort.End();
ProfilerShort.Begin("AddDirtyBlock");
if (from.Physics != null)
{
for (int i = offset; i < offset + count; i++)
{
var block = cubeBlocks[i];
if (block == null) continue;
from.Physics.AddDirtyBlock(block);
}
/*from.UpdatePhysicsShape();
from.RaisePhysicsChanged();*/
}
ProfilerShort.End();
ProfilerShort.Begin("Add block & Copy skeleton");
for (int i = offset; i < offset + count; i++)
{
var block = cubeBlocks[i];
if (block == null) continue;
// CH: TODO: This is to catch a nullref. Remove when not needed
if (from.Skeleton == null) MySandboxGame.Log.WriteLine("from.Skeleton was null");
to.AddBlockInternal(block);
from.Skeleton.CopyTo(to.Skeleton, block.Position, block.Position);
}
ProfilerShort.End();
ProfilerShort.Begin("Block groups");
foreach (var groupBuilder in m_tmpBlockGroups)
{
var group = new MyBlockGroup(to);
// CH: TODO: This is to catch a nullref. Remove when not needed
if (group == null) MySandboxGame.Log.WriteLine("group was null");
if (groupBuilder == null) MySandboxGame.Log.WriteLine("groupBuilder was null");
group.Init(groupBuilder);
to.AddGroup(group);
}
m_tmpBlockGroups.Clear();
from.RemoveEmptyBlockGroups();
ProfilerShort.End();
}
finally
{
from.EnableGenerators(true, true);
to.EnableGenerators(true, true);
ProfilerShort.End();
}
}
private void RemoveEmptyBlockGroups()
{
for (int i = 0; i < BlockGroups.Count; i++)
{
var group = BlockGroups[i];
if (group.Blocks.Count == 0)
{
RemoveGroup(group);
i--;
}
}
}
///
/// Used only when all blocks of grid are moved.
/// Moving only some blocks is unsupported now.
///
static void MoveBlocksAndClose(MyCubeGrid from, MyCubeGrid to, MatrixI transform, bool disableBlockGenerators = true)
{
ProfilerShort.Begin("MoveBlocksAndClose");
from.MarkedForClose = true;
if (disableBlockGenerators)
{
from.EnableGenerators(false, true);
to.EnableGenerators(false, true);
}
ProfilerShort.Begin("MoveGroups");
while (from.BlockGroups.Count > 0)
{
var group = from.BlockGroups[0];
to.AddGroup(group);
// "from" group could have been reinstantiated by OnModifyGroupSuccess
// side effects: we have to remove it by name
from.RemoveGroupByName(group.Name.ToString());
}
ProfilerShort.End();
ProfilerShort.Begin("MyEntities.Remove(grid)");
MyEntities.Remove(from);
ProfilerShort.End();
// Unregister from systems (after removing all blocks from scene)
ProfilerShort.Begin("UnregisterBlocks");
from.UnregisterBlocksBeforeClose();
ProfilerShort.End();
// Remove from scene
ProfilerShort.Begin("RemoveBlocksFromScene");
foreach (var block in from.m_cubeBlocks)
{
if (block.FatBlock != null)
from.Hierarchy.RemoveChild(block.FatBlock, false);
ProfilerShort.Begin("Remove Neighbours");
block.RemoveNeighbours();
ProfilerShort.End();
block.RemoveAuthorship();
}
ProfilerShort.End();
ProfilerShort.Begin("ClosePhysics");
if (from.Physics != null)
{
from.Physics.Close();
from.Physics = null;
from.RaisePhysicsChanged();
}
ProfilerShort.End();
// Add blocks
ProfilerShort.Begin("AddBlocks");
foreach (var block in from.m_cubeBlocks)
{
ProfilerShort.Begin("TransformBlock");
block.Transform(ref transform);
ProfilerShort.End();
ProfilerShort.Begin("AddBlockInternal");
to.AddBlockInternal(block);
ProfilerShort.End();
}
ProfilerShort.End();
ProfilerShort.Begin("CopySkeleton");
from.Skeleton.CopyTo(to.Skeleton, transform, to);
ProfilerShort.End();
if (disableBlockGenerators)
{
from.EnableGenerators(true, true);
to.EnableGenerators(true, true);
}
ProfilerShort.Begin("ClearAndClose");
from.m_blocksForDraw.Clear();
from.m_cubeBlocks.Clear();
from.m_fatBlocks.Clear();
from.m_cubes.Clear();
from.MarkedForClose = false; // TODO: CH, if MarkedForClose is set manually above, it must be reset manually here
if (Sync.IsServer)
from.Close();
ProfilerShort.End();
ProfilerShort.End();
}
///
/// Adds the block to the grid. The block's position and orientation in the grid should be set elsewhere
///
private void AddBlockInternal(MySlimBlock block)
{
block.CubeGrid = this;
// Try merge compound blocks together.
if (MyFakes.ENABLE_COMPOUND_BLOCKS && block.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock compoundBlock = block.FatBlock as MyCompoundCubeBlock;
MySlimBlock existingSlimBlock = GetCubeBlock(block.Min);
MyCompoundCubeBlock existingCompoundBlock = existingSlimBlock != null ? existingSlimBlock.FatBlock as MyCompoundCubeBlock : null;
if (existingCompoundBlock != null)
{
bool added = false;
compoundBlock.UpdateWorldMatrix();
Debug.Assert(m_tmpSlimBlocks.Count == 0);
m_tmpSlimBlocks.Clear();
foreach (var blockInCompound in compoundBlock.GetBlocks())
{
ushort id;
if (existingCompoundBlock.Add(blockInCompound, out id))
{
BoundsInclude(blockInCompound);
m_dirtyRegion.AddCube(blockInCompound.Min);
Physics.AddDirtyBlock(existingSlimBlock);
m_tmpSlimBlocks.Add(blockInCompound);
added = true;
}
}
foreach (var blockToRemove in m_tmpSlimBlocks)
{
compoundBlock.Remove(blockToRemove, merged: true);
}
if (added)
{
if (MyCubeGridSmallToLargeConnection.Static != null && m_enableSmallToLargeConnections)
MyCubeGridSmallToLargeConnection.Static.AddBlockSmallToLargeConnection(block);
foreach (var blockAdded in m_tmpSlimBlocks)
{
NotifyBlockAdded(blockAdded);
}
}
m_tmpSlimBlocks.Clear();
return;
}
}
m_cubeBlocks.Add(block);
if (block.FatBlock != null)
m_fatBlocks.Add(block.FatBlock);
ProfilerShort.Begin("AddNeighbors");
block.AddNeighbours();
ProfilerShort.End();
BoundsInclude(block);
if (block.FatBlock != null)
{
ProfilerShort.Begin("FatBlock");
block.FatBlock.UpdateWorldMatrix();
// CH:TODO: This would actually be better than setting WorldMatrix to local matrix, but it causes inaccurate fat block
// positions for some reasons. Find out why.
//AddChildWithMatrix(block.FatBlock, ref local);
Hierarchy.AddChild(block.FatBlock, false);
GridSystems.RegisterInSystems(block.FatBlock);
if (block.FatBlock.Render.NeedsDrawFromParent)
{
m_blocksForDraw.Add(block.FatBlock);
block.FatBlock.Render.NeedsDraw = false; //blocks shouldnt be drawn on their own at all?
}
MyObjectBuilderType blockType = block.BlockDefinition.Id.TypeId;
if (blockType != typeof(MyObjectBuilder_CubeBlock))
{
if (!BlocksCounters.ContainsKey(blockType))
BlocksCounters.Add(blockType, 0);
BlocksCounters[blockType]++;
}
ProfilerShort.End();
}
ProfilerShort.Begin("AddCubes");
MyBlockOrientation blockOrientation = block.Orientation;
Matrix rotationMatrix;
blockOrientation.GetMatrix(out rotationMatrix);
bool blockAddSuccessfull = true;
Vector3I temp = new Vector3I();
for (temp.X = block.Min.X; temp.X <= block.Max.X; temp.X++)
for (temp.Y = block.Min.Y; temp.Y <= block.Max.Y; temp.Y++)
for (temp.Z = block.Min.Z; temp.Z <= block.Max.Z; temp.Z++)
{
blockAddSuccessfull &= AddCube(block, ref temp, rotationMatrix, block.BlockDefinition);
}
Debug.Assert(blockAddSuccessfull, "Cannot add block!");
ProfilerShort.End();
if (Physics != null)
{
ProfilerShort.Begin("Physics.AddBlock");
Physics.AddBlock(block);
ProfilerShort.End();
}
if (block.FatBlock != null)
{
ProfilerShort.Begin("Owner");
ChangeOwner(block.FatBlock, 0, block.FatBlock.OwnerId);
ProfilerShort.End();
}
if (MyCubeGridSmallToLargeConnection.Static != null && m_enableSmallToLargeConnections && blockAddSuccessfull)
{
ProfilerShort.Begin("CheckAddedBlockSmallToLargeConnection");
MyCubeGridSmallToLargeConnection.Static.AddBlockSmallToLargeConnection(block);
ProfilerShort.End();
}
if (MyFakes.ENABLE_MULTIBLOCK_PART_IDS)
{
ProfilerShort.Begin("AddMultiBlockInfo");
AddMultiBlockInfo(block);
ProfilerShort.End();
}
ProfilerShort.Begin("OnBlockAdded");
NotifyBlockAdded(block);
ProfilerShort.End();
ProfilerShort.Begin("AddAuthorship");
block.AddAuthorship();
if (OnAuthorshipChanged != null)
OnAuthorshipChanged(this);
ProfilerShort.End();
}
private bool IsDamaged(Vector3I bonePos, float epsilon = 0.04f)
{
Vector3 bone;
if(Skeleton.TryGetBone(ref bonePos, out bone))
return !MyUtils.IsZero(ref bone, epsilon * GridSize);
return false;
}
private void AddBlockEdges(MySlimBlock block)
{
var definition = block.BlockDefinition;
if (definition.BlockTopology == MyBlockTopology.Cube && definition.CubeDefinition != null)
{
if (!definition.CubeDefinition.ShowEdges)
return;
Vector3 position = block.Position * GridSize;
Matrix blockTransformMatrix;
block.Orientation.GetMatrix(out blockTransformMatrix);
blockTransformMatrix.Translation = position;
var info = MyCubeGridDefinitions.GetTopologyInfo(definition.CubeDefinition.CubeTopology);
var baseBonePos = block.Position * MyGridSkeleton.BoneDensity + Vector3I.One;
foreach (var edge in info.Edges)
{
Vector3 point0 = Vector3.TransformNormal(edge.Point0, block.Orientation);
Vector3 point1 = Vector3.TransformNormal(edge.Point1, block.Orientation);
Vector3 middle = (point0 + point1) * 0.5f;
if (IsDamaged(baseBonePos + Vector3I.Round(point0)) ||
IsDamaged(baseBonePos + Vector3I.Round(middle)) ||
IsDamaged(baseBonePos + Vector3I.Round(point1)))
continue;
point0 = Vector3.Transform(edge.Point0 * GridSizeHalf, ref blockTransformMatrix);
point1 = Vector3.Transform(edge.Point1 * GridSizeHalf, ref blockTransformMatrix);
Vector3 normal0 = Vector3.TransformNormal(info.Tiles[edge.Side0].Normal, block.Orientation);
Vector3 normal1 = Vector3.TransformNormal(info.Tiles[edge.Side1].Normal, block.Orientation);
// Saturation and Value is offset, from -1 to 1, it must be normalized
var hsvNormalized = block.ColorMaskHSV;
hsvNormalized.Y = (hsvNormalized.Y + 1) * 0.5f;
hsvNormalized.Z = (hsvNormalized.Z + 1) * 0.5f;
Render.RenderData.AddEdgeInfo(ref point0, ref point1, ref normal0, ref normal1, new Color(hsvNormalized), block);
}
}
}
private void RemoveBlockEdges(MySlimBlock block)
{
var definition = block.BlockDefinition;
if (definition.BlockTopology == MyBlockTopology.Cube && definition.CubeDefinition != null)
{
Vector3 position = block.Position * GridSize;
Matrix blockTransformMatrix;
block.Orientation.GetMatrix(out blockTransformMatrix);
blockTransformMatrix.Translation = position;
var info = MyCubeGridDefinitions.GetTopologyInfo(definition.CubeDefinition.CubeTopology);
Vector3 point0, point1;
foreach (var edge in info.Edges)
{
point0 = Vector3.Transform(edge.Point0 * GridSizeHalf, blockTransformMatrix);
point1 = Vector3.Transform(edge.Point1 * GridSizeHalf, blockTransformMatrix);
Render.RenderData.RemoveEdgeInfo(point0, point1, block);
}
}
}
private void DoLazyUpdates()
{
if (MyCubeGridSmallToLargeConnection.Static != null && !m_smallToLargeConnectionsInitialized && m_enableSmallToLargeConnections)
{
// set flag before calling the method AddGridSmallToLargeConnection
m_smallToLargeConnectionsInitialized = true;
MyCubeGridSmallToLargeConnection.Static.AddGridSmallToLargeConnection(this);
}
m_smallToLargeConnectionsInitialized = true;
ProfilerShort.Begin("Send dirty bones");
if (!MyPerGameSettings.Destruction && BonesToSend.InputCount > 0 && m_bonesSendCounter++ > 10) // Only increment counter when there's something waiting
{
m_bonesSendCounter = 0;
if (Sync.IsServer)
{
var segments = BonesToSend.FindSegments(MyVoxelSegmentationType.Simple);
foreach (var seg in segments)
{
SendDirtyBones(seg.Min, seg.Max, Skeleton);
}
}
BonesToSend.ClearInput();
}
ProfilerShort.End();
if (m_blocksForDamageApplicationDirty)
{
// Copy content to temporary, because block can be destroyed in "block.ApplyAccumulatedDamage()" and then it is also removed
// from m_blocksForDamageApplication (see MySlimBlock.OnDestroy).
m_blocksForDamageApplicationCopy.AddHashset(m_blocksForDamageApplication);
foreach (var block in m_blocksForDamageApplicationCopy)
{
if (block.AccumulatedDamage > 0f)
{
block.ApplyAccumulatedDamage();
}
}
m_blocksForDamageApplication.Clear();
m_blocksForDamageApplicationCopy.Clear();
m_blocksForDamageApplicationDirty = false;
}
if (m_disconnectsDirty)
{
DetectDisconnects();
}
if (!MyPerGameSettings.Destruction)
Skeleton.RemoveUnusedBones(this);
if (m_ownershipManager.NeedRecalculateOwners)
{
m_ownershipManager.RecalculateOwners();
m_ownershipManager.NeedRecalculateOwners = false;
NotifyBlockOwnershipChange(this);
}
}
internal void AddForDamageApplication(MySlimBlock block)
{
m_blocksForDamageApplication.Add(block);
m_blocksForDamageApplicationDirty = true;
}
internal void RemoveFromDamageApplication(MySlimBlock block)
{
m_blocksForDamageApplication.Remove(block);
m_blocksForDamageApplicationDirty = m_blocksForDamageApplication.Count > 0;
}
#region Ray casting, intersections and overlaps
//For ModAPI, they dont have havok yet
public bool GetLineIntersectionExactGrid(ref LineD line, ref Vector3I position, ref double distanceSquared)
{
return GetLineIntersectionExactGrid(ref line, ref position, ref distanceSquared, null);
}
public bool GetLineIntersectionExactGrid(ref LineD line, ref Vector3I position, ref double distanceSquared, MyPhysics.HitInfo? hitInfo = null)
{
RayCastCells(line.From, line.To, m_cacheRayCastCells, havokWorld: true);
if (m_cacheRayCastCells.Count == 0)
return false;
if (hitInfo.HasValue)
m_tmpHitList.Add(hitInfo.Value);
else
MyPhysics.CastRay(line.From, line.To, m_tmpHitList, MyPhysics.CollisionLayers.ObjectDetectionCollisionLayer);
bool result = false;
for (int i = 0; i < m_cacheRayCastCells.Count; i++)
{
Vector3I hit = m_cacheRayCastCells[i];
MyCube cube;
m_cubes.TryGetValue(hit, out cube);
double distSq = double.MaxValue;
if (cube == null || cube.CubeBlock.FatBlock == null || !cube.CubeBlock.FatBlock.BlockDefinition.UseModelIntersection)
{
if (m_tmpHitList.Count > 0)
{
int j = 0;
if (MySession.Static.ControlledEntity != null)
while (j < m_tmpHitList.Count - 1 && m_tmpHitList[j].HkHitInfo.GetHitEntity() == MySession.Static.ControlledEntity.Entity)
j++;
if (j > 1 && m_tmpHitList[j].HkHitInfo.GetHitEntity() != this)
continue;
var bias = GridSizeHalfVector;
var locPos = Vector3D.Transform(m_tmpHitList[j].Position, MatrixD.Invert(WorldMatrix));
var blockPos = hit * GridSize;
var dir = locPos - blockPos;
var max = dir.Max() > Math.Abs(dir.Min()) ? dir.Max() : dir.Min();
dir.X = dir.X == max ? max > 0 ? 1 : -1 : 0;
dir.Y = dir.Y == max ? max > 0 ? 1 : -1 : 0;
dir.Z = dir.Z == max ? max > 0 ? 1 : -1 : 0;
locPos -= dir * 0.06f;
if (Vector3D.Max(locPos, blockPos - bias) == locPos && Vector3D.Min(locPos, blockPos + bias) == locPos)
{
if (cube == null)
{
Vector3I nearest;
FixTargetCube(out nearest, locPos * GridSizeR);
if (!m_cubes.TryGetValue(nearest, out cube))
continue;
hit = nearest;
}
distSq = Vector3D.DistanceSquared(line.From, m_tmpHitList[j].Position);// Vector3.Transform(hit*GridSize, WorldMatrix));
if (distSq < distanceSquared)
{
position = hit;
distanceSquared = distSq;
result = true;
continue;
}
}
}
}
else
{
VRage.Game.Models.MyIntersectionResultLineTriangleEx? intersection;
int cubePartIndex;
GetBlockIntersection(cube, ref line, IntersectionFlags.ALL_TRIANGLES, out intersection, out cubePartIndex);
if (intersection.HasValue)
distSq = Vector3.DistanceSquared(line.From, intersection.Value.IntersectionPointInWorldSpace);
}
if (distSq < distanceSquared)
{
distanceSquared = distSq;
position = hit;
result = true;
}
}
m_tmpHitList.Clear();
return result;
}
private void GetBlockIntersection(MyCube cube, ref LineD line, IntersectionFlags flags, out MyIntersectionResultLineTriangleEx? t, out int cubePartIndex)
{
if (cube.CubeBlock.FatBlock != null)
{
if (cube.CubeBlock.FatBlock is MyCompoundCubeBlock)
{
MyCompoundCubeBlock compound = cube.CubeBlock.FatBlock as MyCompoundCubeBlock;
VRage.Game.Models.MyIntersectionResultLineTriangleEx? closestHit = null;
double closestDistance = double.MaxValue;
foreach (var block in compound.GetBlocks())
{
//model block
Matrix local;
block.Orientation.GetMatrix(out local);
Vector3 modelOffset;
Vector3.TransformNormal(ref block.BlockDefinition.ModelOffset, ref local, out modelOffset);
local.Translation = block.Position * GridSize + modelOffset;
MatrixD invLocal = MatrixD.Invert(block.FatBlock.WorldMatrix);
t = block.FatBlock.ModelCollision.GetTrianglePruningStructure().GetIntersectionWithLine(this, ref line, ref invLocal, flags);
if (t == null && block.FatBlock.Subparts != null)
{
foreach (var subpart in block.FatBlock.Subparts)
{
invLocal = MatrixD.Invert(subpart.Value.WorldMatrix);
t = subpart.Value.ModelCollision.GetTrianglePruningStructure().GetIntersectionWithLine(this, ref line, ref invLocal, flags);
if (t != null)
break;
}
}
if (t != null)
{
MyIntersectionResultLineTriangleEx correctIntersection = t.Value;
var hitPoint = Vector3D.Transform(t.Value.IntersectionPointInObjectSpace, block.FatBlock.WorldMatrix);
var distance = Vector3D.Distance(hitPoint, line.From);
if (distance < closestDistance)
{
closestDistance = distance;
MatrixD? cubeWorldMatrix = block.FatBlock.WorldMatrix;
TransformCubeToGrid(ref correctIntersection, ref local, ref cubeWorldMatrix);
closestHit = correctIntersection;
}
}
}
t = closestHit;
}
else
{
//model block
Matrix local;
cube.CubeBlock.Orientation.GetMatrix(out local);
Vector3 modelOffset;
Vector3.TransformNormal(ref cube.CubeBlock.BlockDefinition.ModelOffset, ref local, out modelOffset);
local.Translation = cube.CubeBlock.Position * GridSize + modelOffset;
MatrixD invLocal = MatrixD.Invert(cube.CubeBlock.FatBlock.WorldMatrix);
t = cube.CubeBlock.FatBlock.ModelCollision.GetTrianglePruningStructure().GetIntersectionWithLine(this, ref line, ref invLocal, flags);
if (t == null && cube.CubeBlock.FatBlock.Subparts != null)
{
foreach (var subpart in cube.CubeBlock.FatBlock.Subparts)
{
invLocal = MatrixD.Invert(subpart.Value.WorldMatrix);
t = subpart.Value.ModelCollision.GetTrianglePruningStructure().GetIntersectionWithLine(this, ref line, ref invLocal, flags);
if (t != null)
break;
}
}
if (t != null)
{
MyIntersectionResultLineTriangleEx correctIntersection = t.Value;
MatrixD? cubeWorldMatrix = cube.CubeBlock.FatBlock.WorldMatrix;
TransformCubeToGrid(ref correctIntersection, ref local, ref cubeWorldMatrix);
t = correctIntersection;
}
}
cubePartIndex = -1;
}
else
{
//cube block
VRage.Game.Models.MyIntersectionResultLineTriangleEx? closestHit = null;
float closestDistance = float.MaxValue;
Vector3? closestHitpoint = null;
int closestCubePartIndex = -1;
for (int it = 0; it < cube.Parts.Length; it++)
{
MyCubePart cubepart = cube.Parts[it];
MatrixD world = cubepart.InstanceData.LocalMatrix * WorldMatrix;
MatrixD invWorld = MatrixD.Invert(world);
t = cubepart.Model.GetTrianglePruningStructure().GetIntersectionWithLine(this, ref line, ref invWorld, flags);
if (t != null)
{
MyIntersectionResultLineTriangleEx correctIntersection = t.Value;
var hitPoint = Vector3.Transform(t.Value.IntersectionPointInObjectSpace, world);
float distance = Vector3.Distance(hitPoint, line.From);
if (distance < closestDistance)
{
closestDistance = distance;
Matrix localMatrix = cubepart.InstanceData.LocalMatrix;
MatrixD? cubeWorldMatrix = null;
TransformCubeToGrid(ref correctIntersection, ref localMatrix, ref cubeWorldMatrix);
closestHitpoint = correctIntersection.IntersectionPointInWorldSpace;
closestHit = correctIntersection;
closestCubePartIndex = it;
}
}
}
t = closestHit;
cubePartIndex = closestCubePartIndex;
}
}
public static bool GetLineIntersection(ref LineD line, out MyCubeGrid grid, out Vector3I position, out double distanceSquared)
{
grid = default(MyCubeGrid);
position = default(Vector3I);
distanceSquared = float.MaxValue;
MyEntities.OverlapAllLineSegment(ref line, m_lineOverlapList);
foreach (var e in m_lineOverlapList)
{
MyCubeGrid testGrid = e.Element as MyCubeGrid;
if (testGrid != null)
{
var castResult = testGrid.RayCastBlocks(line.From, line.To);
if (castResult.HasValue)
{
Vector3 closestCorner = testGrid.GetClosestCorner(castResult.Value, line.From);
float testDistSq = (float)Vector3D.DistanceSquared(line.From, Vector3D.Transform(closestCorner, testGrid.WorldMatrix));
if (testDistSq < distanceSquared)
{
distanceSquared = testDistSq;
grid = testGrid;
position = castResult.Value;
}
}
}
}
m_lineOverlapList.Clear();
return grid != null;
}
public static bool GetLineIntersectionExact(ref LineD line, out MyCubeGrid grid, out Vector3I position, out double distanceSquared)
{
grid = default(MyCubeGrid);
position = default(Vector3I);
distanceSquared = float.MaxValue;
double distance = double.MaxValue;
MyEntities.OverlapAllLineSegment(ref line, m_lineOverlapList);
foreach (var e in m_lineOverlapList)
{
MyCubeGrid testGrid = e.Element as MyCubeGrid;
if (testGrid != null)
{
MySlimBlock slimBlock;
double dst;
Vector3D? intersectedObjectPos = testGrid.GetLineIntersectionExactAll(ref line, out dst, out slimBlock);
if (intersectedObjectPos != null && dst < distance)
{
grid = testGrid;
distance = dst;
}
}
}
m_lineOverlapList.Clear();
return grid != null;
}
///
/// Returns closest line (in world space) intersection with all cubes.
///
public Vector3D? GetLineIntersectionExactAll(ref LineD line, out double distance, out MySlimBlock intersectedBlock)
{
intersectedBlock = null;
distance = float.MaxValue;
Vector3I? cubePosRes = null;
Vector3I cubePos = Vector3I.Zero;
double cubeDst = double.MaxValue;
if (GetLineIntersectionExactGrid(ref line, ref cubePos, ref cubeDst))
{
cubeDst = (float)Math.Sqrt(cubeDst);
cubePosRes = cubePos;
}
if (cubePosRes != null)
{
distance = cubeDst;
intersectedBlock = GetCubeBlock(cubePosRes.Value);
if (intersectedBlock == null)
return null;
return cubePos;
}
return null;
}
public void GetBlocksInsideSphere(ref BoundingSphereD sphere, HashSet blocks)
{
blocks.Clear();
if (PositionComp == null)
return;
BoundingBoxD box = new BoundingBoxD(sphere.Center - new Vector3D(sphere.Radius), sphere.Center + new Vector3D(sphere.Radius));
box = box.TransformFast(this.PositionComp.WorldMatrixNormalizedInv);
Vector3D min = box.Min;
Vector3D max = box.Max;
Vector3I start = new Vector3I((int)Math.Round(min.X * GridSizeR), (int)Math.Round(min.Y * GridSizeR), (int)Math.Round(min.Z * GridSizeR));
Vector3I end = new Vector3I((int)Math.Round(max.X * GridSizeR), (int)Math.Round(max.Y * GridSizeR), (int)Math.Round(max.Z * GridSizeR));
Vector3I startIt = Vector3I.Min(start, end);
Vector3I endIt = Vector3I.Max(start, end);
var localSphere = new BoundingSphereD(box.Center, sphere.Radius);
BoundingBoxD blockBox = new BoundingBoxD();
if ((endIt - startIt).Volume() < m_cubes.Count)
{
Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref startIt, ref endIt);
var pos = it.Current;
MyCube cube;
for (; it.IsValid(); it.GetNext(out pos))
{
if (m_cubes.TryGetValue(pos,out cube))
{
var block = cube.CubeBlock;
blockBox.Min = block.Min*GridSize - GridSizeHalf;
blockBox.Max = block.Max*GridSize + GridSizeHalf;
if (blockBox.Intersects(localSphere))
{
blocks.Add(block);
}
}
}
}
else
{
foreach (var value in m_cubes.Values)
{
var block = value.CubeBlock;
blockBox.Min = block.Min*GridSize - GridSizeHalf;
blockBox.Max = block.Max*GridSize + GridSizeHalf;
if (blockBox.Intersects(localSphere))
{
blocks.Add(block);
}
}
}
}
private void QuerySphere(BoundingSphereD sphere, List blocks)
{
Debug.Assert(!Closed);
if (PositionComp == null)
return;
// CH: Testing code to catch a crash:
if (Closed) MyLog.Default.WriteLine("Grid was Closed in MyCubeGrid.QuerySphere!");
if (sphere.Contains(PositionComp.WorldVolume) == ContainmentType.Contains)
{
foreach (var fb in m_fatBlocks)
{
Debug.Assert(m_cubeBlocks.Contains(fb.SlimBlock));
// Assert commented since this will happen a lot in AI School, scenario 2, when the fixing bot is fixing
//Debug.Assert(!fb.Closed);
if(fb.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
blocks.Add(fb);
foreach (var child in fb.Hierarchy.Children)
{
var entity = (MyEntity)child.Entity;
if (entity != null)
blocks.Add(entity);
}
}
return;
}
BoundingBoxD box = new BoundingBoxD(sphere.Center - new Vector3D(sphere.Radius), sphere.Center + new Vector3D(sphere.Radius));
box = box.TransformFast(this.PositionComp.WorldMatrixNormalizedInv);
Vector3D min = box.Min;
Vector3D max = box.Max;
Vector3I start = new Vector3I((int)Math.Round(min.X * GridSizeR), (int)Math.Round(min.Y * GridSizeR), (int)Math.Round(min.Z * GridSizeR));
Vector3I end = new Vector3I((int)Math.Round(max.X * GridSizeR), (int)Math.Round(max.Y * GridSizeR), (int)Math.Round(max.Z * GridSizeR));
Vector3I startIt = Vector3I.Min(start, end);
Vector3I endIt = Vector3I.Max(start, end);
startIt = Vector3I.Max(startIt, Min);
endIt = Vector3I.Min(endIt, Max);
if (startIt.X > endIt.X || startIt.Y > endIt.Y || startIt.Z > endIt.Z)
return;
Vector3 halfGridSize = new Vector3(0.5f);
BoundingBox blockBB = new BoundingBox();
var localSphere = new BoundingSphere((Vector3)box.Center * GridSizeR, (float)sphere.Radius * GridSizeR);
if ((endIt - startIt).Size > m_cubeBlocks.Count)
{
foreach (var fb in m_fatBlocks)
{
Debug.Assert(!fb.Closed);
if (fb.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
blockBB.Min = fb.Min - halfGridSize;
blockBB.Max = fb.Max + halfGridSize;
if (localSphere.Intersects(blockBB))
{
blocks.Add(fb);
foreach (var child in fb.Hierarchy.Children)
{
var entity = (MyEntity)child.Entity;
if (entity != null)
blocks.Add(entity);
}
}
}
return;
}
MyCube block;
if (m_tmpQueryCubeBlocks == null)
m_tmpQueryCubeBlocks = new HashSet();
// CH: Testing code to catch a crash:
if (m_cubes == null) MyLog.Default.WriteLine("m_cubes null in MyCubeGrid.QuerySphere!");
Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref startIt, ref endIt);
var pos = it.Current;
for (; it.IsValid(); it.GetNext(out pos))
{
if (m_cubes.TryGetValue(pos, out block) && block.CubeBlock.FatBlock != null)
{
Debug.Assert(!block.CubeBlock.FatBlock.Closed);
if (block.CubeBlock.FatBlock.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
if (m_tmpQueryCubeBlocks.Contains(block.CubeBlock.FatBlock))
continue;
blockBB.Min = block.CubeBlock.Min - halfGridSize;
blockBB.Max = block.CubeBlock.Max + halfGridSize;
if (localSphere.Intersects(blockBB))
{
blocks.Add(block.CubeBlock.FatBlock);
m_tmpQueryCubeBlocks.Add(block.CubeBlock.FatBlock);
foreach (var child in block.CubeBlock.FatBlock.Hierarchy.Children)
{
var entity = (MyEntity)child.Entity;
if (entity != null)
{
blocks.Add(entity);
m_tmpQueryCubeBlocks.Add(entity);
}
}
}
}
}
m_tmpQueryCubeBlocks.Clear();
}
///
/// Correct interesection transforming vertices from cube to grid coordinates
///
private void TransformCubeToGrid(ref MyIntersectionResultLineTriangleEx triangle, ref Matrix cubeLocalMatrix, ref MatrixD? cubeWorldMatrix)
{
if (cubeWorldMatrix == null)
{
MatrixD gridWorldMatrix = WorldMatrix;
triangle.IntersectionPointInObjectSpace = Vector3.Transform(triangle.IntersectionPointInObjectSpace, ref cubeLocalMatrix);
triangle.IntersectionPointInWorldSpace = Vector3.Transform(triangle.IntersectionPointInObjectSpace, gridWorldMatrix);
triangle.NormalInObjectSpace = Vector3.TransformNormal(triangle.NormalInObjectSpace, ref cubeLocalMatrix);
triangle.NormalInWorldSpace = Vector3.TransformNormal(triangle.NormalInObjectSpace, gridWorldMatrix);
}
else
{
Vector3 intersectionLocal = triangle.IntersectionPointInObjectSpace;
Vector3 normalLocal = triangle.NormalInObjectSpace;
triangle.IntersectionPointInObjectSpace = Vector3.Transform(intersectionLocal, ref cubeLocalMatrix);
triangle.IntersectionPointInWorldSpace = Vector3.Transform(intersectionLocal, cubeWorldMatrix.Value);
triangle.NormalInObjectSpace = Vector3.TransformNormal(normalLocal, ref cubeLocalMatrix);
triangle.NormalInWorldSpace = Vector3.TransformNormal(normalLocal, cubeWorldMatrix.Value);
}
triangle.Triangle.InputTriangle.Transform(ref cubeLocalMatrix);
}
private void QueryLine(LineD line, List> blocks)
{
MyLineSegmentOverlapResult overlap = new MyLineSegmentOverlapResult();
BoundingBoxD bb = new BoundingBoxD();
MatrixD invWorld = PositionComp.WorldMatrixNormalizedInv;//MatrixD.Invert(WorldMatrix);
Vector3D localStart, localEnd;
Vector3D.Transform(ref line.From, ref invWorld, out localStart);
Vector3D.Transform(ref line.To, ref invWorld, out localEnd);
RayD lineLoc = new RayD(localStart, Vector3D.Normalize(localEnd - localStart));
MyCube cube;
RayCastCells(line.From, line.To, m_cacheRayCastCells);
foreach (var pos in m_cacheRayCastCells)
{
if (m_cubes.TryGetValue(pos, out cube) && cube.CubeBlock.FatBlock != null)
{
var block = cube.CubeBlock.FatBlock;
overlap.Element = block;
bb.Min = block.Min * GridSize - GridSizeHalfVector;
bb.Max = block.Max * GridSize + GridSizeHalfVector;
var intersect = lineLoc.Intersects(bb);
if (!intersect.HasValue)
continue;
overlap.Distance = intersect.Value;
blocks.Add(overlap);
}
}
}
[ThreadStatic]
private static HashSet m_tmpQueryCubeBlocks;
private void QueryAABB(BoundingBoxD box, List blocks)
{
if (blocks == null)
{
Debug.Fail("null blocks ! probably dead entity ?");
return;
}
if (PositionComp == null)
{
return;
}
if (box.Contains(PositionComp.WorldAABB) == ContainmentType.Contains)
{
foreach (var fb in m_fatBlocks)
{
Debug.Assert(m_cubeBlocks.Contains(fb.SlimBlock));
Debug.Assert(!fb.Closed,m_fatBlocks.Count+", "+m_cubeBlocks.Count);
if (fb.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
blocks.Add(fb);
if (fb.Hierarchy != null && fb.Hierarchy.Children != null)
{
foreach (var child in fb.Hierarchy.Children)
{
if (child.Container != null)
{
blocks.Add((MyEntity)child.Container.Entity);
}
else
{
System.Diagnostics.Debug.Fail("Child block hasn't container set!");
}
}
}
else
{
System.Diagnostics.Debug.Fail("Fatblock missing hieararchy component!");
}
}
return;
}
var obb = MyOrientedBoundingBoxD.Create(box, PositionComp.WorldMatrixNormalizedInv);
obb.Center *= GridSizeR;
obb.HalfExtent *= GridSizeR;
box = box.TransformFast(this.PositionComp.WorldMatrixNormalizedInv);
Vector3D min = box.Min;
Vector3D max = box.Max;
Vector3I start = new Vector3I((int)Math.Round(min.X * GridSizeR), (int)Math.Round(min.Y * GridSizeR), (int)Math.Round(min.Z * GridSizeR));
Vector3I end = new Vector3I((int)Math.Round(max.X * GridSizeR), (int)Math.Round(max.Y * GridSizeR), (int)Math.Round(max.Z * GridSizeR));
Vector3I startIt = Vector3I.Min(start, end);
Vector3I endIt = Vector3I.Max(start, end);
startIt = Vector3I.Max(startIt, Min);
endIt = Vector3I.Min(endIt, Max);
if (startIt.X > endIt.X || startIt.Y > endIt.Y || startIt.Z > endIt.Z)
return;
Vector3 halfGridSize = new Vector3(0.5f);
BoundingBoxD blockBB = new BoundingBoxD();
if ((endIt - startIt).Size > m_cubeBlocks.Count)
{
foreach (var fb in m_fatBlocks)
{
Debug.Assert(m_cubeBlocks.Contains(fb.SlimBlock));
Debug.Assert(!fb.Closed);
if (fb.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
blockBB.Min = fb.Min - halfGridSize;
blockBB.Max = fb.Max + halfGridSize;
if (obb.Intersects(ref blockBB))
{
blocks.Add(fb);
if (fb.Hierarchy != null && fb.Hierarchy.Children != null)
{
foreach (var child in fb.Hierarchy.Children)
{
if (child.Container != null)
{
blocks.Add((MyEntity)child.Container.Entity);
}
else
{
System.Diagnostics.Debug.Fail("Child block hasn't container set!");
}
}
}
else
{
System.Diagnostics.Debug.Fail("Fatblock is missing hierarchy component!");
}
}
}
return;
}
MyCube block;
Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref startIt, ref endIt);
var pos = it.Current;
if (m_tmpQueryCubeBlocks == null) m_tmpQueryCubeBlocks = new HashSet();
for (; it.IsValid(); it.GetNext(out pos))
{
System.Diagnostics.Debug.Assert(m_cubes != null, "m_cubes on MyCubeGrid are null!");
if (m_cubes != null && m_cubes.TryGetValue(pos, out block) && block.CubeBlock.FatBlock != null)
{
var fatBlock = block.CubeBlock.FatBlock;
if (m_tmpQueryCubeBlocks.Contains(fatBlock))
continue;
blockBB.Min = block.CubeBlock.Min - halfGridSize;
blockBB.Max = block.CubeBlock.Max + halfGridSize;
if (obb.Intersects(ref blockBB))
{
m_tmpQueryCubeBlocks.Add(fatBlock);
blocks.Add(fatBlock);
if (fatBlock.Hierarchy != null && fatBlock.Hierarchy.Children != null)
{
foreach (var child in fatBlock.Hierarchy.Children)
{
if (child.Container != null)
{
blocks.Add((MyEntity)child.Container.Entity);
m_tmpQueryCubeBlocks.Add(fatBlock);
}
else
{
System.Diagnostics.Debug.Fail("child block hasn't container set!");
}
}
}
else
{
System.Diagnostics.Debug.Fail("Fatblock is missing hierarchy component!");
}
}
}
}
m_tmpQueryCubeBlocks.Clear();
}
[ThreadStatic]
private static HashSet m_tmpQuerySlimBlocks;
public void GetBlocksIntersectingOBB(BoundingBoxD box, MatrixD boxTransform, List blocks)
{
if (blocks == null)
{
Debug.Fail("null blocks ! probably dead entity ?");
return;
}
if (PositionComp == null)
{
return;
}
var obbWorld = MyOrientedBoundingBoxD.Create(box, boxTransform);
var gridWorldAabb = PositionComp.WorldAABB;
if (obbWorld.Contains(ref gridWorldAabb) == ContainmentType.Contains)
{
foreach (var slimBlock in GetBlocks())
{
if (slimBlock.FatBlock != null)
{
Debug.Assert(!slimBlock.FatBlock.Closed);
if (slimBlock.FatBlock.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
}
blocks.Add(slimBlock);
}
return;
}
var compositeTransform = boxTransform * PositionComp.WorldMatrixNormalizedInv;
var obb = MyOrientedBoundingBoxD.Create(box, compositeTransform);
obb.Center *= GridSizeR;
obb.HalfExtent *= GridSizeR;
box = box.TransformFast(compositeTransform);
Vector3D min = box.Min;
Vector3D max = box.Max;
Vector3I start = new Vector3I((int)Math.Round(min.X * GridSizeR), (int)Math.Round(min.Y * GridSizeR), (int)Math.Round(min.Z * GridSizeR));
Vector3I end = new Vector3I((int)Math.Round(max.X * GridSizeR), (int)Math.Round(max.Y * GridSizeR), (int)Math.Round(max.Z * GridSizeR));
Vector3I startIt = Vector3I.Min(start, end);
Vector3I endIt = Vector3I.Max(start, end);
startIt = Vector3I.Max(startIt, Min);
endIt = Vector3I.Min(endIt, Max);
if (startIt.X > endIt.X || startIt.Y > endIt.Y || startIt.Z > endIt.Z)
return;
Vector3 halfGridSize = new Vector3(0.5f);
BoundingBoxD blockBB = new BoundingBoxD();
if ((endIt - startIt).Size > m_cubeBlocks.Count)
{
foreach (var slimBlock in GetBlocks())
{
Debug.Assert(!slimBlock.FatBlock.Closed);
if (slimBlock.FatBlock.Closed) //TODO:investigate why there is closed block in the grid/m_fatblock list
continue; //it is possible to have marked for close block there but not closed
blockBB.Min = slimBlock.Min - halfGridSize;
blockBB.Max = slimBlock.Max + halfGridSize;
if (obb.Intersects(ref blockBB))
{
blocks.Add(slimBlock);
}
}
return;
}
MyCube block;
if (m_tmpQuerySlimBlocks == null)
m_tmpQuerySlimBlocks = new HashSet();
Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref startIt, ref endIt);
var pos = it.Current;
for (; it.IsValid(); it.GetNext(out pos))
{
System.Diagnostics.Debug.Assert(m_cubes != null, "m_cubes on MyCubeGrid are null!");
if (m_cubes != null && m_cubes.TryGetValue(pos, out block) && block.CubeBlock != null)
{
var slimBlock = block.CubeBlock;
if (m_tmpQuerySlimBlocks.Contains(slimBlock))
continue;
blockBB.Min = slimBlock.Min - halfGridSize;
blockBB.Max = slimBlock.Max + halfGridSize;
if (obb.Intersects(ref blockBB))
{
m_tmpQuerySlimBlocks.Add(slimBlock);
blocks.Add(slimBlock);
}
}
}
m_tmpQuerySlimBlocks.Clear();
}
///
/// Optimized version where spheres are sorted from smallest to largest
///
///
///
///
///
///
///
///
///
///
public void GetBlocksInsideSpheres(
ref BoundingSphereD sphere1,
ref BoundingSphereD sphere2,
ref BoundingSphereD sphere3,
HashSet blocks1,
HashSet blocks2,
HashSet blocks3,
bool respectDeformationRatio, float detectionBlockHalfSize, ref MatrixD invWorldGrid)
{
blocks1.Clear();
blocks2.Clear();
blocks3.Clear();
m_processedBlocks.Clear();
Vector3D center;
Vector3D.Transform(ref sphere3.Center, ref invWorldGrid, out center);
Vector3I startIt = Vector3I.Round((center - sphere3.Radius) * GridSizeR);
Vector3I endIt = Vector3I.Round((center + sphere3.Radius) * GridSizeR);
var halfSize = new Vector3(detectionBlockHalfSize);
var localSphere1 = new BoundingSphereD(center, sphere1.Radius);
var localSphere2 = new BoundingSphereD(center, sphere2.Radius);
var localSphere3 = new BoundingSphereD(center, sphere3.Radius);
int cellsToCheck = (endIt.X - startIt.X) * (endIt.Y - startIt.Y) * (endIt.Z - startIt.Z);
if (cellsToCheck < m_cubes.Count)
{
Vector3I cubePos = new Vector3I();
MyCube cube;
for (cubePos.X = startIt.X; cubePos.X <= endIt.X; cubePos.X++)
{
for (cubePos.Y = startIt.Y; cubePos.Y <= endIt.Y; cubePos.Y++)
{
for (cubePos.Z = startIt.Z; cubePos.Z <= endIt.Z; cubePos.Z++)
{
if (m_cubes.TryGetValue(cubePos, out cube))
{
var block = cube.CubeBlock;
if (block.FatBlock != null && m_processedBlocks.Contains(block.FatBlock))
{
continue;
}
m_processedBlocks.Add(block.FatBlock);
if (respectDeformationRatio)
{
localSphere1.Radius = sphere1.Radius * block.DeformationRatio;
localSphere2.Radius = sphere2.Radius * block.DeformationRatio;
localSphere3.Radius = sphere3.Radius * block.DeformationRatio;
}
BoundingBox blockBox;
if (block.FatBlock != null)
{
blockBox = new BoundingBox(block.Min * GridSize - GridSizeHalf, block.Max * GridSize + GridSizeHalf);
}
else
{
blockBox = new BoundingBox(block.Position * GridSize - halfSize, block.Position * GridSize + halfSize);
}
//If biggest sphere fails no need to check the rest
if (blockBox.Intersects(localSphere3))
{
if (blockBox.Intersects(localSphere2))
{
if(blockBox.Intersects(localSphere1))
blocks1.Add(block);
else
blocks2.Add(block);
}
else
blocks3.Add(block);
}
}
}
}
}
}
else
{
foreach (var cube in m_cubes.Values)
{
var block = cube.CubeBlock;
if (block.FatBlock != null && m_processedBlocks.Contains(block.FatBlock))
{
continue;
}
m_processedBlocks.Add(block.FatBlock);
if (respectDeformationRatio)
{
localSphere1.Radius = sphere1.Radius * block.DeformationRatio;
localSphere2.Radius = sphere2.Radius * block.DeformationRatio;
localSphere3.Radius = sphere3.Radius * block.DeformationRatio;
}
BoundingBox blockBox;
if (block.FatBlock != null)
{
blockBox = new BoundingBox(block.Min * GridSize - GridSizeHalf, block.Max * GridSize + GridSizeHalf);
}
else
{
blockBox = new BoundingBox(block.Position * GridSize - halfSize, block.Position * GridSize + halfSize);
}
//If biggest sphere fails no need to check the rest
if (blockBox.Intersects(localSphere3))
{
if (blockBox.Intersects(localSphere2))
{
if (blockBox.Intersects(localSphere1))
blocks1.Add(block);
else
blocks2.Add(block);
}
else
blocks3.Add(block);
}
}
}
m_processedBlocks.Clear();
}
///
/// Obtains all blocks intersected by raycast
///
internal HashSet RayCastBlocksAll(Vector3 worldStart, Vector3 worldEnd)
{
RayCastCells(worldStart, worldEnd, m_cacheRayCastCells);
HashSet result = new HashSet();
foreach (var i in m_cacheRayCastCells)
{
if (m_cubes.ContainsKey(i))
{
result.Add(m_cubes[i]);
}
}
return result;
}
///
/// Obtains all blocks intersected by raycast
///
internal List RayCastBlocksAllOrdered(Vector3 worldStart, Vector3 worldEnd)
{
RayCastCells(worldStart, worldEnd, m_cacheRayCastCells);
List result = new List();
foreach (var i in m_cacheRayCastCells)
{
if (m_cubes.ContainsKey(i) && !result.Contains(m_cubes[i]))
{
result.Add(m_cubes[i]);
}
}
return result;
}
///
/// Obtains position of first hit block.
///
public Vector3I? RayCastBlocks(Vector3D worldStart, Vector3D worldEnd)
{
RayCastCells(worldStart, worldEnd, m_cacheRayCastCells);
foreach (var i in m_cacheRayCastCells)
{
if (m_cubes.ContainsKey(i))
{
return i;
}
}
return null;
}
///
/// Obtains positions of grid cells regardless of whether these cells are taken up by blocks or not.
///
public void RayCastCells(Vector3D worldStart, Vector3D worldEnd, List outHitPositions, Vector3I? gridSizeInflate = null, bool havokWorld = false,
bool clearOutHitPositions = true)
{
MatrixD invWorld = PositionComp.WorldMatrixNormalizedInv;//MatrixD.Invert(WorldMatrix);
Vector3D localStart, localEnd;
Vector3D.Transform(ref worldStart, ref invWorld, out localStart);
Vector3D.Transform(ref worldEnd, ref invWorld, out localEnd);
//We need move the line, because MyGridIntersection calculates the center of the box in the corner
var offset = GridSizeHalfVector;
localStart += offset;
localEnd += offset;
var min = Min - Vector3I.One;
var max = Max + Vector3I.One;
if (gridSizeInflate.HasValue)
{
min -= gridSizeInflate.Value;
max += gridSizeInflate.Value;
}
if (clearOutHitPositions)
outHitPositions.Clear();
if (havokWorld)
MyGridIntersection.CalculateHavok(outHitPositions, GridSize, localStart, localEnd, min, max);
else
MyGridIntersection.Calculate(outHitPositions, GridSize, localStart, localEnd, min, max);
}
///
/// Obtains positions of static grid cells regardless of whether these cells are taken up by blocks or not. Usefull when placing block on voxel.
///
public static void RayCastStaticCells(Vector3D worldStart, Vector3D worldEnd, List outHitPositions, float gridSize, Vector3I? gridSizeInflate = null, bool havokWorld = false)
{
Vector3D localStart = worldStart;
Vector3D localEnd = worldEnd;
//We need move the line, because MyGridIntersection calculates the center of the box in the corner
var offset = new Vector3D(gridSize * 0.5f);
localStart += offset;
localEnd += offset;
var min = -Vector3I.One;
var max = Vector3I.One;
if (gridSizeInflate.HasValue)
{
min -= gridSizeInflate.Value;
max += gridSizeInflate.Value;
}
outHitPositions.Clear();
if (havokWorld)
MyGridIntersection.CalculateHavok(outHitPositions, gridSize, localStart, localEnd, min, max);
else
MyGridIntersection.Calculate(outHitPositions, gridSize, localStart, localEnd, min, max);
}
/////
///// Finds children which overlap box given by start and end (which do not have to be min and max).
///// Coordinates are given in grid coordinates (integer indices).
/////
//private void GetOverlappedBlocks(Vector3I minI, Vector3I maxI, HashSet outOverlappedBlocks)
//{
// Debug.Assert(outOverlappedBlocks != null);
// var tmp = new Vector3I();
// for (tmp.Z = minI.Z; tmp.Z <= maxI.Z; ++tmp.Z)
// for (tmp.Y = minI.Y; tmp.Y <= maxI.Y; ++tmp.Y)
// for (tmp.X = minI.X; tmp.X <= maxI.X; ++tmp.X)
// {
// var block = GetCubeBlock(tmp);
// if (block != null)
// outOverlappedBlocks.Add(block);
// }
//}
#endregion
#region Nested types
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyBlockBuildArea
{
// 34B in total
public DefinitionIdBlit DefinitionId; // 6B
public uint ColorMaskHSV;
public Vector3I PosInGrid; // Could be Vector3S, we'll save another 6B
public Vector3B BlockMin;
public Vector3B BlockMax;
public Vector3UByte BuildAreaSize;
public Vector3B StepDelta;
public Base6Directions.Direction OrientationForward;
public Base6Directions.Direction OrientationUp;
}
[ProtoContract]
public struct MyBlockLocation
{
[ProtoMember]
public Vector3I Min;
[ProtoMember]
public Vector3I Max; // Will be obsolete
[ProtoMember]
public Vector3I CenterPos; // Will be obsolete
[ProtoMember]
public MyBlockOrientation Orientation; // Will be different
[ProtoMember]
public long EntityId;
[ProtoMember]
public DefinitionIdBlit BlockDefinition;
[ProtoMember]
public long Owner;
public MyBlockLocation(MyDefinitionId blockDefinition, Vector3I min, Vector3I max, Vector3I center, Quaternion orientation, long entityId, long owner)
{
BlockDefinition = blockDefinition;
Min = min;
Max = max;
CenterPos = center;
Orientation = new MyBlockOrientation(ref orientation);
EntityId = entityId;
Owner = owner;
}
}
[ProtoContract]
public struct BlockPositionId
{
[ProtoMember]
public Vector3I Position;
[ProtoMember]
public uint CompoundId;
}
private enum NeighborOffsetIndex
{
XUP = 0,
XDOWN = 1,
YUP = 2,
YDOWN = 3,
ZUP = 4,
ZDOWN = 5,
XUP_YUP = 6,
XUP_YDOWN = 7,
XDOWN_YUP = 8,
XDOWN_YDOWN = 9,
YUP_ZUP = 10,
YUP_ZDOWN = 11,
YDOWN_ZUP = 12,
YDOWN_ZDOWN = 13,
XUP_ZUP = 14,
XUP_ZDOWN = 15,
XDOWN_ZUP = 16,
XDOWN_ZDOWN = 17,
XUP_YUP_ZUP = 18,
XUP_YUP_ZDOWN = 19,
XUP_YDOWN_ZUP = 20,
XUP_YDOWN_ZDOWN = 21,
XDOWN_YUP_ZUP = 22,
XDOWN_YUP_ZDOWN = 23,
XDOWN_YDOWN_ZUP = 24,
XDOWN_YDOWN_ZDOWN = 25
}
private struct MyNeighbourCachedBlock
{
public Vector3I Position;
public MyCubeBlockDefinition BlockDefinition;
public MyBlockOrientation Orientation;
public override int GetHashCode()
{
return Position.GetHashCode();
}
}
///
/// Used when calculating index of added block. Might not be count of
/// blocks since removal of a block does not decrement this. Key is numerical
/// ID of cube block definition.
///
public class BlockTypeCounter
{
private Dictionary m_countById = new Dictionary(MyDefinitionId.Comparer);
internal int GetNextNumber(MyDefinitionId blockType)
{
int idCounter = 0;
m_countById.TryGetValue(blockType, out idCounter);
++idCounter;
m_countById[blockType] = idCounter;
return idCounter;
}
}
#endregion
void IMyGridConnectivityTest.GetConnectedBlocks(Vector3I minI, Vector3I maxI, Dictionary outOverlappedCubeBlocks)
{
Debug.Assert(outOverlappedCubeBlocks != null);
var tmp = new Vector3I();
for (tmp.Z = minI.Z; tmp.Z <= maxI.Z; ++tmp.Z)
for (tmp.Y = minI.Y; tmp.Y <= maxI.Y; ++tmp.Y)
for (tmp.X = minI.X; tmp.X <= maxI.X; ++tmp.X)
{
var block = GetCubeBlock(tmp);
if (block != null)
outOverlappedCubeBlocks[block.Position] = new ConnectivityResult() { Definition = block.BlockDefinition, FatBlock = block.FatBlock, Orientation = block.Orientation, Position = block.Position };
}
}
private string MakeCustomName()
{
StringBuilder output = new StringBuilder();
int mod = 10000;
long prefix = MyMath.Mod(EntityId, mod);
string name = null;
if (IsStatic)
{
//name = MyTexts.GetString(MySpaceTexts.CustomShipName_Platform);
name = MyTexts.GetString(MyCommonTexts.DetailStaticGrid);
}
else
{
switch (GridSizeEnum)
{
//case Common.ObjectBuilders.MyCubeSize.Small: name = MyTexts.GetString(MySpaceTexts.CustomShipName_SmallShip); break;
case MyCubeSize.Small: name = MyTexts.GetString(MyCommonTexts.DetailSmallGrid); break;
//case Common.ObjectBuilders.MyCubeSize.Large: name = MyTexts.GetString(MySpaceTexts.CustomShipName_LargeShip); break;
case MyCubeSize.Large: name = MyTexts.GetString(MyCommonTexts.DetailLargeGrid); break;
}
}
output.Append(name ?? "Grid").Append(" ").Append(prefix.ToString());
return output.ToString();
}
public void ChangeOwner(MyCubeBlock block, long oldOwner, long newOwner)
{
if (!MyFakes.ENABLE_TERMINAL_PROPERTIES)
return;
m_ownershipManager.ChangeBlockOwnership(block, oldOwner, newOwner);
}
public void RecalculateOwners()
{
if (!MyFakes.ENABLE_TERMINAL_PROPERTIES)
return;
m_ownershipManager.RecalculateOwners();
}
public void UpdateOwnership(long ownerId, bool isFunctional)
{
if (!MyFakes.ENABLE_TERMINAL_PROPERTIES)
return;
m_ownershipManager.UpdateOnFunctionalChange(ownerId, isFunctional);
}
class MyCubeGridPosition : MyPositionComponent
{
MyCubeGrid m_grid;
public override void OnAddedToContainer()
{
base.OnAddedToContainer();
m_grid = Container.Entity as MyCubeGrid;
}
protected override void OnWorldPositionChanged(object source, bool updateChildren)
{
m_grid.m_worldPositionChanged = true;
base.OnWorldPositionChanged(source, updateChildren);
}
}
public MyFracturedBlock CreateFracturedBlock(MyObjectBuilder_FracturedBlock fracturedBlockBuilder, Vector3I position)
{
Debug.Assert(!MyFakes.ENABLE_FRACTURE_COMPONENT);
ProfilerShort.Begin("CreateFractureBlockBuilder");
var defId = new MyDefinitionId(typeof(MyObjectBuilder_FracturedBlock), "FracturedBlockLarge");
var def = Sandbox.Definitions.MyDefinitionManager.Static.GetCubeBlockDefinition(defId);
var bPos = position;
MyCube oldBlock;
if (m_cubes.TryGetValue(bPos, out oldBlock))
RemoveBlockInternal(oldBlock.CubeBlock, close: true);
fracturedBlockBuilder.CreatingFracturedBlock = true;
var sb = AddBlock(fracturedBlockBuilder, false); //BuildBlock(def, Vector3.Zero, bPos, Quaternion.Identity, 0, 0, null);
System.Diagnostics.Debug.Assert(sb != null, "Error created fractured block!");
if (sb != null)
{
var fb = sb.FatBlock as MyFracturedBlock;
fb.Render.UpdateRenderObject(true);
ProfilerShort.End();
UpdateBlockNeighbours(fb.SlimBlock);
return fb;
}
return null;
}
private MyFracturedBlock CreateFracturedBlock(MyFracturedBlock.Info info)// Havok.HkdBreakableShape shape, Vector3I pos)
{
Debug.Assert(!MyFakes.ENABLE_FRACTURE_COMPONENT);
ProfilerShort.Begin("CreateFractureBlock");
var defId = new MyDefinitionId(typeof(MyObjectBuilder_FracturedBlock), "FracturedBlockLarge");
var def = Sandbox.Definitions.MyDefinitionManager.Static.GetCubeBlockDefinition(defId);
var bPos = info.Position;// m_grid.WorldToGridInteger(worldMatrix.Translation);
MyCube oldBlock;
if (m_cubes.TryGetValue(bPos, out oldBlock))
RemoveBlock(oldBlock.CubeBlock, false);
var blockObjectBuilder = MyCubeGrid.CreateBlockObjectBuilder(def, bPos, new MyBlockOrientation(ref Quaternion.Identity), 0, 0, true);
blockObjectBuilder.ColorMaskHSV = Vector3.Zero;
(blockObjectBuilder as MyObjectBuilder_FracturedBlock).CreatingFracturedBlock = true;
var sb = AddBlock(blockObjectBuilder, false); //BuildBlock(def, Vector3.Zero, bPos, Quaternion.Identity, 0, 0, null);
if (sb == null)
{
Debug.Fail("Fracture block not added");
info.Shape.RemoveReference();
ProfilerShort.End();
return null;
}
var fb = sb.FatBlock as MyFracturedBlock;
fb.OriginalBlocks = info.OriginalBlocks;
fb.Orientations = info.Orientations;
Debug.Assert(info.MultiBlocks == null || info.MultiBlocks.Count == fb.OriginalBlocks.Count);
fb.MultiBlocks = info.MultiBlocks;
ProfilerShort.Begin("SetRenderData");
fb.SetDataFromHavok(info.Shape, info.Compound);
ProfilerShort.End();
fb.Render.UpdateRenderObject(true);
ProfilerShort.End();
UpdateBlockNeighbours(fb.SlimBlock);
if (Sync.IsServer)
{
MySyncDestructions.CreateFracturedBlock((MyObjectBuilder_FracturedBlock)fb.GetObjectBuilderCubeBlock(), this.EntityId, bPos);
}
return fb;
}
bool m_generatorsEnabled = true;
public bool EnableGenerators(bool enable, bool fromServer = false)
{
System.Diagnostics.Debug.Assert(Sync.IsServer || fromServer, "Cannot change this on client");
bool oldEnabled = m_generatorsEnabled;
if (Sync.IsServer || fromServer)
{
Debug.Assert(Render is MyRenderComponentCubeGrid, "Invalid Render - cannot access grid generators");
if (!(Render is MyRenderComponentCubeGrid))
{
m_generatorsEnabled = false;
return false;
}
if (m_generatorsEnabled != enable)
{
AdditionalModelGenerators.ForEach(g => g.EnableGenerator(enable));
m_generatorsEnabled = enable;
//if (Sync.IsServer)
// MySyncDestructions.EnableGenerators(this, enable);
}
}
return oldEnabled;
}
// Returns generating block (not compound block, but block inside) from generated one. Can return also MyFracturedBlock!
public MySlimBlock GetGeneratingBlock(MySlimBlock generatedBlock)
{
if (generatedBlock == null || !generatedBlock.BlockDefinition.IsGeneratedBlock)
return null;
foreach (var generator in AdditionalModelGenerators)
{
var generatingBlock = generator.GetGeneratingBlock(generatedBlock);
if (generatingBlock != null)
return generatingBlock;
}
return null;
}
private static readonly Vector3I[] m_tmpBlockSurroundingOffsets = new Vector3I[]
{
new Vector3I(0, 0, 0),
new Vector3I(1, 0, 0),
new Vector3I(-1, 0, 0),
new Vector3I(0, 0, 1),
new Vector3I(0, 0, -1),
new Vector3I(1, 0, 1),
new Vector3I(-1, 0, 1),
new Vector3I(1, 0, -1),
new Vector3I(-1, 0, -1),
new Vector3I(0, 1, 0),
new Vector3I(1, 1, 0),
new Vector3I(-1, 1, 0),
new Vector3I(0, 1, 1),
new Vector3I(0, 1, -1),
new Vector3I(1, 1, 1),
new Vector3I(-1, 1, 1),
new Vector3I(1, 1, -1),
new Vector3I(-1, 1, -1),
new Vector3I(0, -1, 0),
new Vector3I(1, -1, 0),
new Vector3I(-1, -1, 0),
new Vector3I(0, -1, 1),
new Vector3I(0, -1, -1),
new Vector3I(1, -1, 1),
new Vector3I(-1, -1, 1),
new Vector3I(1, -1, -1),
new Vector3I(-1, -1, -1),
};
///
/// Returns array of generated blocks from given generating block.
///
public void GetGeneratedBlocks(MySlimBlock generatingBlock, List outGeneratedBlocks)
{
ProfilerShort.Begin("MyCubeGrid.GetGeneratedBlocks");
Debug.Assert(!(generatingBlock.FatBlock is MyCompoundCubeBlock));
outGeneratedBlocks.Clear();
if (generatingBlock == null || (generatingBlock.FatBlock is MyCompoundCubeBlock))
{
ProfilerShort.End();
return;
}
if (generatingBlock.BlockDefinition.IsGeneratedBlock || generatingBlock.BlockDefinition.GeneratedBlockDefinitions == null
|| generatingBlock.BlockDefinition.GeneratedBlockDefinitions.Length == 0)
{
ProfilerShort.End();
return;
}
foreach (var offset in m_tmpBlockSurroundingOffsets)
{
var surroundingBlock = generatingBlock.CubeGrid.GetCubeBlock(generatingBlock.Position + offset);
if (surroundingBlock == null || surroundingBlock == generatingBlock)
continue;
if (surroundingBlock.FatBlock is MyCompoundCubeBlock)
{
var compoundBlock = surroundingBlock.FatBlock as MyCompoundCubeBlock;
foreach (var blockInCompound in compoundBlock.GetBlocks())
{
if (blockInCompound == generatingBlock || !blockInCompound.BlockDefinition.IsGeneratedBlock)
continue;
foreach (var generator in AdditionalModelGenerators)
{
var localGeneratingBlock = generator.GetGeneratingBlock(blockInCompound);
if (generatingBlock != localGeneratingBlock)
continue;
outGeneratedBlocks.Add(blockInCompound);
}
}
}
else
{
if (!surroundingBlock.BlockDefinition.IsGeneratedBlock)
continue;
foreach (var generator in AdditionalModelGenerators)
{
var localGeneratingBlock = generator.GetGeneratingBlock(surroundingBlock);
if (generatingBlock != localGeneratingBlock)
continue;
outGeneratedBlocks.Add(surroundingBlock);
}
}
}
ProfilerShort.End();
}
public void OnIntegrityChanged(MySlimBlock block)
{
NotifyBlockIntegrityChanged(block);
}
public void PasteBlocksToGrid(List gridsToMerge, long inventoryEntityId, bool multiBlock, bool instantBuild)
{
MyMultiplayer.RaiseEvent(this, x => x.PasteBlocksToGridServer_Implementation, gridsToMerge, inventoryEntityId, multiBlock, instantBuild);
}
[Event, Reliable, Server]
private void PasteBlocksToGridServer_Implementation(List gridsToMerge, long inventoryEntityId, bool multiBlock, bool instantBuild)
{
if (!MyEventContext.Current.IsLocallyInvoked && !MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value))
{
MyEventContext.ValidationFailed();
return;
}
bool isAdmin = (MyEventContext.Current.IsLocallyInvoked || MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value));
MyEntities.RemapObjectBuilderCollection(gridsToMerge);
MatrixI mergeTransform = PasteBlocksServer(gridsToMerge);
if ((isAdmin && instantBuild) == false)
{
MyEntity entity;
if (MyEntities.TryGetEntityById(inventoryEntityId, out entity) && entity != null)
{
MyInventoryBase buildInventory = MyCubeBuilder.BuildComponent.GetBuilderInventory(entity);
if (buildInventory != null)
{
if (multiBlock)
{
MyMultiBlockClipboard.TakeMaterialsFromBuilder(gridsToMerge, entity);
}
else
{
MyGridClipboard.CalculateItemRequirements(gridsToMerge, m_buildComponents);
foreach (var item in m_buildComponents.TotalMaterials)
{
buildInventory.RemoveItemsOfType(item.Value, item.Key);
}
}
}
}
}
MyMultiplayer.RaiseEvent(this, x => x.PasteBlocksToGridClient_Implementation, gridsToMerge[0], mergeTransform);
}
[Event, Reliable, Broadcast]
private void PasteBlocksToGridClient_Implementation(MyObjectBuilder_CubeGrid gridToMerge, MatrixI mergeTransform)
{
PasteBlocksClient(gridToMerge, mergeTransform);
}
public void UpdateOxygenAmount(float[] oxygenAmount)
{
Debug.Assert(Sync.IsServer);
Debug.Assert(MySession.Static.Settings.EnableOxygen);
Debug.Assert(MySession.Static.Settings.EnableOxygenPressurization);
MyMultiplayer.RaiseEvent(this, x => x.UpdateOxygenAmount_Implementation, oxygenAmount);
}
// TODO: Make unreliable!
[Event, Reliable, Broadcast]
private void UpdateOxygenAmount_Implementation(float[] oxygenAmount)
{
Debug.Assert(MySession.Static.Settings.EnableOxygen);
Debug.Assert(MySession.Static.Settings.EnableOxygenPressurization);
if (GridSystems != null && GridSystems.GasSystem != null)
{
GridSystems.GasSystem.UpdateOxygenAmount(oxygenAmount);
}
}
private void PasteBlocksClient(MyObjectBuilder_CubeGrid gridToMerge, MatrixI mergeTransform)
{
var pastedGrid = MyEntities.CreateFromObjectBuilder(gridToMerge) as MyCubeGrid;
pastedGrid.SentFromServer = true;
MyEntities.Add(pastedGrid);
MySession.Static.TotalBlocksCreated += (uint)pastedGrid.BlocksCount;
MergeGridInternal(pastedGrid, ref mergeTransform);
}
private MatrixI PasteBlocksServer(List gridsToMerge)
{
Debug.Assert(Sync.IsServer);
MyCubeGrid firstEntity = null;
foreach (var gridbuilder in gridsToMerge)
{
var pastedGrid = MyEntities.CreateFromObjectBuilder(gridbuilder) as MyCubeGrid;
if (firstEntity == null)
firstEntity = pastedGrid;
MyEntities.Add(pastedGrid);
MySession.Static.TotalBlocksCreated += (uint)pastedGrid.BlocksCount;
}
MatrixI mergingTransform = CalculateMergeTransform(firstEntity,
WorldToGridInteger(firstEntity.PositionComp.GetPosition()));
MergeGridInternal(firstEntity, ref mergingTransform, false);
return mergingTransform;
}
public static bool CanPasteGrid()
{
return MySession.Static.IsCopyPastingEnabled;
}
///
/// Returns biggest grid in physical group by AABB volume
///
public MyCubeGrid GetBiggestGridInGroup()
{
MyCubeGrid biggestGridInGroup = this;
double maxVolume = 0;
foreach (var grid in MyCubeGridGroups.Static.Physical.GetGroup(this).Nodes)
{
var volume = grid.NodeData.PositionComp.WorldAABB.Size.Volume;
if (volume > maxVolume)
{
maxVolume = volume;
biggestGridInGroup = grid.NodeData;
}
}
return biggestGridInGroup;
}
public void ConvertFracturedBlocksToComponents()
{
Debug.Assert(Sync.IsServer);
var fracturedBlocks = new List();
foreach (var block in m_cubeBlocks)
{
MyFracturedBlock fb = block.FatBlock as MyFracturedBlock;
if (fb != null)
fracturedBlocks.Add(fb);
}
bool oldEnabled = EnableGenerators(false);
try
{
foreach (var fb in fracturedBlocks)
{
var convertedBuilder = fb.ConvertToOriginalBlocksWithFractureComponent();
RemoveBlockInternal(fb.SlimBlock, true, markDirtyDisconnects: false);
if (convertedBuilder != null)
AddBlock(convertedBuilder, false);
}
}
finally
{
EnableGenerators(oldEnabled);
}
}
public void PrepareMultiBlockInfos()
{
Debug.Assert(MyFakes.ENABLE_MULTIBLOCK_PART_IDS);
foreach (var block in GetBlocks())
AddMultiBlockInfo(block);
}
internal void AddMultiBlockInfo(MySlimBlock block)
{
Debug.Assert(MyFakes.ENABLE_MULTIBLOCK_PART_IDS);
var compoundBlock = block.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
{
foreach (var blockInCompound in compoundBlock.GetBlocks())
if (blockInCompound.IsMultiBlockPart)
AddMultiBlockInfo(blockInCompound);
return;
}
Debug.Assert(compoundBlock == null);
if (!block.IsMultiBlockPart)
return;
if (m_multiBlockInfos == null)
m_multiBlockInfos = new Dictionary();
MyCubeGridMultiBlockInfo mbInfo;
if (!m_multiBlockInfos.TryGetValue(block.MultiBlockId, out mbInfo))
{
mbInfo = new MyCubeGridMultiBlockInfo();
mbInfo.MultiBlockId = block.MultiBlockId;
mbInfo.MultiBlockDefinition = block.MultiBlockDefinition;
mbInfo.MainBlockDefinition = MyDefinitionManager.Static.GetCubeBlockDefinitionForMultiBlock(block.MultiBlockDefinition.Id.SubtypeName);
Debug.Assert(mbInfo.MainBlockDefinition != null);
m_multiBlockInfos.Add(block.MultiBlockId, mbInfo);
}
mbInfo.Blocks.Add(block);
}
internal void RemoveMultiBlockInfo(MySlimBlock block)
{
Debug.Assert(MyFakes.ENABLE_MULTIBLOCK_PART_IDS);
if (m_multiBlockInfos == null)
return;
var compoundBlock = block.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
{
foreach (var blockInCompound in compoundBlock.GetBlocks())
if (blockInCompound.IsMultiBlockPart)
RemoveMultiBlockInfo(blockInCompound);
return;
}
Debug.Assert(compoundBlock == null);
if (!block.IsMultiBlockPart)
return;
MyCubeGridMultiBlockInfo mbInfo;
if (m_multiBlockInfos.TryGetValue(block.MultiBlockId, out mbInfo))
{
if (mbInfo.Blocks.Remove(block))
{
if (mbInfo.Blocks.Count == 0)
{
if (m_multiBlockInfos.Remove(block.MultiBlockId))
{
if (m_multiBlockInfos.Count == 0)
m_multiBlockInfos = null;
}
}
}
}
}
public MyCubeGridMultiBlockInfo GetMultiBlockInfo(int multiBlockId)
{
if (m_multiBlockInfos != null)
{
MyCubeGridMultiBlockInfo info;
if (m_multiBlockInfos.TryGetValue(multiBlockId, out info))
return info;
}
return null;
}
///
/// Writes multiblocks (compound block and block ID) to outMultiBlocks collection with the same multiblockId.
///
public void GetBlocksInMultiBlock(int multiBlockId, HashSet> outMultiBlocks)
{
Debug.Assert(multiBlockId != 0);
if (multiBlockId == 0)
return;
var multiBlockInfo = GetMultiBlockInfo(multiBlockId);
if (multiBlockInfo != null)
{
foreach (var multiBlockPart in multiBlockInfo.Blocks)
{
var existingBlock = GetCubeBlock(multiBlockPart.Position);
var compoundBlock = existingBlock.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
{
ushort? blockInCompoundId = compoundBlock.GetBlockId(multiBlockPart);
Debug.Assert(blockInCompoundId != null);
outMultiBlocks.Add(new Tuple(existingBlock, blockInCompoundId));
}
else
{
outMultiBlocks.Add(new Tuple(existingBlock, null));
}
}
}
}
public bool CanAddMultiBlocks(MyCubeGridMultiBlockInfo multiBlockInfo, ref MatrixI transform, List multiBlockIndices)
{
MatrixI outBlockRotation;
foreach (var index in multiBlockIndices)
{
Debug.Assert(index < multiBlockInfo.MultiBlockDefinition.BlockDefinitions.Length);
if (index < multiBlockInfo.MultiBlockDefinition.BlockDefinitions.Length)
{
var blockDefInfo = multiBlockInfo.MultiBlockDefinition.BlockDefinitions[index];
MyCubeBlockDefinition blockDef;
if (!MyDefinitionManager.Static.TryGetCubeBlockDefinition(blockDefInfo.Id, out blockDef) || blockDef == null)
return false;
var position = Vector3I.Transform(blockDefInfo.Min, ref transform);
var blockDefRotation = new MatrixI(blockDefInfo.Forward, blockDefInfo.Up);
MatrixI.Multiply(ref blockDefRotation, ref transform, out outBlockRotation);
var blockOrientation = outBlockRotation.GetBlockOrientation();
if (!CanPlaceBlock(position, position, blockOrientation, blockDef, ignoreMultiblockId: multiBlockInfo.MultiBlockId, ignoreFracturedPieces: true))
return false;
}
}
return true;
}
///
/// Builds multiblock parts according to specified indices.
///
public bool BuildMultiBlocks(MyCubeGridMultiBlockInfo multiBlockInfo, ref MatrixI transform, List multiBlockIndices, long builderEntityId)
{
List locations = new List();
List blockBuilders = new List();
MatrixI outBlockRotation;
foreach (var index in multiBlockIndices)
{
Debug.Assert(index < multiBlockInfo.MultiBlockDefinition.BlockDefinitions.Length);
if (index < multiBlockInfo.MultiBlockDefinition.BlockDefinitions.Length)
{
var blockDefInfo = multiBlockInfo.MultiBlockDefinition.BlockDefinitions[index];
MyCubeBlockDefinition blockDef;
if (!MyDefinitionManager.Static.TryGetCubeBlockDefinition(blockDefInfo.Id, out blockDef) || blockDef == null)
return false;
var position = Vector3I.Transform(blockDefInfo.Min, ref transform);
var blockDefRotation = new MatrixI(blockDefInfo.Forward, blockDefInfo.Up);
MatrixI.Multiply(ref blockDefRotation, ref transform, out outBlockRotation);
var blockOrientation = outBlockRotation.GetBlockOrientation();
if (!CanPlaceBlock(position, position, blockOrientation, blockDef, ignoreMultiblockId: multiBlockInfo.MultiBlockId))
return false;
MyObjectBuilder_CubeBlock blockBuilder = MyObjectBuilderSerializer.CreateNewObject(blockDefInfo.Id) as MyObjectBuilder_CubeBlock;
blockBuilder.Orientation = Base6Directions.GetOrientation(blockOrientation.Forward, blockOrientation.Up);
blockBuilder.Min = position;
blockBuilder.ColorMaskHSV = MyPlayer.SelectedColor;
blockBuilder.MultiBlockId = multiBlockInfo.MultiBlockId;
blockBuilder.MultiBlockIndex = index;
blockBuilder.MultiBlockDefinition = multiBlockInfo.MultiBlockDefinition.Id;
blockBuilders.Add(blockBuilder);
MyBlockLocation location = new MyBlockLocation();
location.Min = position;
location.Max = position;
location.CenterPos = position;
location.Orientation = new MyBlockOrientation(blockOrientation.Forward, blockOrientation.Up);
location.BlockDefinition = blockDefInfo.Id;
location.EntityId = MyEntityIdentifier.AllocateId();
location.Owner = builderEntityId;
locations.Add(location);
}
}
Debug.Assert(locations.Count == blockBuilders.Count);
if (MySession.Static.SurvivalMode)
{
MyEntity toolOwner = MyEntities.GetEntityById(builderEntityId);
if (toolOwner == null)
return false;
HashSet setLocations = new HashSet(locations);
MyCubeBuilder.BuildComponent.GetBlocksPlacementMaterials(setLocations, this);
if (!MyCubeBuilder.BuildComponent.HasBuildingMaterials(toolOwner))
return false;
}
for (int i = 0; i < locations.Count && i < blockBuilders.Count; ++i)
{
var location = locations[i];
var blockBuilder = blockBuilders[i];
MyMultiplayer.RaiseEvent(this, x => x.BuildBlockRequest, MyPlayer.SelectedColor.PackHSVToUint(), location, blockBuilder, builderEntityId,false,MySession.Static.LocalPlayerId);
}
return true;
}
private bool GetMissingBlocksMultiBlock(int multiblockId, out MyCubeGridMultiBlockInfo multiBlockInfo, out MatrixI transform, List multiBlockIndices)
{
transform = default(MatrixI);
multiBlockInfo = GetMultiBlockInfo(multiblockId);
Debug.Assert(multiBlockInfo != null);
if (multiBlockInfo == null)
return false;
return multiBlockInfo.GetMissingBlocks(out transform, multiBlockIndices);
}
public bool CanAddMissingBlocksInMultiBlock(int multiBlockId)
{
try
{
MatrixI transform;
MyCubeGridMultiBlockInfo multiBlockInfo;
if (!GetMissingBlocksMultiBlock(multiBlockId, out multiBlockInfo, out transform, m_tmpMultiBlockIndices))
return false;
return CanAddMultiBlocks(multiBlockInfo, ref transform, m_tmpMultiBlockIndices);
}
finally
{
m_tmpMultiBlockIndices.Clear();
}
}
public void AddMissingBlocksInMultiBlock(int multiBlockId, long toolOwnerId)
{
Debug.Assert(Sync.IsServer);
try
{
MatrixI transform;
MyCubeGridMultiBlockInfo multiBlockInfo;
if (!GetMissingBlocksMultiBlock(multiBlockId, out multiBlockInfo, out transform, m_tmpMultiBlockIndices))
return;
BuildMultiBlocks(multiBlockInfo, ref transform, m_tmpMultiBlockIndices, toolOwnerId);
}
finally
{
m_tmpMultiBlockIndices.Clear();
}
}
///
/// Checks if the given block can be added to place where multiblock area (note that even if some parts of multiblock are destroyed then they still
/// occupy - virtually - its place).
///
public bool CanAddOtherBlockInMultiBlock(Vector3I min, Vector3I max, MyBlockOrientation orientation, MyCubeBlockDefinition definition, int? ignoreMultiblockId)
{
if (m_multiBlockInfos == null)
return true;
foreach (var entry in m_multiBlockInfos)
{
if (ignoreMultiblockId == null || ignoreMultiblockId.Value != entry.Key)
{
var mbInfo = entry.Value;
if (!mbInfo.CanAddBlock(ref min, ref max, orientation, definition))
return false;
}
}
return true;
}
public static bool IsGridInCompleteState(MyCubeGrid grid)
{
foreach (var block in grid.CubeBlocks)
{
if (!block.IsFullIntegrity || block.BuildLevelRatio != 1.0f)
return false;
}
return true;
}
public bool WillRemoveBlockSplitGrid( MySlimBlock testBlock )
{
return m_disconnectHelper.TryDisconnect( testBlock );
}
/// In world coordinates
public MySlimBlock GetTargetedBlock(Vector3D position)
{
Vector3I blockPos;
FixTargetCube(out blockPos, Vector3D.Transform(position, PositionComp.WorldMatrixNormalizedInv) * GridSizeR);
return GetCubeBlock(blockPos);
}
#region Multiplayer
[Event, Reliable, Server]
public static void TryCreateGrid_Implementation(MyCubeSize cubeSize, bool isStatic, MyPositionAndOrientation position, long inventoryEntityId, bool instantBuild)
{
string prefabName;
bool isAdmin = (MyEventContext.Current.IsLocallyInvoked || MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value));
MyDefinitionManager.Static.GetBaseBlockPrefabName(cubeSize, isStatic, MySession.Static.CreativeMode || (instantBuild && isAdmin), out prefabName);
if (prefabName == null)
return;
var gridBuilders = MyPrefabManager.Static.GetGridPrefab(prefabName);
Debug.Assert(gridBuilders != null && gridBuilders.Length > 0);
if (gridBuilders == null || gridBuilders.Length == 0)
return;
foreach (var gridBuilder in gridBuilders)
{
gridBuilder.PositionAndOrientation = position;
}
MyEntities.RemapObjectBuilderCollection(gridBuilders);
if ((instantBuild && isAdmin) == false)
{
MyEntity inventoryOwner;
if (MyEntities.TryGetEntityById(inventoryEntityId, out inventoryOwner) && inventoryOwner != null)
{
MyInventoryBase buildInventory = MyCubeBuilder.BuildComponent.GetBuilderInventory(inventoryOwner);
if (buildInventory != null)
{
MyGridClipboard.CalculateItemRequirements(gridBuilders, m_buildComponents);
foreach (var item in m_buildComponents.TotalMaterials)
{
buildInventory.RemoveItemsOfType(item.Value, item.Key);
}
}
}
else if (isAdmin == false && MySession.Static.CreativeMode == false && MyFinalBuildConstants.IS_OFFICIAL)
{
return;
}
}
List results = new List();
foreach (var entity in gridBuilders)
{
MySandboxGame.Log.WriteLine("CreateCompressedMsg: Type: " + entity.GetType().Name.ToString() + " Name: " + entity.Name + " EntityID: " + entity.EntityId.ToString("X8"));
// This does not work, because EntityCreation is not supported on separate thread
//MyEntities.CreateAsync(entity, false, (e) => results.Add((MyCubeGrid)e));
MyCubeGrid grid = MyEntities.CreateFromObjectBuilder(entity) as MyCubeGrid;
results.Add(grid);
if (instantBuild && isAdmin)
{
ChangeOwnership(inventoryEntityId, grid);
}
MySandboxGame.Log.WriteLine("Status: Exists(" + MyEntities.EntityExists(entity.EntityId) + ") InScene(" + ((entity.PersistentFlags & MyPersistentEntityFlags2.InScene) == MyPersistentEntityFlags2.InScene) + ")");
}
AfterPaste(results, Vector3.Zero, false);
}
///
/// Use only for cut request
///
public void SendGridCloseRequest()
{
MyMultiplayer.RaiseStaticEvent(s => OnGridClosedRequest, EntityId);
}
[Event, Reliable, Server]
static void OnGridClosedRequest(long entityId)
{
if (!MyEventContext.Current.IsLocallyInvoked && !MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value))
{
MyEventContext.ValidationFailed();
return;
}
MyEntity entity;
MyEntities.TryGetEntityById(entityId, out entity);
if (entity == null)
return;
foreach (var block in (entity as MyCubeGrid).GetBlocks())
{
block.RemoveAuthorship();
var cockpit = block.FatBlock as MyCockpit;
if (cockpit != null && cockpit.Pilot != null)
cockpit.Use();
}
// Test right to closing entity (e.g. is creative mode?)
if (!entity.MarkedForClose)
entity.Close(); // close only on server, server uses replication to propagate it to clients
}
private class PasteGridData : ParallelTasks.WorkData
{
List m_entities;
bool m_detectDisconnects;
long m_inventoryEntityId;
Vector3 m_objectVelocity;
bool m_multiBlock;
bool m_instantBuild;
List m_results;
bool m_canPlaceGrid;
List m_resultIDs;
bool m_removeScripts;
//GK: Added minimum event info, because at parallel task completion (OnPasteCompleted) we no longer have MyEventContext.Current
public readonly EndpointId SenderEndpointId;
public readonly bool IsLocallyInvoked;
public PasteGridData(List entities, bool detectDisconnects, long inventoryEntityId, Vector3 objectVelocity, bool multiBlock,
bool instantBuild, bool shouldRemoveScripts, EndpointId senderEndpointId, bool isLocallyInvoked)
{
m_entities = new List(entities);
m_detectDisconnects = detectDisconnects;
m_inventoryEntityId = inventoryEntityId;
m_objectVelocity = objectVelocity;
m_multiBlock = multiBlock;
m_instantBuild = instantBuild;
SenderEndpointId = senderEndpointId;
IsLocallyInvoked = isLocallyInvoked;
m_removeScripts = shouldRemoveScripts;
}
public void TryPasteGrid()
{
bool isAdmin = (MyEventContext.Current.IsLocallyInvoked || MySession.Static.HasPlayerCreativeRights(MyEventContext.Current.Sender.Value));
bool validBattlePaste = false;
if (MySession.Static.SurvivalMode && !validBattlePaste && !isAdmin)
{
return;
}
//Fix for when the player wants to paste a ship with the same object builder multiple times. Should be done in a less performance heavy way if possible
for (int i = 0; i < m_entities.Count; i++)
{
m_entities[i] = (MyObjectBuilder_CubeGrid)m_entities[i].Clone();
}
MyEntities.RemapObjectBuilderCollection(m_entities);
if (m_removeScripts)
{
foreach (var grid in m_entities)
{
foreach (var block in grid.CubeBlocks)
{
var programmable = block as MyObjectBuilder_MyProgrammableBlock;
if (programmable == null)
continue;
programmable.Program = null;
}
}
}
if ((m_instantBuild && isAdmin) == false)
{
MyEntity inventoryOwner;
if (MyEntities.TryGetEntityById(m_inventoryEntityId, out inventoryOwner) && inventoryOwner != null)
{
// TODO: If there's not enough materials, it should return false!
if (m_multiBlock)
{
MyMultiBlockClipboard.TakeMaterialsFromBuilder(m_entities, inventoryOwner);
}
else
{
MyInventoryBase buildInventory = MyCubeBuilder.BuildComponent.GetBuilderInventory(inventoryOwner);
if (buildInventory != null)
{
MyGridClipboard.CalculateItemRequirements(m_entities, m_buildComponents);
foreach (var item in m_buildComponents.TotalMaterials)
{
buildInventory.RemoveItemsOfType(item.Value, item.Key);
}
}
}
}
}
m_results = new List();
MyEntityIdentifier.LazyInitPerThreadStorage(2048);
m_canPlaceGrid = true;
foreach (var entity in m_entities)
{
MySandboxGame.Log.WriteLine("CreateCompressedMsg: Type: " + entity.GetType().Name.ToString() + " Name: " + entity.Name + " EntityID: " + entity.EntityId.ToString("X8"));
MyCubeGrid grid = MyEntities.CreateFromObjectBuilder(entity, false) as MyCubeGrid;
m_results.Add(grid);
m_canPlaceGrid &= TestPastedGridPlacement(grid);
if (m_canPlaceGrid == false)
{
break;
}
if (m_instantBuild && isAdmin)
{
ChangeOwnership(m_inventoryEntityId, grid);
}
MySandboxGame.Log.WriteLine("Status: Exists(" + MyEntities.EntityExists(entity.EntityId) + ") InScene(" + ((entity.PersistentFlags & MyPersistentEntityFlags2.InScene) == MyPersistentEntityFlags2.InScene) + ")");
}
m_resultIDs = new List();
MyEntityIdentifier.GetPerThreadEntities(m_resultIDs);
MyEntityIdentifier.ClearPerThreadEntities();
}
private bool TestPastedGridPlacement(MyCubeGrid grid)
{
var settings = MyClipboardComponent.ClipboardDefinition.PastingSettings.GetGridPlacementSettings(grid.GridSizeEnum, grid.IsStatic);
return TestPlacementArea(grid, grid.IsStatic, ref settings, (BoundingBoxD)grid.PositionComp.LocalAABB, !grid.IsStatic);
}
public void Callback()
{
if (!IsLocallyInvoked)
{
MyMultiplayer.RaiseStaticEvent(s => MyCubeGrid.SendHudNotificationAfterPaste, SenderEndpointId);
}
else if (!MySandboxGame.IsDedicated)
{
MyHud.PopRotatingWheelVisible();
}
if (m_canPlaceGrid && m_results.Count > 0)
{
foreach (var entity in m_resultIDs)
{
VRage.ModAPI.IMyEntity foundEntity;
MyEntityIdentifier.TryGetEntity(entity.EntityId, out foundEntity);
if (foundEntity == null)
MyEntityIdentifier.AddEntityWithId(entity);
else
Debug.Fail("Two threads added the same entity");
}
foreach (var grid in m_results)
{
m_canPlaceGrid &= TestPastedGridPlacement(grid);
}
if (m_canPlaceGrid)
AfterPaste(m_results, m_objectVelocity, m_detectDisconnects);
}
else
{
foreach (var grid in m_results)
{
grid.Close();
}
if (!IsLocallyInvoked)
{
MyMultiplayer.RaiseStaticEvent(s => MyCubeGrid.ShowPasteFailedOperation, SenderEndpointId);
}
}
}
}
[Event, Reliable, Server]
public static void TryPasteGrid_Implementation(List entities, bool detectDisconnects, long inventoryEntityId, Vector3 objectVelocity, bool multiBlock,
bool instantBuild)
{
if (!MyEventContext.Current.IsLocallyInvoked && !MySession.Static.IsCopyPastingEnabledForUser(MyEventContext.Current.Sender.Value)) //GK: Check the correct user that is the one of the current event context
{
MyEventContext.ValidationFailed();
MyMultiplayer.RaiseStaticEvent(s => MyCubeGrid.SendHudNotificationAfterPaste, MyEventContext.Current.Sender);
return;
}
bool shouldRemoveScripts = !MySession.Static.IsUserScripter(MyEventContext.Current.Sender.Value);
PasteGridData workData = new PasteGridData(entities, detectDisconnects, inventoryEntityId, objectVelocity, multiBlock, instantBuild, shouldRemoveScripts, MyEventContext.Current.Sender, MyEventContext.Current.IsLocallyInvoked);
Parallel.Start(TryPasteGrid_ImplementationInternal, OnPasteCompleted, workData);
}
static void TryPasteGrid_ImplementationInternal(WorkData workData)
{
PasteGridData pasteData = workData as PasteGridData;
if (pasteData == null)
{
workData.FlagAsFailed();
return;
}
pasteData.TryPasteGrid();
}
static void OnPasteCompleted(WorkData workData)
{
PasteGridData pasteData = workData as PasteGridData;
if (pasteData == null)
{
workData.FlagAsFailed();
return;
}
pasteData.Callback();
}
[Event, Reliable, Client]
public static void ShowPasteFailedOperation()
{
MyHud.Notifications.Add(MyNotificationSingletons.PasteFailed);
}
[Event, Reliable, Client]
public static void SendHudNotificationAfterPaste()
{
MyHud.PopRotatingWheelVisible();
}
private static void ChangeOwnership(long inventoryEntityId, MyCubeGrid grid)
{
MyEntity inventoryOwner;
if (MyEntities.TryGetEntityById(inventoryEntityId, out inventoryOwner) && inventoryOwner != null)
{
MyCharacter character = inventoryOwner as MyCharacter;
if (character != null)
{
grid.ChangeGridOwner(character.ControllerInfo.Controller.Player.Identity.IdentityId, MyOwnershipShareModeEnum.Faction);
}
}
}
static void AfterPaste(List grids, Vector3 objectVelocity, bool detectDisconnects)
{
foreach (var pastedGrid in grids)
{
if (pastedGrid.IsStatic)
{
pastedGrid.TestDynamic = MyCubeGrid.MyTestDynamicReason.GridCopied;
}
MyEntities.Add(pastedGrid);
if (pastedGrid.Physics != null)
{
if (!pastedGrid.IsStatic)
{
pastedGrid.Physics.LinearVelocity = objectVelocity;
}
if (!pastedGrid.IsStatic && MySession.Static.ControlledEntity != null && MySession.Static.ControlledEntity.Entity.Physics != null)
{
if (MySession.Static.ControlledEntity != null)
pastedGrid.Physics.AngularVelocity = MySession.Static.ControlledEntity.Entity.Physics.AngularVelocity;
}
}
else
Debug.Fail("Pasted grid without physics!");
if (detectDisconnects)
{
pastedGrid.DetectDisconnectsAfterFrame();
}
MySession.Static.TotalBlocksCreated += (uint)pastedGrid.BlocksCount;
if (pastedGrid.IsStatic)
{
foreach (var block in pastedGrid.CubeBlocks)
{
if (pastedGrid.DetectMerge(block) != null)
break;
}
}
pastedGrid.IsReadyForReplication = true;
}
MatrixD worldMatrix = grids[0].PositionComp.WorldMatrix;
// grid[0] because we need only first grid for this
bool result = MyCoordinateSystem.Static.IsLocalCoordSysExist(ref worldMatrix, grids[0].GridSize);
if (grids[0].GridSizeEnum == MyCubeSize.Large)
{
if (result)
{
MyCoordinateSystem.Static.RegisterCubeGrid(grids[0]);
}
else
{
MyCoordinateSystem.Static.CreateCoordSys(grids[0], MyClipboardComponent.ClipboardDefinition.PastingSettings.StaticGridAlignToCenter, true);
}
}
}
[Event, Reliable, Broadcast]
public void RequestConvertToDynamic()
{
if (IsStatic)
{
this.ConvertToDynamic();
}
}
public void RecalculateGravity()
{
m_gravity = MyGravityProviderSystem.CalculateNaturalGravityInPoint(PositionComp.GetPosition());
if (MyPerGameSettings.Game == GameEnum.VRS_GAME)
m_gravity += MyGravityProviderSystem.CalculateArtificialGravityInPoint(PositionComp.GetPosition());
}
public void ActivatePhysics()
{
if (Physics != null && Physics.Enabled)
{
Physics.RigidBody.Activate();
if (Physics.RigidBody2 != null)
{
Physics.RigidBody2.Activate();
}
}
}
void SendDirtyBones(Vector3I minBone, Vector3I maxBone, MyGridSkeleton skeleton)
{
m_boneByteList.Clear();
skeleton.SerializePart(minBone, maxBone, GridSize, m_boneByteList);
if (m_boneByteList.Count > 0)
{
MyMultiplayer.RaiseEvent(this, x => x.OnBonesReceived, minBone, maxBone, m_boneByteList);
}
}
[Event, Reliable, Broadcast]
void OnBonesReceived(Vector3I minBone, Vector3I maxBone, List boneByteList)
{
Skeleton.DeserializePart(minBone, maxBone, GridSize, boneByteList);
Vector3I minCube = Vector3I.Zero;
Vector3I maxCube = Vector3I.Zero;
// To hit incident cubes
Vector3I min = minBone;// -Vector3I.One;
Vector3I max = maxBone;// +Vector3I.One;
Skeleton.Wrap(ref minCube, ref min);
Skeleton.Wrap(ref maxCube, ref max);
minCube -= Vector3I.One;
maxCube += Vector3I.One;
Vector3I pos;
for (pos.X = minCube.X; pos.X <= maxCube.X; pos.X++)
{
for (pos.Y = minCube.Y; pos.Y <= maxCube.Y; pos.Y++)
{
for (pos.Z = minCube.Z; pos.Z <= maxCube.Z; pos.Z++)
{
SetCubeDirty(pos);
}
}
}
}
[Event, Reliable, Broadcast]
void OnBonesMultiplied(Vector3I blockLocation, float multiplier)
{
var block = GetCubeBlock(blockLocation);
Debug.Assert(block != null, "Block was null in OnBonesMultiplied handler!");
if (block == null) return;
MultiplyBlockSkeleton(block, multiplier);
}
public void SendReflectorState(MyMultipleEnabledEnum value)
{
MyMultiplayer.RaiseEvent(this, x => x.RelfectorStateRecived, value);
}
[Event, Reliable, Server, Broadcast]
void RelfectorStateRecived(MyMultipleEnabledEnum value)
{
GridSystems.ReflectorLightSystem.ReflectorStateChanged(value);
}
public void SendIntegrityChanged(MySlimBlock mySlimBlock, MyIntegrityChangeEnum integrityChangeType, long toolOwner)
{
MyMultiplayer.RaiseEvent(this, x => x.BlockIntegrityChanged,mySlimBlock.Position,GetSubBlockId(mySlimBlock), mySlimBlock.BuildIntegrity,mySlimBlock.Integrity, integrityChangeType, toolOwner);
}
public void SendStockpileChanged(MySlimBlock mySlimBlock, List list)
{
if (list.Count > 0)
{
MyMultiplayer.RaiseEvent(this, x => x.BlockStockpileChanged, mySlimBlock.Position, GetSubBlockId(mySlimBlock), list);
}
}
public void SendFractureComponentRepaired(MySlimBlock mySlimBlock, long toolOwner)
{
MyMultiplayer.RaiseEvent(this, x => x.FractureComponentRepaired, mySlimBlock.Position, GetSubBlockId(mySlimBlock), toolOwner);
}
ushort GetSubBlockId(MySlimBlock slimBlock)
{
var block = slimBlock.CubeGrid.GetCubeBlock(slimBlock.Position);
MyCompoundCubeBlock compoundBlock = null;
if (block != null)
compoundBlock = block.FatBlock as MyCompoundCubeBlock;
if (compoundBlock != null)
{
var subBlockId = compoundBlock.GetBlockId(slimBlock);
return subBlockId ?? 0;
}
return 0;
}
public void RequestFillStockpile(Vector3I blockPosition, MyInventory fromInventory)
{
MyMultiplayer.RaiseEvent(this, x => x.OnStockpileFillRequest, blockPosition, fromInventory.Owner.EntityId, fromInventory.InventoryIdx);
}
[Event, Reliable, Server]
void OnStockpileFillRequest(Vector3I blockPosition, long ownerEntityId, byte inventoryIndex)
{
var block = GetCubeBlock(blockPosition);
Debug.Assert(block != null, "Could not find block whose stockpile fill was requested");
if (block == null) return;
MyEntity ownerEntity = null;
if (!MyEntities.TryGetEntityById(ownerEntityId, out ownerEntity))
{
Debug.Assert(false, "Stockpile fill inventory owner entity was null");
return;
}
var owner = (ownerEntity != null && ownerEntity.HasInventory) ? ownerEntity : null;
Debug.Assert(owner != null, "Stockpile fill inventory owner was not an inventory owner");
var inventory = owner.GetInventory(inventoryIndex);
Debug.Assert(inventory != null, "Stockpile fill inventory owner did not have the given inventory");
block.MoveItemsToConstructionStockpile(inventory as MyInventoryBase);
}
public void RequestSetToConstruction(Vector3I blockPosition, MyInventory fromInventory)
{
MyMultiplayer.RaiseEvent(this, x => x.OnSetToConstructionRequest, blockPosition, fromInventory.Owner.EntityId, fromInventory.InventoryIdx, MySession.Static.LocalPlayerId);
}
[Event, Reliable, Server]
private void OnSetToConstructionRequest(Vector3I blockPosition,long ownerEntityId, byte inventoryIndex,long requestingPlayer)
{
var block = GetCubeBlock(blockPosition);
Debug.Assert(block != null, "Could not find block to set to construction site");
if (block == null) return;
block.SetToConstructionSite();
MyEntity ownerEntity = null;
if (!MyEntities.TryGetEntityById(ownerEntityId, out ownerEntity))
{
Debug.Assert(false, "Set to construction site inventory owner entity was null");
return;
}
var owner = (ownerEntity != null && ownerEntity.HasInventory) ? ownerEntity : null;
Debug.Assert(owner != null, "Set to construction site inventory owner was not an inventory owner");
var inventory = owner.GetInventory(inventoryIndex) as MyInventoryBase;
Debug.Assert(inventory != null, "Set to construction site inventory owner did not have the given inventory");
block.MoveItemsToConstructionStockpile(inventory);
block.IncreaseMountLevel(MyWelder.WELDER_AMOUNT_PER_SECOND * VRage.Game.MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS, requestingPlayer);
}
public void ChangePowerProducerState(MyMultipleEnabledEnum enabledState, long playerId)
{
MyMultiplayer.RaiseEvent(this, x => x.OnPowerProducerStateRequest, enabledState, playerId); ;
}
public void SendPowerDistributorState(MyMultipleEnabledEnum enabledState, long playerId)
{
MyMultiplayer.RaiseEvent(this, x => x.OnPowerProducerStateRequest, enabledState, playerId);
}
[Event, Reliable, Server,Broadcast]
private void OnPowerProducerStateRequest(MyMultipleEnabledEnum enabledState, long playerId)
{
this.GridSystems.SyncObject_PowerProducerStateChanged(enabledState, playerId);
}
[Event, Reliable, Server, BroadcastExcept]
private void OnZeroThrustReceived()
{
var thrustComp = Components.Get();
if (thrustComp != null)
{
thrustComp.ControlThrust = Vector3.Zero;
}
}
public void RequestConversionToShip()
{
MyMultiplayer.RaiseEvent(this, x => x.OnConvertedToShipRequest);
}
public void RequestConversionToStation()
{
MyMultiplayer.RaiseEvent(this, x => x.OnConvertedToStationRequest);
}
[Event, Reliable, Server]
public void OnConvertedToShipRequest()
{
if(IsStatic == false)
{
return;
}
ConvertToDynamic();
MyMultiplayer.RaiseEvent(this, x => x.ConvertToDynamic);
}
[Event, Reliable, Server]
public void OnConvertedToStationRequest()
{
if (IsStatic)
{
return;
}
ConvertToStatic();
MyMultiplayer.RaiseEvent(this, x => x.ConvertToStatic);
}
public void ChangeOwnerRequest(MyCubeGrid grid, MyCubeBlock block, long playerId, MyOwnershipShareModeEnum shareMode)
{
MyMultiplayer.RaiseEvent(this, x => x.OnChangeOwnerRequest,block.EntityId,playerId,shareMode);
}
[Event, Reliable, Server]
void OnChangeOwnerRequest(long blockId, long owner, MyOwnershipShareModeEnum shareMode)
{
MyCubeBlock block = null;
if (MyEntities.TryGetEntityById(blockId, out block))
{
var ownerComp = block.Components.Get();
if (Sync.IsServer && block.IDModule != null && ((block.IDModule.Owner == 0) || block.IDModule.Owner == owner || (owner == 0)))
{
OnChangeOwner(blockId, owner, shareMode);
MyMultiplayer.RaiseEvent(this, x => x.OnChangeOwner, blockId, owner, shareMode);
}
else if (Sync.IsServer && ownerComp != null && (ownerComp.OwnerId == 0 || ownerComp.OwnerId == owner || owner == 0))
{
OnChangeOwner(blockId, owner, shareMode);
MyMultiplayer.RaiseEvent(this, x => x.OnChangeOwner, blockId, owner, shareMode);
}
else
{
bool shouldHaveOwnership = block.BlockDefinition.ContainsComputer();
if (block.UseObjectsComponent != null)
shouldHaveOwnership = shouldHaveOwnership || block.UseObjectsComponent.GetDetectors("ownership").Count > 0;
if (shouldHaveOwnership)
System.Diagnostics.Debug.Fail("Invalid ownership change request!");
}
}
}
[Event, Reliable, Broadcast]
void OnChangeOwner(long blockId, long owner, MyOwnershipShareModeEnum shareMode)
{
MyCubeBlock block = null;
if (MyEntities.TryGetEntityById(blockId, out block))
{
block.ChangeOwner(owner, shareMode);
}
}
void HandBrakeChanged()
{
GridSystems.WheelSystem.HandBrake = m_handBrakeSync;
}
public void SetHandbrakeRequest(bool v)
{
m_handBrakeSync.Value = v;
}
public void ChangeGridOwner(long playerId, MyOwnershipShareModeEnum shareMode)
{
MyMultiplayer.RaiseEvent(this, x => x.OnChangeGridOwner, playerId, shareMode);
OnChangeGridOwner(playerId, shareMode);
}
[Event, Reliable, Broadcast]
void OnChangeGridOwner(long playerId, MyOwnershipShareModeEnum shareMode)
{
foreach (var block in GetBlocks())
{
if (block.FatBlock != null && block.BlockDefinition.RatioEnoughForOwnership(block.BuildLevelRatio))
{
block.FatBlock.ChangeOwner(playerId, shareMode);
}
}
}
public void AnnounceRemoveSplit(List blocks)
{
m_tmpPositionListSend.Clear();
foreach (var block in blocks)
{
m_tmpPositionListSend.Add(block.Position);
}
MyMultiplayer.RaiseEvent(this, x => x.OnRemoveSplit, m_tmpPositionListSend);
}
[Event, Reliable, Broadcast]
void OnRemoveSplit(List removedBlocks)
{
m_tmpPositionListReceive.Clear();
foreach (var position in removedBlocks)
{
var block = GetCubeBlock(position);
Debug.Assert(block != null, "Block was null when trying to remove a grid split. Desync?");
if (block == null)
{
MySandboxGame.Log.WriteLine("Block was null when trying to remove a grid split. Desync?");
continue;
}
m_tmpBlockListReceive.Add(block);
}
MyCubeGrid.RemoveSplit(this, m_tmpBlockListReceive, 0, m_tmpBlockListReceive.Count, sync: false);
}
public void ChangeDisplayNameRequest(String displayName)
{
MyMultiplayer.RaiseEvent(this, x => x.OnChangeDisplayNameRequest, displayName);
}
[Event, Reliable, Server,Broadcast]
void OnChangeDisplayNameRequest(String displayName)
{
DisplayName = displayName;
}
public void ModifyGroup(MyBlockGroup group)
{
m_tmpBlockIdList.Clear();
foreach (var block in group.Blocks)
{
m_tmpBlockIdList.Add(block.EntityId);
}
MyMultiplayer.RaiseEvent(this, x => x.OnModifyGroupSuccess, group.Name.ToString(), m_tmpBlockIdList);
}
[Event, Reliable, Server, BroadcastExcept]
void OnModifyGroupSuccess(String name,List blocks)
{
if (blocks == null || blocks.Count == 0)
{
foreach (var group in BlockGroups)
{
if (group.Name.ToString().Equals(name))
{
RemoveGroup(group);
break;
}
}
}
else
{
MyBlockGroup group = new MyBlockGroup(this);
group.Name.Clear().Append(name);
foreach (var blockId in blocks)
{
MyTerminalBlock block = null;
if (MyEntities.TryGetEntityById(blockId, out block))
group.Blocks.Add(block);
}
AddGroup(group);
}
}
public void RazeBlockInCompoundBlock(List> locationsAndIds)
{
ConvertToLocationIdentityList(locationsAndIds, m_tmpLocationsAndIdsSend);
MyMultiplayer.RaiseEvent(this, x => x.OnRazeBlockInCompoundBlockRequest, m_tmpLocationsAndIdsSend);
}
[Event, Reliable, Server]
private void OnRazeBlockInCompoundBlockRequest(List locationsAndIds)
{
OnRazeBlockInCompoundBlock(locationsAndIds);
if (m_tmpLocationsAndIdsReceive.Count > 0)
{
// Broadcast to clients, use result collection
ConvertToLocationIdentityList(m_tmpLocationsAndIdsReceive, m_tmpLocationsAndIdsSend);
MyMultiplayer.RaiseEvent(this, x => x.OnRazeBlockInCompoundBlockSuccess, m_tmpLocationsAndIdsSend);
}
}
[Event, Reliable, Broadcast]
private void OnRazeBlockInCompoundBlockSuccess(List locationsAndIds)
{
OnRazeBlockInCompoundBlock(locationsAndIds);
}
private void OnRazeBlockInCompoundBlock(List locationsAndIds)
{
m_tmpLocationsAndIdsReceive.Clear();
RazeBlockInCompoundBlockSuccess(locationsAndIds, m_tmpLocationsAndIdsReceive);
}
private static void ConvertToLocationIdentityList(List> locationsAndIdsFrom, List locationsAndIdsTo)
{
locationsAndIdsTo.Clear();
locationsAndIdsTo.Capacity = locationsAndIdsFrom.Count;
foreach (var tuple in locationsAndIdsFrom)
locationsAndIdsTo.Add(new LocationIdentity() { Location = tuple.Item1, Id = tuple.Item2 });
}
public static void ChangeOwnersRequest(MyOwnershipShareModeEnum shareMode, List requests, long requestingPlayer)
{
System.Diagnostics.Debug.Assert((int)shareMode >= 0);
MyMultiplayer.RaiseStaticEvent(s => MyCubeGrid.OnChangeOwnersRequest, shareMode, requests, requestingPlayer);
}
[Event, Reliable, Server]
private static void OnChangeOwnersRequest(MyOwnershipShareModeEnum shareMode, List requests, long requestingPlayer)
{
MyCubeBlock block = null;
int c = 0;
MyIdentity identity = MySession.Static.Players.TryGetIdentity(requestingPlayer);
MyPlayer player = identity != null ? MyPlayer.GetPlayerFromCharacter(identity.Character) : null;
if (identity != null && identity.Character != null && player != null && MySession.Static.IsUserSpaceMaster(player.Client.SteamUserId))
c = requests.Count;
while (c < requests.Count)
{
var request = requests[c];
if (MyEntities.TryGetEntityById(request.BlockId, out block))
{
var ownerComp = block.Components.Get();
if (Sync.IsServer && block.IDModule != null && ((block.IDModule.Owner == 0) || block.IDModule.Owner == requestingPlayer || (request.Owner == 0)))
{
c++;
}
else if (Sync.IsServer && ownerComp != null && (ownerComp.OwnerId == 0 || ownerComp.OwnerId == requestingPlayer || request.Owner == 0))
{
c++;
}
else
{
System.Diagnostics.Debug.Fail("Invalid ownership change request!");
requests.RemoveAtFast(c);
}
}
else
{
c++;
}
}
if (requests.Count > 0)
{
OnChangeOwnersSuccess(shareMode, requests);
MyMultiplayer.RaiseStaticEvent(s => MyCubeGrid.OnChangeOwnersSuccess, shareMode, requests);
}
}
[Event, Reliable, Broadcast]
private static void OnChangeOwnersSuccess(MyOwnershipShareModeEnum shareMode, List