/// <summary> /// Finds a block that is attached to Controller /// </summary> /// <remarks> /// Search is performed immediately. /// For a navigating block, always use AttachedGrid.AttachmentKind.None. /// </remarks> protected bool GetLocalBlock(IMyCubeBlock autopilot, string searchFor, out IMyCubeBlock localBlock, out string message, AttachedGrid.AttachmentKind allowedAttachments = AttachedGrid.AttachmentKind.None) { searchFor = searchFor.RemoveWhitespace(); IMyCubeBlock foundBlock = null; int bestNameLength = int.MaxValue; foreach (IMyCubeBlock block in AttachedGrid.AttachedCubeBlocks(autopilot.CubeGrid, allowedAttachments, true)) { if (autopilot.canControlBlock(block)) { string blockName = block.DisplayNameText.RemoveWhitespace(); if (blockName.Length < bestNameLength && blockName.Contains(searchFor, StringComparison.InvariantCultureIgnoreCase)) { foundBlock = block; bestNameLength = blockName.Length; if (searchFor.Length == bestNameLength) { break; } } } } if (foundBlock == null) { message = "Not found: " + searchFor; localBlock = null; return(false); } message = null; localBlock = foundBlock; return(true); }
protected void SetPropertyOfBlock(Pathfinder pathfinder, string blockName, string propName, T propValue) { blockName = blockName.LowerRemoveWhitespace(); propName = propName.Trim(); // leave spaces in propName foreach (IMyCubeBlock fatblock in AttachedGrid.AttachedCubeBlocks(pathfinder.Mover.Block.CubeGrid, AttachedGrid.AttachmentKind.Permanent, true)) { if (!(fatblock is IMyTerminalBlock)) { continue; } if (!pathfinder.Mover.Block.Controller.canControlBlock(fatblock)) { continue; } if (!fatblock.DisplayNameText.LowerRemoveWhitespace().Contains(blockName)) { continue; } IMyTerminalBlock terminalBlock = fatblock as IMyTerminalBlock; ITerminalProperty <T> property = terminalBlock.GetProperty(propName) as ITerminalProperty <T>; if (property != null) { property.SetValue(fatblock, propValue); } } }
private static void RunActionOnBlock(Pathfinder pathfinder, string blockName, string actionString, List<Ingame.TerminalActionParameter> termParams) { blockName = blockName.Trim(); actionString = actionString.Trim(); // leave spaces in actionString foreach (IMyCubeBlock fatblock in AttachedGrid.AttachedCubeBlocks(pathfinder.Mover.Block.CubeGrid, AttachedGrid.AttachmentKind.Permanent, true)) { if (!(fatblock is IMyTerminalBlock)) continue; if (!pathfinder.Mover.Block.Controller.canControlBlock(fatblock)) continue; if (!fatblock.DisplayNameText.Contains(blockName, StringComparison.InvariantCultureIgnoreCase)) continue; IMyTerminalBlock terminalBlock = fatblock as IMyTerminalBlock; Sandbox.ModAPI.Interfaces.ITerminalAction actionToRun = terminalBlock.GetActionWithName(actionString); // get actionToRun on every iteration so invalid blocks can be ignored if (actionToRun != null) { if (termParams != null) actionToRun.Apply(fatblock, termParams); else actionToRun.Apply(fatblock); } } }
/// <summary> /// Tests whether a connection is possible between this and another NetworkNode. /// </summary> /// <param name="other">Node to test connection to this.</param> private CommunicationType TestConnection(RelayNode other) { if (!this.m_ownerId().canConsiderFriendly(other.m_ownerId())) { if (this.m_comp_radio != null && other.m_comp_radio != null && this.m_comp_radio.CanBroadcastPositionTo(other.m_comp_radio) && other.Storage != null) { if (s_sendPositionTo.Add(other.Storage)) { Log.TraceLog("Hostile receiver in range: " + other.DebugName + ", new storage: " + other.Storage.PrimaryNode.DebugName); } else { Log.TraceLog("Hostile receiver in range: " + other.DebugName + ", existing storage: " + other.Storage.PrimaryNode.DebugName); } } return(CommunicationType.None); } // test block connection // skip is working test so that storage doesn't split if ship powers off if (this.m_comp_blockAttach != null && other.m_comp_blockAttach != null && AttachedGrid.IsGridAttached(this.m_comp_blockAttach.CubeGrid, other.m_comp_blockAttach.CubeGrid, AttachedGrid.AttachmentKind.Terminal)) { return(CommunicationType.TwoWay); } // test laser if (this.m_comp_laser != null && other.m_comp_laser != null && m_comp_laser.Other == other.m_comp_laser && other.m_comp_laser.Other == m_comp_laser) { return(CommunicationType.TwoWay); } // test radio if (this.m_comp_radio != null && other.m_comp_radio != null) { // check radio antenna to radio antenna CommunicationType radioResult; radioResult = this.m_comp_radio.TestConnection(other.m_comp_radio); if (radioResult != CommunicationType.None) { Log.TraceLog("radio connection to " + other.DebugName + " is " + radioResult); return(radioResult); } // check beacon to radio antenna if (this.m_comp_radio.CanBroadcastPositionTo(other.m_comp_radio) && other.Storage != null) { if (s_sendPositionTo.Add(other.Storage)) { Log.TraceLog("Friendly receiver in range: " + other.DebugName + ", new storage: " + other.Storage.PrimaryNode.DebugName); } else { Log.TraceLog("Friendly receiver in range: " + other.DebugName + ", existing storage: " + other.Storage.PrimaryNode.DebugName); } } } return(CommunicationType.None); }
private bool GetTextPanel(string name, out IMyTextPanel panel) { IMyCubeBlock foundBlock = null; int bestNameLength = int.MaxValue; foreach (IMyCubeBlock Fatblock in AttachedGrid.AttachedCubeBlocks(Grid, AttachedGrid.AttachmentKind.Permanent, true)) { if (Block.canControlBlock(Fatblock)) { string blockName = Fatblock.DisplayNameText.LowerRemoveWhitespace(); if (blockName.Length < bestNameLength && blockName.Contains(name)) { foundBlock = Fatblock; bestNameLength = blockName.Length; if (name.Length == bestNameLength) { break; } } } } panel = foundBlock as IMyTextPanel; return(panel != null); }
/// <returns>true iff the entity should be kept</returns> public static bool collect_Entity(IMyCubeGrid myGrid, MyEntity entity) { if (!(entity is IMyCubeGrid) && !(entity is IMyVoxelMap) && !(entity is IMyFloatingObject)) { return(false); } IMyCubeGrid asGrid = entity as IMyCubeGrid; if (asGrid != null) { if (asGrid == myGrid) { return(false); } if (!asGrid.Save) { return(false); } if (AttachedGrid.IsGridAttached(myGrid, asGrid, AttachedGrid.AttachmentKind.Physics)) { return(false); } } if (entity.Physics != null && entity.Physics.Mass > 0 && entity.Physics.Mass < 1000) { return(false); } return(true); }
public void UpdateTarget(AntennaRelay.LastSeen enemy) { if (enemy == null) { return; } foreach (IMyCubeGrid grid in AttachedGrid.AttachedGrids(m_block.CubeGrid, AttachedGrid.AttachmentKind.Terminal, true)) { CubeGridCache cache = CubeGridCache.GetFor(grid); if (cache == null) { continue; } foreach (IMyCubeBlock warhead in cache.BlocksOfType(typeof(MyObjectBuilder_Warhead))) { if (m_block.canControlBlock(warhead)) { Log.DebugLog("Starting countdown for " + warhead.getBestName(), Logger.severity.DEBUG); warhead.ApplyAction("StartCountdown"); } } } m_countingDown = true; }
private void AllLastSeen(RelayStorage store) { Vector3D myPos = m_block.GetPosition(); store.ForEachLastSeen((LastSeen seen) => { IMyCubeGrid grid = seen.Entity as IMyCubeGrid; if (grid != null && AttachedGrid.IsGridAttached(m_block.CubeGrid, grid, AttachedGrid.AttachmentKind.Physics)) { return; } ExtensionsRelations.Relations relations = m_block.getRelationsTo(seen.Entity, ExtensionsRelations.Relations.Enemy).highestPriority(); m_sortableList.Add(new sortableLastSeen(myPos, seen, relations, m_options)); //Log.DebugLog("item: " + seen.Entity.getBestName() + ", relations: " + relations, "Display()"); }); }
private bool BatteriesCharged(IMyCubeGrid startGrid) { foreach (IMyCubeGrid attachedGrid in AttachedGrid.AttachedGrids(startGrid, AttachedGrid.AttachmentKind.Permanent, true)) { CubeGridCache cache = CubeGridCache.GetFor(attachedGrid); if (cache == null) { return(false); } foreach (IMyBatteryBlock battery in cache.BlocksOfType(typeof(MyObjectBuilder_BatteryBlock))) { if (battery.IsCharging) { return(false); } } } Logger.DebugLog("All batteries are recharged", Logger.severity.DEBUG); return(true); }
private static bool CheckParams(VRage.Game.ModAPI.IMyCubeBlock autopilot, string blockName, string actionName, string[] parameters, out string message, out List<Ingame.TerminalActionParameter> termParams) { int needParams = -1; message = actionName + " not found"; foreach (IMyCubeBlock block in AttachedGrid.AttachedCubeBlocks((IMyCubeGrid)autopilot.CubeGrid, AttachedGrid.AttachmentKind.Permanent, true)) { if (!block.DisplayNameText.Contains(blockName, StringComparison.InvariantCultureIgnoreCase)) continue; IMyTerminalBlock term = block as IMyTerminalBlock; if (term == null) continue; ITerminalAction terminalAction = (ITerminalAction)term.GetActionWithName(actionName); if (terminalAction != null) { int paramCount = terminalAction.GetParameterDefinitions().Count; if (Math.Abs(parameters.Length - paramCount) > (Math.Abs(parameters.Length - needParams))) continue; needParams = paramCount; if (parameters.Length == needParams && CheckParams(terminalAction, out message, out termParams, parameters)) return true; } } if (needParams < 0) { message = actionName + " has no parameters"; termParams = null; return false; } if (parameters.Length != needParams) { message = actionName + " requires " + needParams + " parameters, got " + parameters.Length; termParams = null; return false; } termParams = null; return false; }
private void ListContent(IMyTerminalBlock autopilot, List<MyTerminalControlListBoxItem> items, List<MyTerminalControlListBoxItem> selected) { string blockName = m_targetBlock.ToString().Trim(); if (string.IsNullOrWhiteSpace(blockName)) return; HashSet<ITerminalAction> termActs = new HashSet<ITerminalAction>(); foreach (IMyCubeBlock block in AttachedGrid.AttachedCubeBlocks((IMyCubeGrid)autopilot.CubeGrid, AttachedGrid.AttachmentKind.Permanent, true)) { if (!block.DisplayNameText.Contains(blockName, StringComparison.InvariantCultureIgnoreCase)) continue; IMyTerminalBlock term = block as IMyTerminalBlock; if (term == null) continue; term.GetActions(null, action => { if (termActs.Add((ITerminalAction)action)) items.Add(new MyTerminalControlListBoxItem(MyStringId.GetOrCompute(action.Id), MyStringId.NullOrEmpty, action)); return false; }); } }
private void CalculateInertiaMoment() { Log.DebugLog("recalculating inertia moment", Logger.severity.INFO); m_calcInertiaMoment = Vector3.Zero; foreach (IMySlimBlock slim in (AttachedGrid.AttachedSlimBlocks(myGrid, AttachedGrid.AttachmentKind.Physics, true))) { AddToInertiaMoment(slim); } using (m_lock.AcquireExclusiveUsing()) { m_inertiaMoment = m_calcInertiaMoment; m_invertedInertiaMoment = 1f / m_inertiaMoment; } Log.DebugLog("Inertia moment: " + m_inertiaMoment, Logger.severity.DEBUG); Log.DebugLog("Inverted inertia moment: " + m_invertedInertiaMoment, Logger.severity.DEBUG); m_updating = false; }
/// <summary> /// Find a block that matches m_targetBlockName. /// </summary> private void BlockSearch() { Log.DebugLog("Grid == null", Logger.severity.FATAL, condition: Grid == null); Log.DebugLog("m_targetBlockName == null", Logger.severity.FATAL, condition: m_targetBlockName == null); NextSearch_Block = Globals.UpdateCount + SearchInterval_Block; Block = null; int bestNameLength = int.MaxValue; IMyCubeGrid asGrid = Grid.Entity as IMyCubeGrid; Log.DebugLog("asGrid == null", Logger.severity.FATAL, condition: asGrid == null); foreach (IMyCubeBlock Fatblock in AttachedGrid.AttachedCubeBlocks(asGrid, m_allowedAttachment, true)) { if (!m_controlBlock.CubeBlock.canControlBlock(Fatblock)) { continue; } string blockName = Fatblock.DisplayNameText.LowerRemoveWhitespace(); if (BlockCondition != null && !BlockCondition(Fatblock)) { continue; } //Log.DebugLog("checking block name: \"" + blockName + "\" contains \"" + m_targetBlockName + "\"", "BlockSearch()"); if (blockName.Length < bestNameLength && blockName.Contains(m_targetBlockName)) { Log.DebugLog("block name matches: " + Fatblock.DisplayNameText); Block = Fatblock; bestNameLength = blockName.Length; if (m_targetBlockName.Length == bestNameLength) { return; } } } }
/// <summary> /// Tests if autopilot's ship would endanger a grid with a ship tool. /// </summary> /// <param name="grid">The grid that is potentially endangered.</param> /// <param name="input"><see cref="TestInput"/></param> /// <param name="result"><see cref="GridTestResult"/></param> /// True if the ship would endanger grid. private bool EndangerGrid(MyCubeGrid grid, ref TestInput input, ref GridTestResult result) { Vector3D currentPosition = AutopilotGrid.GetCentre(); Vector3D obstructPositon = grid.GetCentre(); Vector3D rejectD = input.Direction; Vector3D gridPosition = grid.GetCentre() - input.Offset; float gridRadius = grid.PositionComp.LocalVolume.Radius; Vector3 rejectDispF; Vector3.Multiply(ref input.Direction, input.Length, out rejectDispF); Vector3D rejectDisp = rejectDispF; IEnumerable <CubeGridCache> myCaches = AttachedGrid.AttachedGrids(AutopilotGrid, AttachedGrid.AttachmentKind.Terminal, true).Select(CubeGridCache.GetFor); foreach (CubeGridCache cache in myCaches) { if (cache == null) { Logger.DebugLog("Missing a cache", Logger.severity.DEBUG); return(true); } foreach (MyShipDrill drill in cache.BlocksOfType(typeof(MyObjectBuilder_Drill))) { if (drill.IsShooting && ToolObstructed(drill, ref gridPosition, gridRadius, ref rejectDisp, ref result) && IsRejectionTowards(ref obstructPositon, ref currentPosition, ref rejectD)) { return(true); } } foreach (MyShipGrinder grinder in cache.BlocksOfType(typeof(MyObjectBuilder_ShipGrinder))) { if (grinder.IsShooting && ToolObstructed(grinder, ref gridPosition, gridRadius, ref rejectDisp, ref result) && IsRejectionTowards(ref obstructPositon, ref currentPosition, ref rejectD)) { return(true); } } } return(false); }
private void ListContent(IMyTerminalBlock autopilot, List <MyTerminalControlListBoxItem> items, List <MyTerminalControlListBoxItem> selected) { string blockName = m_targetBlock.ToString().Trim(); if (string.IsNullOrWhiteSpace(blockName)) { return; } HashSet <ITerminalProperty <T> > termProps = new HashSet <ITerminalProperty <T> >(); foreach (IMyCubeBlock block in AttachedGrid.AttachedCubeBlocks((IMyCubeGrid)autopilot.CubeGrid, AttachedGrid.AttachmentKind.Permanent, true)) { if (!block.DisplayNameText.Contains(blockName, StringComparison.InvariantCultureIgnoreCase)) { continue; } IMyTerminalBlock term = block as IMyTerminalBlock; if (term == null) { continue; } term.GetProperties(null, property => { ITerminalProperty <T> propT = property as ITerminalProperty <T>; if (propT == null) { return(false); } if (termProps.Add(propT)) { items.Add(new MyTerminalControlListBoxItem(MyStringId.GetOrCompute(propT.Id), MyStringId.NullOrEmpty, propT)); } return(false); }); } }
private bool IsConnected(RelayNode node) { return(!node.Block.Closed && node.Block.IsWorking && m_block.canConsiderFriendly(node.Block) && AttachedGrid.IsGridAttached(m_block.CubeGrid, node.Block.CubeGrid, AttachedGrid.AttachmentKind.Terminal)); }
public void Update10() { if (Globals.ElapsedTime < m_nextHack) { return; } if (!m_hackBlock.IsWorking) { m_strengthLeft = 0f; return; } IMyCubeGrid attached = m_hackBlock.GetAttachedEntity() as IMyCubeGrid; if (attached == null) { m_strengthLeft = 0f; return; } // break force might be removed from game entirely //if (m_hackBlock.BreakForce > allowedBreakForce) //{ // Log.DebugLog("break force too high: " + m_hackBlock.BreakForce); // ITerminalProperty<float> prop = m_hackBlock.GetProperty("BreakForce") as ITerminalProperty<float>; // if (prop == null) // { // Log.DebugLog("break force is disabled in SE", Logger.severity.INFO); // allowedBreakForce = float.PositiveInfinity; // } // else // prop.SetValue(m_hackBlock, allowedBreakForce); //} //if (allowedBreakForce == float.PositiveInfinity) // landing gear is unbreakable, disconnect / fail if not otherwise attached if (!AttachedGrid.IsGridAttached(m_hackBlock.CubeGrid as IMyCubeGrid, attached, AttachedGrid.AttachmentKind.Physics)) { Log.DebugLog("no other connection to attached, hacker must disconnect", Logger.severity.DEBUG); ITerminalProperty <bool> autolock = m_hackBlock.GetProperty("Autolock") as ITerminalProperty <bool>; if (autolock.GetValue(m_hackBlock)) { autolock.SetValue(m_hackBlock, false); } m_hackBlock.GetActionWithName("Unlock").Apply(m_hackBlock); return; } m_nextHack = Globals.ElapsedTime + s_hackFrequency; m_strengthLeft += s_hackStrength; foreach (int i in Enumerable.Range(0, 8).OrderBy(x => Globals.Random.Next())) { Disruption disrupt; switch (i) { case 0: disrupt = new AirVentDepressurize(); break; case 1: disrupt = new DoorLock(); break; case 2: disrupt = new GravityReverse(); break; case 3: disrupt = new DisableTurret(); break; case 4: disrupt = new TraitorTurret(); break; case 5: disrupt = new CryoChamberMurder(); break; case 6: disrupt = new JumpDriveDrain(); break; case 7: disrupt = new MedicalRoom(); break; default: Log.AlwaysLog("Case not implemented: " + i, Logger.severity.FATAL); continue; } foreach (IMyCubeGrid grid in AttachedGrid.AttachedGrids(attached, AttachedGrid.AttachmentKind.Terminal, true)) { disrupt.Start(grid, s_hackLength, ref m_strengthLeft, m_hackBlock.OwnerId); } } }
/// <summary> /// Test if it is safe for the grid to rotate. /// </summary> /// <param name="axis">Normalized axis of rotation in world space.</param> /// <returns>True iff the path is clear.</returns> private bool in_TestRotate(Vector3 axis) { IMyCubeGrid myGrid = m_block.CubeGrid; Vector3 centreOfMass = myGrid.Physics.CenterOfMassWorld; float longestDim = myGrid.GetLongestDim(); // calculate height Matrix toMyLocal = myGrid.WorldMatrixNormalizedInv; Vector3 myLocalCoM = Vector3.Transform(centreOfMass, toMyLocal); Vector3 myLocalAxis = Vector3.Transform(axis, toMyLocal.GetOrientation()); Vector3 myLocalCentre = myGrid.LocalAABB.Center; // CoM may not be on ship (it now considers mass from attached grids) Ray upper = new Ray(myLocalCentre + myLocalAxis * longestDim * 2f, -myLocalAxis); float? upperBound = myGrid.LocalAABB.Intersects(upper); if (!upperBound.HasValue) { Log.AlwaysLog("Math fail, upperBound does not have a value", Logger.severity.FATAL); } Ray lower = new Ray(myLocalCentre - myLocalAxis * longestDim * 2f, myLocalAxis); float?lowerBound = myGrid.LocalAABB.Intersects(lower); if (!lowerBound.HasValue) { Log.AlwaysLog("Math fail, lowerBound does not have a value", Logger.severity.FATAL); } //Log.DebugLog("LocalAABB: " + myGrid.LocalAABB + ", centre: " + myLocalCentre + ", axis: " + myLocalAxis + ", longest dimension: " + longestDim + ", upper ray: " + upper + ", lower ray: " + lower); float height = longestDim * 4f - upperBound.Value - lowerBound.Value; float furthest = 0f; foreach (IMyCubeGrid grid in AttachedGrid.AttachedGrids(myGrid, Attached.AttachedGrid.AttachmentKind.Physics, true)) { CubeGridCache cache = CubeGridCache.GetFor(grid); if (cache == null) { return(false); } foreach (Vector3I cell in cache.OccupiedCells()) { Vector3 rejection = Vector3.Reject(cell * myGrid.GridSize, myLocalAxis); float cellDistSquared = Vector3.DistanceSquared(myLocalCoM, rejection); if (cellDistSquared > furthest) { furthest = cellDistSquared; } } } float length = (float)Math.Sqrt(furthest) + myGrid.GridSize; //Log.DebugLog("height: " + height + ", length: " + length); BoundingSphereD surroundingSphere = new BoundingSphereD(centreOfMass, Math.Max(length, height) * MathHelper.Sqrt2); m_obstructions.Clear(); MyGamePruningStructure.GetAllTopMostEntitiesInSphere(ref surroundingSphere, m_obstructions); LineSegment axisSegment = new LineSegment(); m_closestPlanet = null; foreach (MyEntity entity in m_collector.Invoke(m_obstructions)) { if (entity is IMyVoxelBase) { IMyVoxelMap voxel = entity as IMyVoxelMap; if (voxel != null) { if (voxel.GetIntersectionWithSphere(ref surroundingSphere)) { Log.DebugLog("Too close to " + voxel.getBestName() + ", CoM: " + centreOfMass.ToGpsTag("Centre of Mass") + ", required distance: " + surroundingSphere.Radius); ObstructingEntity = voxel; return(false); } continue; } if (m_closestPlanet == null) { MyPlanet planet = entity as MyPlanet; if (planet == null) { continue; } double distToPlanetSq = Vector3D.DistanceSquared(centreOfMass, planet.PositionComp.GetPosition()); if (distToPlanetSq < planet.MaximumRadius * planet.MaximumRadius) { m_closestPlanet = planet; if (m_planetObstruction) { Log.DebugLog("planet blocking"); ObstructingEntity = m_closestPlanet; return(false); } } } continue; } IMyCubeGrid grid = entity as IMyCubeGrid; if (grid != null) { Matrix toLocal = grid.WorldMatrixNormalizedInv; Vector3 localAxis = Vector3.Transform(axis, toLocal.GetOrientation()); Vector3 localCentre = Vector3.Transform(centreOfMass, toLocal); axisSegment.From = localCentre - localAxis * height; axisSegment.To = localCentre + localAxis * height; CubeGridCache cache = CubeGridCache.GetFor(grid); if (cache == null) { return(false); } foreach (Vector3I cell in cache.OccupiedCells()) { if (axisSegment.PointInCylinder(length, cell * grid.GridSize)) { Log.DebugLog("axis segment: " + axisSegment.From + " to " + axisSegment.To + ", radius: " + length + ", hit " + grid.nameWithId() + " at " + cell); ObstructingEntity = grid; return(false); } } continue; } Log.DebugLog("No tests for object: " + entity.getBestName(), Logger.severity.INFO); ObstructingEntity = entity; return(false); } MyAPIGateway.Utilities.TryInvokeOnGameThread(TestPlanet); ObstructingEntity = null; return(true); }
/// <summary> /// Updates direct connections between this node and other nodes. /// When debug is set, checks connection to primary storage node. /// </summary> public void Update100() { s_sendPositionTo.Clear(); bool checkPrimary = false; Registrar.ForEach((RelayNode node) => { if (node == this) { return; } CommunicationType connToNode = TestConnection(node); if (connToNode == CommunicationType.TwoWay) { if (m_directConnect.Add(node)) { Log.TraceLog("Now connected to " + node.DebugName, Logger.severity.DEBUG); if (this.Storage == null) { if (node.Storage != null) { Log.TraceLog("Using storage from other node: " + node.DebugName + ", primary: " + node.Storage.PrimaryNode.DebugName, Logger.severity.DEBUG); this.Storage = node.Storage; } } else if (node.Storage != null && this.Storage != node.Storage) { // should prefer blocks since they are less likely to be removed from world while offline if (this.Block != null && node.Block == null) { Log.TraceLog("Nodes have different storages, copying to this node's storage because this node is a block", Logger.severity.DEBUG); node.Storage.CopyTo(this.Storage); node.Storage = this.Storage; } else if (this.Block == null && node.Block != null) { Log.TraceLog("Nodes have different storages, copying to other node's storage beacause other node is a block", Logger.severity.DEBUG); this.Storage.CopyTo(node.Storage); this.Storage = node.Storage; } else if (this.Storage.Size < node.Storage.Size) { Log.TraceLog("Nodes have different storages, copying to other node's storage", Logger.severity.DEBUG); this.Storage.CopyTo(node.Storage); this.Storage = node.Storage; } else { Log.TraceLog("Nodes have different storages, copying to this node's storage", Logger.severity.DEBUG); node.Storage.CopyTo(this.Storage); node.Storage = this.Storage; } } } } else { if (m_directConnect.Remove(node)) { Log.TraceLog("No longer connected to " + node.DebugName, Logger.severity.DEBUG); checkPrimary = true; } } if (Storage != null) { if (connToNode == CommunicationType.OneWay) { if (m_oneWayConnect.Add(node)) { Log.TraceLog("New one-way connection to " + node.DebugName, Logger.severity.DEBUG); Storage.AddPushTo(node); } } else { if (m_oneWayConnect.Remove(node)) { Log.TraceLog("Lost one-way connection to " + node.DebugName, Logger.severity.DEBUG); Storage.RemovePushTo(node); } } } }); if (Storage == null) { Log.DebugLog("No storage, creating a new one", Logger.severity.INFO); Storage = new RelayStorage(this); } else if (checkPrimary && !IsConnectedTo(Storage.PrimaryNode)) { Log.DebugLog("Lost connection to primary, cloning storage", Logger.severity.INFO); Storage = Storage.Clone(this); } // connections don't update immediately, so don't worry about a single message (per block) Log.TraceLog("Not connected to primary node", Logger.severity.INFO, condition: !IsConnectedTo(Storage.PrimaryNode)); IMyEntity topEntity = m_entity.GetTopMostParent(); Log.TraceLog("Sending self to " + s_sendPositionTo.Count + " neutral/hostile storages", Logger.severity.TRACE); RelayStorage.Receive(s_sendPositionTo, new LastSeen(topEntity, LastSeen.DetectedBy.Broadcasting)); if (Storage.VeryRecentRadarInfo(topEntity.EntityId)) { return; } if (Block == null) { Storage.Receive(new LastSeen(topEntity, LastSeen.DetectedBy.Broadcasting, new LastSeen.RadarInfo(topEntity))); } else { foreach (IMyCubeGrid grid in AttachedGrid.AttachedGrids(Block.CubeGrid, AttachedGrid.AttachmentKind.Terminal, true)) { Storage.Receive(new LastSeen(grid, LastSeen.DetectedBy.Broadcasting, new LastSeen.RadarInfo(grid))); } } }
/// <summary> /// Tests if the ship is obstructed by any voxel. /// </summary> /// <param name="input"><see cref="TestInput"/></param> /// <param name="result"><see cref="VoxelTestResult"/></param> /// <returns>True iff a voxel is obstructing the ship.</returns> public bool RayCastIntersectsVoxel(ref TestInput input, out VoxelTestResult result) { Profiler.StartProfileBlock(); Logger.DebugLog("direction vector is invalid: " + input.Direction, Logger.severity.FATAL, condition: !input.Direction.IsValid() || Math.Abs(1f - input.Direction.LengthSquared()) > 0.01f); Logger.TraceLog(input.ToString()); if (input.Length < 1f) { // need to skip as Proximity doesn't work with short capsules // should be safe, as the ship got here somehow Logger.TraceLog("Input length is small, no voxel test necessary"); result = VoxelTestResult.Default; result.Distance = input.Length; result.Proximity = 1f; return(false); } Vector3D currentPosition = AutopilotGrid.GetCentre(); Vector3 startOffset; Vector3.Multiply(ref input.Direction, StartRayCast, out startOffset); Vector3D startOffsetD = startOffset; Vector3D totalOffset; Vector3D.Add(ref input.Offset, ref startOffsetD, out totalOffset); CapsuleD capsule; Vector3D.Add(ref currentPosition, ref totalOffset, out capsule.P0); Vector3D capsuleDisp; { capsuleDisp.X = input.Direction.X * input.Length; capsuleDisp.Y = input.Direction.Y * input.Length; capsuleDisp.Z = input.Direction.Z * input.Length; } Vector3D.Add(ref capsule.P0, ref capsuleDisp, out capsule.P1); capsule.Radius = AutopilotGrid.PositionComp.LocalVolume.Radius; //Logger.DebugLog("current position: " + currentPosition + ", offset: " + offset + ", line: " + rayDirectionLength + ", start: " + capsule.P0 + ", end: " + capsule.P1); result = VoxelTestResult.Default; Vector3D hitPosition; float proximity = (float)CapsuleDExtensions.ProximityToVoxel(ref capsule, out result.ObstructingVoxel, out hitPosition, true, input.Length); result.Proximity = 10f * proximity; // lie because we have not done a proper test but could have a very nice result if (proximity > 1f) { Logger.TraceLog("Large capsule DOES NOT intersect voxel: " + capsule.String() + ", proximity: " + proximity + "/" + result.Proximity); result.Distance = input.Length; Profiler.EndProfileBlock(); return(false); } Logger.TraceLog("Large capsule DOES intersect voxel: " + capsule.String() + ", proximity: " + proximity + "/" + result.Proximity); IEnumerable <CubeGridCache> myCaches = AttachedGrid.AttachedGrids(AutopilotGrid, AttachedGrid.AttachmentKind.Physics, true).Select(CubeGridCache.GetFor); Vector3 v; input.Direction.CalculatePerpendicularVector(out v); Vector3 w; Vector3.Cross(ref input.Direction, ref v, out w); Matrix to3D = new Matrix(v.X, v.Y, v.Z, 0f, w.X, w.Y, w.Z, 0f, input.Direction.X, input.Direction.Y, input.Direction.Z, 0f, 0f, 0f, 0f, 1f); Matrix to2D; Matrix.Invert(ref to3D, out to2D); Vector2IMatrix <Vector3D> apShipRejections; ResourcePool.Get(out apShipRejections); MatrixD worldMatrix = AutopilotGrid.WorldMatrix; float gridSize = AutopilotGrid.GridSize; foreach (CubeGridCache cache in myCaches) { if (cache == null) { Logger.DebugLog("Missing a cache", Logger.severity.DEBUG); Profiler.EndProfileBlock(); return(false); } foreach (Vector3I cell in cache.OccupiedCells()) { Vector3 local = cell * gridSize; Vector3D world; Vector3D.Transform(ref local, ref worldMatrix, out world); Vector3D offsetWorld; Vector3D.Add(ref world, ref totalOffset, out offsetWorld); Vector3D relative; Vector3D.Subtract(ref offsetWorld, ref currentPosition, out relative); Vector3 relativeF = relative; Vector3 rejection; Vector3.Reject(ref relativeF, ref input.Direction, out rejection); Vector3 planarComponents; Vector3.Transform(ref rejection, ref to2D, out planarComponents); Logger.DebugLog("Math fail: rejection: " + rejection + ", planar components: " + planarComponents + "\nto3D: " + to3D, Logger.severity.FATAL, condition: planarComponents.Z > 0.001f || planarComponents.Z < -0.001f); Vector2 pc2 = new Vector2(planarComponents.X, planarComponents.Y); apShipRejections.Add(ToCell(pc2, gridSize), offsetWorld); } } Vector2IMatrix <bool> testedRejections; ResourcePool.Get(out testedRejections); const int allowedEmpty = 2; foreach (KeyValuePair <Vector2I, Vector3D> cell in apShipRejections.MiddleOut()) { //Logger.DebugLog("Cell was not set: " + cell, Logger.severity.FATAL, condition: cell.Value == Vector3D.Zero); if (!testedRejections.Add(cell.Key, true)) { continue; } int ringIndex = 0; m_insideRing.Clear(); int biggestRingSq = 0; while (true) { int consecutiveEmpty = 0; ExpandingRings.Ring ring = ExpandingRings.GetRing(ringIndex++); foreach (Vector2I ringOffset in ring.Squares) { if (apShipRejections.Contains(ringOffset + cell.Key)) { consecutiveEmpty = 0; } else { consecutiveEmpty++; if (consecutiveEmpty > allowedEmpty) { goto GotRing; } } } m_insideRing.AddArray(ring.Squares); biggestRingSq = ring.DistanceSquared; } GotRing: foreach (Vector2I ringOffset in m_insideRing) { testedRejections.Add(ringOffset + cell.Key, true); } capsule.P0 = cell.Value; Vector3D.Add(ref capsule.P0, ref capsuleDisp, out capsule.P1); capsule.Radius = (1f + (float)Math.Sqrt(biggestRingSq)) * gridSize; result.Proximity = (float)CapsuleDExtensions.ProximityToVoxel(ref capsule, out result.ObstructingVoxel, out hitPosition, true, input.Length); if (result.Proximity <= 1f) { Logger.TraceLog("Block capsule does hit voxel: " + capsule.String() + ", proxmity: " + result.Proximity); double distance; Vector3D.Distance(ref capsule.P0, ref hitPosition, out distance); result.Distance = (float)distance; apShipRejections.Clear(); testedRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(testedRejections); Profiler.EndProfileBlock(); return(true); } } Logger.TraceLog("Ship's path is clear from voxels, proximity: " + result.Proximity); apShipRejections.Clear(); testedRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(testedRejections); Profiler.EndProfileBlock(); return(false); }
/// <summary> /// Tests a grid for obstructing the ship via vector rejection. /// </summary> /// <param name="oGrid">The grid that may obstruct this one.</param> /// <param name="ignoreBlock">Block to ignore, or null</param> /// <param name="input"><see cref="TestInput"/></param> /// <param name="result"><see cref="GridTestResult"/></param> /// <returns>True if oGrid is blocking the ship.</returns> private bool RejectionIntersects(MyCubeGrid oGrid, MyCubeBlock ignoreBlock, ref TestInput input, ref GridTestResult result) { //Logger.DebugLog("Rejection vector is not normalized, length squared: " + rejectionVector.LengthSquared(), Logger.severity.FATAL, condition: Math.Abs(rejectionVector.LengthSquared() - 1f) > 0.001f); //Logger.DebugLog("Testing for rejection intersection: " + oGrid.nameWithId() + ", starting from: " + (AutopilotGrid.GetCentre() + offset) + ", rejection vector: " + rejectionVector + ", distance: " + rejectionDistance + // ", final: " + (AutopilotGrid.GetCentre() + offset + rejectionVector * rejectionDistance)); //Logger.DebugLog("rejction distance < 0: " + rejectionDistance, Logger.severity.ERROR, condition: rejectionDistance < 0f); IEnumerable <CubeGridCache> myCaches = AttachedGrid.AttachedGrids(AutopilotGrid, AttachedGrid.AttachmentKind.Physics, true).Select(CubeGridCache.GetFor); Vector3D currentPosition = AutopilotGrid.GetCentre(); CubeGridCache oCache = CubeGridCache.GetFor(oGrid); if (oCache == null) { Logger.DebugLog("Failed to get cache for other grid", Logger.severity.DEBUG); return(false); } bool checkBlock = ignoreBlock != null && oGrid == ignoreBlock.CubeGrid; Vector3 v; input.Direction.CalculatePerpendicularVector(out v); Vector3 w; Vector3.Cross(ref input.Direction, ref v, out w); Matrix to3D = new Matrix(v.X, v.Y, v.Z, 0f, w.X, w.Y, w.Z, 0f, input.Direction.X, input.Direction.Y, input.Direction.Z, 0f, 0f, 0f, 0f, 1f); Matrix to2D; Matrix.Invert(ref to3D, out to2D); float roundTo; int minDistanceSquared; if (AutopilotGrid.GridSizeEnum == oGrid.GridSizeEnum) { roundTo = AutopilotGrid.GridSize; minDistanceSquared = 1; } else { roundTo = Math.Min(AutopilotGrid.GridSize, oGrid.GridSize); minDistanceSquared = (int)Math.Ceiling(Math.Max(AutopilotGrid.GridSize, oGrid.GridSize) / roundTo); minDistanceSquared *= minDistanceSquared; } int maxDistanceSquared = minDistanceSquared * 100; Profiler.StartProfileBlock("RejectionIntersects:Build ship"); Vector2IMatrix <bool> apShipRejections; ResourcePool.Get(out apShipRejections); MatrixD worldMatrix = AutopilotGrid.WorldMatrix; float gridSize = AutopilotGrid.GridSize; float minProjection = float.MaxValue, maxProjection = float.MinValue; // the permitted range when rejecting the other grids cells foreach (CubeGridCache cache in myCaches) { if (cache == null) { Logger.DebugLog("Missing a cache", Logger.severity.DEBUG); Profiler.EndProfileBlock(); return(false); } foreach (Vector3I cell in cache.OccupiedCells()) { Vector3 local = cell * gridSize; MyCubeBlock block = (MyCubeBlock)cache.CubeGrid.GetCubeBlock(cell)?.FatBlock; if (block != null && block.Subparts != null && block.Subparts.Count != 0 && !CellOccupiedByBlock(cell, block)) { continue; } Vector3D world; Vector3D.Transform(ref local, ref worldMatrix, out world); Vector3D relative; Vector3D.Subtract(ref world, ref currentPosition, out relative); Vector3 relativeF = relative; float projectionDistance; Vector3 rejection; VectorExtensions.RejectNormalized(ref relativeF, ref input.Direction, out projectionDistance, out rejection); if (projectionDistance < minProjection) { minProjection = projectionDistance; } else if (projectionDistance > maxProjection) { maxProjection = projectionDistance; } Vector3 planarComponents; Vector3.Transform(ref rejection, ref to2D, out planarComponents); //Logger.DebugLog("Math fail: rejection: " + rejection + ", planar components: " + planarComponents + "\nto3D: " + to3D, Logger.severity.WARNING, condition: planarComponents.Z > 0.001f || planarComponents.Z < -0.001f); Vector2 pc2 = new Vector2(planarComponents.X, planarComponents.Y); apShipRejections[ToCell(pc2, roundTo)] = true; //Logger.DebugLog("My rejection: " + rejection + ", planar: " + ToCell(pc2, roundTo)); } } Profiler.EndProfileBlock(); minProjection += StartRayCast; // allow autopilot to move away from a touching object //Logger.DebugLog("projection min: " + minProjection + ", max: " + maxProjection + ", max for other: " + (maxProjection + rejectionDistance)); maxProjection += input.Length; //Logger.DebugLog("checking other grid cells"); Profiler.StartProfileBlock("RejectionIntersects:other grid"); Vector2IMatrix <bool> otherGridRejections; ResourcePool.Get(out otherGridRejections); worldMatrix = oGrid.WorldMatrix; gridSize = oGrid.GridSize; foreach (Vector3I cell in oCache.OccupiedCells()) { //Logger.DebugLog("cell: " + cell); Vector3 local = cell * gridSize; Vector3D world; Vector3D.Transform(ref local, ref worldMatrix, out world); Vector3D offsetWorld; Vector3D.Subtract(ref world, ref input.Offset, out offsetWorld); Vector3D relative; Vector3D.Subtract(ref offsetWorld, ref currentPosition, out relative); Vector3 relativeF = relative; Vector3 rejection; VectorExtensions.RejectNormalized(ref relativeF, ref input.Direction, out result.Distance, out rejection); if (result.Distance < minProjection || result.Distance > maxProjection) { continue; } Vector3 planarComponents; Vector3.Transform(ref rejection, ref to2D, out planarComponents); //Logger.DebugLog("Math fail: rejection: " + rejection + ", planar components: " + planarComponents + "\nto3D: " + to3D, Logger.severity.WARNING, condition: planarComponents.Z > 0.001f || planarComponents.Z < -0.001f); Vector2 pc2 = new Vector2(planarComponents.X, planarComponents.Y); Vector2I cell2D = ToCell(pc2, roundTo); if (!otherGridRejections.Add(cell2D, true)) { //Logger.DebugLog("Already tested: " + cell2D); continue; } //Logger.DebugLog("Rejection: " + rejection + ", planar: " + cell2D); //Logger.DebugLog("testing range. x: " + (cell2D.X - steps) + " - " + (cell2D.X + steps)); ExpandingRings.Ring ring = default(ExpandingRings.Ring); for (int ringIndex = 0; ring.DistanceSquared <= maxDistanceSquared; ringIndex++) { ring = ExpandingRings.GetRing(ringIndex); for (int squareIndex = 0; squareIndex < ring.Squares.Length; squareIndex++) { if (apShipRejections.Contains(cell2D + ring.Squares[squareIndex])) { if (ring.DistanceSquared <= minDistanceSquared) { IMySlimBlock slim = oGrid.GetCubeBlock(cell); if (slim != null) { if (checkBlock && slim.FatBlock == ignoreBlock) { continue; } MyCubeBlock fat = (MyCubeBlock)slim.FatBlock; if (fat != null && fat.Subparts != null && fat.Subparts.Count != 0 && !CellOccupiedByBlock(cell, fat)) { continue; } result.ObstructingBlock = fat; } else { result.ObstructingBlock = null; } result.Proximity = 0f; Logger.DebugLog("Hit, projectionDistance: " + result.Distance + ", min: " + minProjection + ", max: " + maxProjection + ", ring: " + ringIndex + ", ring dist sq: " + ring.DistanceSquared + ", min dist sq: " + minDistanceSquared + ", max dist sq: " + maxDistanceSquared); Profiler.EndProfileBlock(); apShipRejections.Clear(); otherGridRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(otherGridRejections); return(true); } else { maxDistanceSquared = ring.DistanceSquared; goto NextCell; } } } } NextCell :; } Profiler.EndProfileBlock(); apShipRejections.Clear(); otherGridRejections.Clear(); ResourcePool.Return(apShipRejections); ResourcePool.Return(otherGridRejections); result.Proximity = (float)Math.Sqrt(maxDistanceSquared); return(false); }