/// <summary> /// Positions the provided elements randomly inside a sphere surrounding the HQ element /// in such a way that the CollisionAvoidanceZones are not in contact. /// </summary> /// <param name="radius">The radius of the sphere in units.</param> /// <param name="hqElement">The hq element.</param> /// <param name="elementsToPositionAroundHQ">The non-HQ elements to position.</param> /// <returns> /// <c>true</c> if all elements were successfully positioned without overlap. /// </returns> private bool TryPositionRandomWithinSphere(float radius, AUnitElementItem hqElement, AUnitElementItem[] elementsToPositionAroundHQ) { IList<ElementSphere> allElementSpheres = new List<ElementSphere>(); ElementSphere hqElementSphere = new ElementSphere(hqElement); allElementSpheres.Add(hqElementSphere); int iterateCount = 0; Vector3[] formationStationOffsets = new Vector3[elementsToPositionAroundHQ.Length]; for (int i = 0; i < elementsToPositionAroundHQ.Length; i++) { Vector3 candidateStationOffset = UnityEngine.Random.insideUnitSphere * radius; AUnitElementItem elementCandidate = elementsToPositionAroundHQ[i]; ElementSphere elementCandidateSphere = new ElementSphere(elementCandidate); elementCandidateSphere.Center = candidateStationOffset; if (allElementSpheres.All(es => !Intersects(es, elementCandidateSphere))) { // candidate doesn't intersect with any spheres already present allElementSpheres.Add(elementCandidateSphere); formationStationOffsets[i] = candidateStationOffset; iterateCount = Constants.Zero; } else { i--; iterateCount++; if (iterateCount >= 10) { // HACK D.Warn("{0}.{1} had a positioning iteration error.", _unitCmd.FullName, GetType().Name); return false; } } } _unitCmd.PositionElementInFormation(hqElement, Vector3.zero); for (int i = 0; i < elementsToPositionAroundHQ.Length; i++) { _unitCmd.PositionElementInFormation(elementsToPositionAroundHQ[i], formationStationOffsets[i]); //elementsToPosition[i].transform.localPosition = localFormationPositions[i]; // won't work as the position of the Element's parent is arbitrary } return true; }
public ElementSphere(AUnitElementItem element) { Center = element.Position; Radius = element.KeepoutZoneRadius; }
/// <summary> /// Removes the element from the Unit. /// <remarks>4.19.16 Just discovered I still had asserts in place that require that the Base's HQElement die last, /// a holdover from when Bases distributed damage to protect the HQ until last. I'm allowing Bases to change their /// HQElement if it dies now until I determine how I want Base.HQELements to operate game play wise.</remarks> /// </summary> /// <param name="element">The element.</param> public override void RemoveElement(AUnitElementItem element) { base.RemoveElement(element); if (!IsOperational) { // BaseCmd has died return; } var facility = element as FacilityItem; if (facility == HQElement) { // HQ Element has been removed HQElement = SelectHQElement(); } }
protected void Idling_UponSubordinateElementDeath(AUnitElementItem deadSubordinateElement) { LogEvent(); }
protected virtual void HandleHQElementChanging(AUnitElementItem newHQElement) { Utility.ValidateNotNull(newHQElement); var previousHQElement = HQElement; if (previousHQElement != null) { previousHQElement.IsHQ = false; // don't remove previousHQElement.ShowDebugLog if ShowHQDebugLog as its probably dieing } else { // first assignment of HQ D.Assert(!IsOperational); // OPTIMIZE Just a FYI warning as formations currently assume this if (newHQElement.transform.rotation != Quaternion.identity) { D.Warn("{0} first HQ Element rotation = {1}.", DebugName, newHQElement.transform.rotation); } } if (!Elements.Contains(newHQElement)) { // the player will typically select/change the HQ element of a Unit from the elements already present in the unit D.Warn("{0} assigned HQElement {1} that is not already present in Unit.", DebugName, newHQElement.DebugName); AddElement(newHQElement); } }
private void UponSubordinateElementDeath(AUnitElementItem deadSubordinateElement) { RelayToCurrentState(deadSubordinateElement); }
public virtual void RemoveElement(AUnitElementItem element) { bool isRemoved = Elements.Remove(element); D.Assert(isRemoved, element.DebugName); Data.RemoveElement(element.Data); DetachSensorsFromMonitors(element.Data.Sensors); if (!IsOperational) { return; // avoid the following work if removing during startup } if (Elements.Count == Constants.Zero) { if (Data.UnitHealth > Constants.ZeroF) { D.Error("{0} has UnitHealth of {1:0.0000} remaining.", DebugName, Data.UnitHealth); } IsOperational = false; // tell Cmd its dead return; } AssessIcon(); FormationMgr.HandleElementRemoval(element); }
private void HQElementPropChangingHandler(AUnitElementItem newHQElement) { HandleHQElementChanging(newHQElement); }
/// <summary> /// Adds the Element to this Command including parenting if needed. /// </summary> /// <param name="element">The Element to add.</param> public virtual void AddElement(AUnitElementItem element) { if (Elements.Contains(element)) { D.Error("{0} attempting to add {1} that is already present.", DebugName, element.DebugName); } if (element.IsHQ) { D.Error("{0} adding element {1} already designated as the HQ Element.", DebugName, element.DebugName); } if (element.IsOperational != IsOperational) { D.Error("{0}: Adding element {1} with incorrect IsOperational state.", DebugName, element.DebugName); } Elements.Add(element); Data.AddElement(element.Data); element.Command = this; element.AttachAsChildOf(UnitContainer); //TODO consider changing HQElement var unattachedSensors = element.Data.Sensors.Where(sensor => sensor.RangeMonitor == null); if (unattachedSensors.Any()) { //D.Log(ShowDebugLog, "{0} is attaching {1}'s sensors: {2}.", DebugName, element.DebugName, unattachedSensors.Select(s => s.Name).Concatenate()); AttachSensorsToMonitors(unattachedSensors.ToList()); // WARNING: IEnumerable<T> lazy evaluation GOTCHA! The IEnumerable unattachedSensors at this point (after AttachSensorsToMonitors // completes) no longer points to any sensors as they would now all be attached. This is because the IEnumerable is not an // already constructed collection. It is a pointer to a sensor that when called evaluates the sensor against the criteria. // Since the Attach method modifies the sensor, the sensor being evaluated will no longer meet the unattached criteria, // so unattachedSensors will no longer point to anything. Using ToList() to feed the method delivers a fully evaluated // collection to the method, but this doesn't change the fact that Sensors now has no sensors that are unattached, thus // the unattachedSensors IEnumerable when used again no longer points to anything. } if (!IsOperational) { // avoid the following extra work if adding during Cmd construction D.AssertNull(HQElement); // During Cmd construction, HQElement will be designated AFTER all Elements are return; // added resulting in _formationMgr adding all elements into the formation at once } AssessIcon(); FormationMgr.AddAndPositionNonHQElement(element); }
void AssumingFormation_UponSubordinateElementDeath(AUnitElementItem deadSubordinateElement) { LogEvent(); }
protected override void HandleHQElementChanging(AUnitElementItem newHQElement) { base.HandleHQElementChanging(newHQElement); _navigator.HandleHQElementChanging(HQElement, newHQElement as ShipItem); }
void ExecuteAttackOrder_UponSubordinateElementDeath(AUnitElementItem deadSubordinateElement) { LogEvent(); }
public override void RemoveElement(AUnitElementItem element) { base.RemoveElement(element); var ship = element as ShipItem; // Remove FS so the GC can clean it up. Also, if joining another fleet, the joined fleet will find it null // when adding the ship and therefore make a new FS with the proper reference to the joined fleet ship.FormationStation = null; if (!IsOperational) { // fleetCmd has died return; } if (ship == HQElement) { // HQ Element has been removed HQElement = SelectHQElement(); } }