/// <summary> /// Plots a course and notifies the requester of the outcome via the onCoursePlotCompleted event if set. /// </summary> /// <param name="target">The target.</param> /// <param name="speed">The speed.</param> public void PlotCourse(INavigableTarget target, Speed speed) { Target = target; Speed = speed; D.Assert(speed != Speed.Stop, "{0} designated speed to new target {1} is 0!".Inject(_fleet.FullName, target.FullName)); InitializeTargetValues(); InitializeReplotValues(); GenerateCourse(); }
/// <summary> /// Checks for an obstacle enroute to the designated <c>navTarget</c>. Returns true if one /// is found and provides the detour around it. /// </summary> /// <param name="destination">The current destination.</param> /// <param name="destinationCastingKeepoutRadius">The distance around the destination to avoid casting into.</param> /// <param name="detour">The obstacle detour.</param> /// <param name="obstacleHitDistance">The obstacle hit distance.</param> /// <returns> /// <c>true</c> if an obstacle was found, false if the way is clear. /// </returns> protected bool TryCheckForObstacleEnrouteTo(INavigableTarget destination, float destinationCastingKeepoutRadius, out INavigableTarget detour, out float obstacleHitDistance) { detour = null; obstacleHitDistance = Mathf.Infinity; Vector3 vectorToDestination = destination.Position - Position; float destinationDistance = vectorToDestination.magnitude; if (destinationDistance <= destinationCastingKeepoutRadius) { return false; } Vector3 destinationBearing = vectorToDestination.normalized; float rayLength = destinationDistance - destinationCastingKeepoutRadius; Ray entryRay = new Ray(Position, destinationBearing); RaycastHit entryHit; if (Physics.Raycast(entryRay, out entryHit, rayLength, _keepoutOnlyLayerMask.value)) { // there is a keepout zone obstacle in the way var obstacle = entryHit.transform; string obstacleName = obstacle.parent.name + "." + obstacle.name; obstacleHitDistance = entryHit.distance; D.Log("{0} encountered obstacle {1} centered at {2} when checking approach to {3}. \nRay length = {4:0.#}, DistanceToHit = {5:0.#}.", Name, obstacleName, obstacle.position, destination.FullName, rayLength, obstacleHitDistance); detour = GenerateDetourAroundObstacle(entryRay, entryHit); INavigableTarget newDetour; float newObstacleHitDistance; if (TryCheckForObstacleEnrouteTo(detour, 0F, out newDetour, out newObstacleHitDistance)) { D.Warn("{0} found another obstacle on the way to detour {1}.", Name, detour.FullName); detour = newDetour; obstacleHitDistance = newObstacleHitDistance; } return true; } return false; }
/// <summary> /// Refreshes the course. /// </summary> /// <param name="mode">The mode.</param> /// <param name="waypoint">The waypoint.</param> protected abstract void RefreshCourse(CourseRefreshMode mode, INavigableTarget waypoint = null);
void Moving_ExitState() { LogEvent(); var mortalMoveTarget = _moveTarget as IMortalTarget; if (mortalMoveTarget != null) { mortalMoveTarget.onTargetDeathOneShot -= OnTargetDeath; } _moveTarget = null; _navigator.DisengageAutoPilot(); }
/// <summary> /// Plots the course to the target and notifies the requester of the outcome via the onCoursePlotSuccess or Failure events. /// </summary> /// <param name="target">The target.</param> /// <param name="travelSpeed">The speed to travel at.</param> public virtual void PlotCourse(INavigableTarget target, Speed travelSpeed, OrderSource orderSource) { D.Assert(travelSpeed != default(Speed) && travelSpeed != Speed.Stop && travelSpeed != Speed.EmergencyStop, "{0} speed of {1} is illegal.".Inject(Name, travelSpeed.GetValueName())); Target = target; TravelSpeed = travelSpeed; _orderSource = orderSource; }
/// <summary> /// Checks to see if any System entry or exit points need to be set. If it is determined an entry or exit /// point is needed, the appropriate point will be set to minimize the amount of InSystem travel time req'd to reach the /// target and the method will return true. These points will then be inserted into the course that is plotted by GenerateCourse(); /// </summary> /// <param name="target">The target.</param> /// <param name="fleetSystemExitPt">The fleet system exit pt.</param> /// <param name="targetSystemEntryPt">The target system entry pt.</param> /// <returns></returns> private bool TryCheckForSystemAccessPoints(INavigableTarget target, out Vector3 fleetSystemExitPt, out Vector3 targetSystemEntryPt) { targetSystemEntryPt = Vector3.zero; fleetSystemExitPt = Vector3.zero; SystemModel fleetSystem = null; SystemModel targetSystem = null; //D.Log("{0}.Topography = {1}.", _fleet.FullName, _fleet.Topography); if (_fleet.Topography == Topography.System) { var fleetSectorIndex = SectorGrid.GetSectorIndex(_fleet.Position); D.Assert(SystemCreator.TryGetSystem(fleetSectorIndex, out fleetSystem)); // error if a system isn't found D.Assert(Vector3.SqrMagnitude(fleetSystem.Position - _fleet.Position) <= fleetSystem.Radius * fleetSystem.Radius); D.Log("{0} is plotting a course from within System {1}.", _fleet.FullName, fleetSystem.FullName); } //D.Log("{0}.Topography = {1}.", target.FullName, target.Topography); if (target.Topography == Topography.System) { var targetSectorIndex = SectorGrid.GetSectorIndex(target.Position); D.Assert(SystemCreator.TryGetSystem(targetSectorIndex, out targetSystem)); // error if a system isn't found D.Assert(Vector3.SqrMagnitude(targetSystem.Position - target.Position) <= targetSystem.Radius * targetSystem.Radius); D.Log("{0}'s target {1} is contained within System {2}.", _fleet.FullName, target.FullName, targetSystem.FullName); } var result = false; if (fleetSystem != null) { if (fleetSystem == targetSystem) { // the target and fleet are in the same system so exit and entry points aren't needed D.Log("{0} start and destination {1} is contained within System {2}.", _fleet.FullName, target.FullName, fleetSystem.FullName); return result; } fleetSystemExitPt = UnityUtility.FindClosestPointOnSphereTo(_fleet.Position, fleetSystem.Position, fleetSystem.Radius); result = true; } if (targetSystem != null) { targetSystemEntryPt = UnityUtility.FindClosestPointOnSphereTo(target.Position, targetSystem.Position, targetSystem.Radius); result = true; } return result; }
public void __IssueShipMovementOrders(INavigableTarget target, Speed speed) { var shipMoveToOrder = new ShipOrder(ShipDirective.Move, OrderSource.UnitCommand, target, speed); Elements.ForAll(e => (e as ShipModel).CurrentOrder = shipMoveToOrder); }
IEnumerator ExecuteJoinFleetOrder_EnterState() { D.Log("{0}.ExecuteJoinFleetOrder_EnterState called.", FullName); _moveTarget = CurrentOrder.Target; D.Assert(CurrentOrder.Speed == Speed.None, "{0}.JoinFleetOrder has speed set to {1}.".Inject(FullName, CurrentOrder.Speed.GetValueName())); _moveSpeed = Speed.FleetStandard; Call(FleetState.Moving); yield return null; // required immediately after Call() to avoid FSM bug if (_isDestinationUnreachable) { CurrentState = FleetState.Idling; yield break; } // we've arrived so transfer the ship to the fleet we are joining var fleetToJoin = CurrentOrder.Target as FleetCmdModel; var ship = Elements[0] as ShipModel; // IMPROVE more than one ship? TransferShip(ship, fleetToJoin); // removing the only ship will immediately call FleetState.Dead }
/// <summary> /// Plots the course to the target and notifies the requester of the outcome via the onCoursePlotSuccess or Failure events. /// </summary> /// <param name="target">The target.</param> /// <param name="speed">The speed.</param> public void PlotCourse(INavigableTarget target, Speed speed) { D.Assert(speed != default(Speed) && speed != Speed.Stop, "{0} speed of {1} is illegal.".Inject(_fleet.FullName, speed.GetValueName())); TryCheckForSystemAccessPoints(target, out _fleetSystemExitPoint, out _targetSystemEntryPoint); Target = target; FleetSpeed = speed; AssessFrequencyOfCourseProgressChecks(); InitializeReplotValues(); GenerateCourse(); }
/// <summary> /// Plots the course to the target and notifies the requester of the /// outcome via the onCoursePlotCompleted event. /// </summary> /// <param name="target">The target.</param> /// <param name="speed">The speed.</param> /// <param name="standoffDistance">The distance to standoff from the target. When appropriate, this is added to the radius of the target to /// determine how close the ship is allowed to approach.</param> /// <param name="isFleetMove">if set to <c>true</c> the ship will only move when the fleet is ready.</param> public void PlotCourse(INavigableTarget target, Speed speed, float standoffDistance, bool isFleetMove) { if (target is IFormationStation) { PlotCourse(target as IFormationStation, speed); } else if (target is StationaryLocation) { PlotCourse(target as StationaryLocation, speed, isFleetMove); } else if (target is ICmdTarget) { PlotCourse(target as ICmdTarget, speed, standoffDistance); } else if (target is IElementAttackableTarget) { PlotCourse(target as IElementAttackableTarget, speed, standoffDistance); } else if (target is IMortalTarget) { D.Assert(target is IPlanetoidModel); PlotCourse(target as IMortalTarget, speed, standoffDistance, isFleetMove); } else { D.Error("{0} of Type {1} not anticipated.", target.FullName, target.GetType().Name); } }
IEnumerator ExecuteAttackOrder_EnterState() { D.Log("{0}.ExecuteAttackOrder_EnterState called. Target = {1}.", FullName, CurrentOrder.Target.FullName); _moveTarget = CurrentOrder.Target; _moveSpeed = Speed.FleetFull; Call(FleetState.Moving); yield return null; // required immediately after Call() to avoid FSM bug if (_isDestinationUnreachable) { CurrentState = FleetState.Idling; yield break; } if (!(CurrentOrder.Target as IMortalTarget).IsAlive) { // Moving Return()s if the target dies CurrentState = FleetState.Idling; yield break; } Call(FleetState.Attacking); yield return null; // required immediately after Call() to avoid FSM bug CurrentState = FleetState.Idling; }
/// <summary> /// Plots the course to the target and notifies the requester of the outcome via the onCoursePlotSuccess or Failure events. /// </summary> /// <param name="target">The target.</param> /// <param name="speed">The speed.</param> /// <param name="orderSource">The source of this move order.</param> public void PlotCourse(INavigableTarget target, Speed speed, OrderSource orderSource) { D.Assert(speed != default(Speed) && speed != Speed.Stop, "{0} speed of {1} is illegal.".Inject(_ship.FullName, speed.GetValueName())); // NOTE: I know of no way to check whether a target is unreachable at this stage since many targets move, // and most have a closeEnoughDistance that makes them reachable even when enclosed in a keepoutZone if (target is IFormationStation) { D.Assert(orderSource == OrderSource.ElementCaptain); DestinationInfo = new ShipDestinationInfo(target as IFormationStation); } else if (target is SectorModel) { Vector3 destinationOffset = orderSource == OrderSource.UnitCommand ? _ship.Data.FormationStation.StationOffset : Vector3.zero; DestinationInfo = new ShipDestinationInfo(target as SectorModel, destinationOffset); } else if (target is StationaryLocation) { Vector3 destinationOffset = orderSource == OrderSource.UnitCommand ? _ship.Data.FormationStation.StationOffset : Vector3.zero; var autoPilotSpeedReference = new Reference<float>(() => _autoPilotSpeedInUnitsPerHour); DestinationInfo = new ShipDestinationInfo((StationaryLocation)target, destinationOffset, autoPilotSpeedReference); } else if (target is FleetCmdModel) { D.Assert(orderSource == OrderSource.UnitCommand); var fleetTarget = target as FleetCmdModel; bool isEnemy = _ship.Owner.IsEnemyOf(fleetTarget.Owner); DestinationInfo = new ShipDestinationInfo(fleetTarget, _ship.Data.FormationStation.StationOffset, isEnemy); } else if (target is AUnitBaseCmdModel) { D.Assert(orderSource == OrderSource.UnitCommand); var baseTarget = target as AUnitBaseCmdModel; bool isEnemy = _ship.Owner.IsEnemyOf(baseTarget.Owner); DestinationInfo = new ShipDestinationInfo(baseTarget, _ship.Data.FormationStation.StationOffset, isEnemy); } else if (target is FacilityModel) { D.Assert(orderSource == OrderSource.ElementCaptain); var facilityTarget = target as FacilityModel; bool isEnemy = _ship.Owner.IsEnemyOf(facilityTarget.Owner); DestinationInfo = new ShipDestinationInfo(facilityTarget, isEnemy); } else if (target is ShipModel) { D.Assert(orderSource == OrderSource.ElementCaptain); var shipTarget = target as ShipModel; bool isEnemy = _ship.Owner.IsEnemyOf(shipTarget.Owner); DestinationInfo = new ShipDestinationInfo(shipTarget, isEnemy); } else if (target is APlanetoidModel) { Vector3 destinationOffset = orderSource == OrderSource.UnitCommand ? _ship.Data.FormationStation.StationOffset : Vector3.zero; DestinationInfo = new ShipDestinationInfo(target as APlanetoidModel, destinationOffset); } else if (target is SystemModel) { Vector3 destinationOffset = orderSource == OrderSource.UnitCommand ? _ship.Data.FormationStation.StationOffset : Vector3.zero; DestinationInfo = new ShipDestinationInfo(target as SystemModel, destinationOffset); } else if (target is StarModel) { Vector3 destinationOffset = orderSource == OrderSource.UnitCommand ? _ship.Data.FormationStation.StationOffset : Vector3.zero; DestinationInfo = new ShipDestinationInfo(target as StarModel, destinationOffset); } else if (target is UniverseCenterModel) { Vector3 destinationOffset = orderSource == OrderSource.UnitCommand ? _ship.Data.FormationStation.StationOffset : Vector3.zero; DestinationInfo = new ShipDestinationInfo(target as UniverseCenterModel, destinationOffset); } else { D.Error("{0} of Type {1} not anticipated.", target.FullName, target.GetType().Name); return; } OrderSource = orderSource; AutoPilotSpeed = speed; RefreshNavigationalValues(); OnCoursePlotSuccess(); }
IEnumerator ExecuteRepairOrder_EnterState() { D.Log("{0}.ExecuteRepairOrder_EnterState called.", FullName); TryBreakOrbit(); _moveSpeed = Speed.Full; _moveTarget = CurrentOrder.Target; _orderSource = OrderSource.ElementCaptain; // UNCLEAR what if the fleet issued the fleet-wide repair order? Call(ShipState.Moving); yield return null; // required immediately after Call() to avoid FSM bug // Return()s here if (_isDestinationUnreachable) { //TODO how to handle move errors? CurrentState = ShipState.Idling; yield break; } if (AssessWhetherToAssumeOrbit()) { Call(ShipState.AssumingOrbit); yield return null; // required immediately after Call() to avoid FSM bug // Return()s here } Call(ShipState.Repairing); yield return null; // required immediately after Call() to avoid FSM bug CurrentState = ShipState.Idling; }
private IMortalTarget _primaryTarget; // IMPROVE take this previous target into account when PickPrimaryTarget() IEnumerator ExecuteAttackOrder_EnterState() { D.Log("{0}.ExecuteAttackOrder_EnterState() called.", FullName); TryBreakOrbit(); _ordersTarget = CurrentOrder.Target as IMortalTarget; while (_ordersTarget.IsAlive) { // once picked, _primaryTarget cannot be null when _ordersTarget is alive bool inRange = PickPrimaryTarget(out _primaryTarget); if (inRange) { D.Assert(_primaryTarget != null); // while this inRange state exists, we wait for OnWeaponReady() to be called } else { _moveTarget = _primaryTarget; _moveSpeed = Speed.Full; _orderSource = OrderSource.ElementCaptain; Call(ShipState.Moving); yield return null; // required immediately after Call() to avoid FSM bug if (_isDestinationUnreachable) { __HandleDestinationUnreachable(); yield break; } _helm.AllStop(); // stop and shoot after completing move } yield return null; } CurrentState = ShipState.Idling; }
void Moving_ExitState() { LogEvent(); var mortalMoveTarget = _moveTarget as IMortalTarget; if (mortalMoveTarget != null) { mortalMoveTarget.onTargetDeathOneShot -= OnTargetDeath; } _moveTarget = null; _moveSpeed = Speed.None; _orderSource = OrderSource.None; _helm.DisengageAutoPilot(); // the ship retains its existing speed and heading upon exit }
/// <summary> /// The Captain uses this method to issue orders. /// </summary> /// <param name="order">The order.</param> /// <param name="retainSuperiorsOrder">if set to <c>true</c> [retain superiors order].</param> /// <param name="target">The target.</param> /// <param name="speed">The speed.</param> private void OverrideCurrentOrder(ShipDirective order, bool retainSuperiorsOrder, INavigableTarget target = null, Speed speed = Speed.None) { // if the captain says to, and the current existing order is from his superior, then record it as a standing order ShipOrder standingOrder = null; if (retainSuperiorsOrder && CurrentOrder != null) { if (CurrentOrder.Source != OrderSource.ElementCaptain) { // the current order is from the Captain's superior so retain it standingOrder = CurrentOrder; if (IsHQElement) { // the captain is overriding his superior on the flagship so declare an emergency // HACK Command.__OnHQElementEmergency(); } } else if (CurrentOrder.StandingOrder != null) { // the current order is from the Captain, but there is a standing order in it so retain it standingOrder = CurrentOrder.StandingOrder; } } ShipOrder newOrder = new ShipOrder(order, OrderSource.ElementCaptain, target, speed) { StandingOrder = standingOrder }; CurrentOrder = newOrder; }