Now tracking toolbarIcon.
Now tracking toolbarIcon.

// AntennaRange // AntennaRange
// //
// AntennaRelay.cs // AntennaRelay.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// 3. Neither the name of the copyright holder nor the names of its contributors may be used // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior written permission. // to endorse or promote products derived from this software without specific prior written permission.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ToadicusTools; using ToadicusTools;
   
namespace AntennaRange namespace AntennaRange
{ {
public class AntennaRelay public class AntennaRelay
{ {
public static bool requireLineOfSight; public static bool requireLineOfSight;
   
// We don't have a Bard, so we'll hide Kerbin here. // We don't have a Bard, so we'll hide Kerbin here.
public static CelestialBody Kerbin; protected CelestialBody Kerbin;
   
protected CelestialBody _firstOccludingBody; protected CelestialBody _firstOccludingBody;
   
protected IAntennaRelay _nearestRelayCache; protected IAntennaRelay _nearestRelayCache;
protected IAntennaRelay moduleRef; protected IAntennaRelay moduleRef;
   
protected System.Diagnostics.Stopwatch searchTimer; protected System.Diagnostics.Stopwatch searchTimer;
protected long millisecondsBetweenSearches; protected long millisecondsBetweenSearches;
   
/// <summary> /// <summary>
/// Gets the parent Vessel. /// Gets the parent Vessel.
/// </summary> /// </summary>
/// <value>The parent Vessel.</value> /// <value>The parent Vessel.</value>
public virtual Vessel vessel public virtual Vessel vessel
{ {
get get
{ {
return this.moduleRef.vessel; return this.moduleRef.vessel;
} }
} }
   
/// <summary> /// <summary>
/// Gets or sets the nearest relay. /// Gets or sets the nearest relay.
/// </summary> /// </summary>
/// <value>The nearest relay</value> /// <value>The nearest relay</value>
public IAntennaRelay nearestRelay public IAntennaRelay nearestRelay
{ {
get get
{ {
if (this.searchTimer.IsRunning && if (this.searchTimer.IsRunning &&
this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches) this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches)
{ {
this._nearestRelayCache = this.FindNearestRelay(); this._nearestRelayCache = this.FindNearestRelay();
this.searchTimer.Restart(); this.searchTimer.Restart();
} }
   
return this._nearestRelayCache; return this._nearestRelayCache;
} }
protected set protected set
{ {
this._nearestRelayCache = value; this._nearestRelayCache = value;
} }
} }
   
/// <summary> /// <summary>
/// Gets the first occluding body. /// Gets the first occluding body.
/// </summary> /// </summary>
/// <value>The first occluding body.</value> /// <value>The first occluding body.</value>
public CelestialBody firstOccludingBody public CelestialBody firstOccludingBody
{ {
get get
{ {
return this._firstOccludingBody; return this._firstOccludingBody;
} }
} }
   
/// <summary> /// <summary>
/// Gets the transmit distance. /// Gets the transmit distance.
/// </summary> /// </summary>
/// <value>The transmit distance.</value> /// <value>The transmit distance.</value>
public double transmitDistance public double transmitDistance
{ {
get get
{ {
this.nearestRelay = this.FindNearestRelay(); this.nearestRelay = this.FindNearestRelay();
   
// If there is no available relay nearby... // If there is no available relay nearby...
if (this.nearestRelay == null) if (this.nearestRelay == null)
{ {
// .. return the distance to Kerbin // .. return the distance to Kerbin
return this.DistanceTo(Kerbin); return this.DistanceTo(this.Kerbin);
} }
else else
{ {
/// ...otherwise, return the distance to the nearest available relay. /// ...otherwise, return the distance to the nearest available relay.
return this.DistanceTo(nearestRelay); return this.DistanceTo(nearestRelay);
} }
} }
} }
   
/// <summary> /// <summary>
/// The maximum distance at which this relay can operate. /// The maximum distance at which this relay can operate.
/// </summary> /// </summary>
/// <value>The max transmit distance.</value> /// <value>The max transmit distance.</value>
public virtual float maxTransmitDistance public virtual float maxTransmitDistance
{ {
get; get;
set; set;
} }
   
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during /// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
/// the current relay attempt. /// the current relay attempt.
/// </summary> /// </summary>
/// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value> /// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
public virtual bool relayChecked public virtual bool relayChecked
{ {
get; get;
protected set; protected set;
} }
   
/// <summary> /// <summary>
/// Determines whether this instance can transmit. /// Determines whether this instance can transmit.
/// </summary> /// </summary>
/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
public virtual bool CanTransmit() public virtual bool CanTransmit()
{ {
if ( if (
this.transmitDistance > this.maxTransmitDistance || this.transmitDistance > this.maxTransmitDistance ||
( (
requireLineOfSight && requireLineOfSight &&
this.nearestRelay == null && this.nearestRelay == null &&
!this.vessel.hasLineOfSightTo(Kerbin, out this._firstOccludingBody) !this.vessel.hasLineOfSightTo(this.Kerbin, out this._firstOccludingBody)
) )
) )
{ {
return false; return false;
} }
else else
{ {
return true; return true;
} }
} }
   
/// <summary> /// <summary>
/// Finds the nearest relay. /// Finds the nearest relay.
/// </summary> /// </summary>
/// <returns>The nearest relay or null, if no relays in range.</returns> /// <returns>The nearest relay or null, if no relays in range.</returns>
public IAntennaRelay FindNearestRelay() public IAntennaRelay FindNearestRelay()
{ {
if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches) if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches)
{ {
return this.nearestRelay; return this.nearestRelay;
} }
   
if (this.searchTimer.IsRunning) if (this.searchTimer.IsRunning)
{ {
this.searchTimer.Stop(); this.searchTimer.Stop();
this.searchTimer.Reset(); this.searchTimer.Reset();
} }
   
this.searchTimer.Start(); this.searchTimer.Start();
   
Tools.PostDebugMessage(string.Format( Tools.PostDebugMessage(string.Format(
"{0}: finding nearest relay for {1} ({2})", "{0}: finding nearest relay for {1} ({2})",
this.GetType().Name, this.GetType().Name,
this, this,
this.vessel.id this.vessel.id
)); ));
   
this._firstOccludingBody = null; this._firstOccludingBody = null;
   
// Set this vessel as checked, so that we don't check it again. // Set this vessel as checked, so that we don't check it again.
RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true; RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
   
double nearestSqrDistance = double.PositiveInfinity; double nearestSqrDistance = double.PositiveInfinity;
IAntennaRelay _nearestRelay = null; IAntennaRelay _nearestRelay = null;
   
/* /*
* Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too * Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too
* far away. When we find a candidate, get through its antennae for relays which have not been checked yet * far away. When we find a candidate, get through its antennae for relays which have not been checked yet
* and that can transmit. Once we find a suitable candidate, assign it to _nearestRelay for comparison * and that can transmit. Once we find a suitable candidate, assign it to _nearestRelay for comparison
* against future finds. * against future finds.
* */ * */
foreach (Vessel potentialVessel in FlightGlobals.Vessels) foreach (Vessel potentialVessel in FlightGlobals.Vessels)
{ {
// Skip vessels that have already been checked for a nearest relay this pass. // Skip vessels that have already been checked for a nearest relay this pass.
if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id)) if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id))
{ {
continue; continue;
} }
   
// Skip vessels of the wrong type. // Skip vessels of the wrong type.
switch (potentialVessel.vesselType) switch (potentialVessel.vesselType)
{ {
case VesselType.Debris: case VesselType.Debris:
case VesselType.Flag: case VesselType.Flag:
case VesselType.EVA: case VesselType.EVA:
case VesselType.SpaceObject: case VesselType.SpaceObject:
case VesselType.Unknown: case VesselType.Unknown:
continue; continue;
default: default:
break; break;
} }
   
// Skip vessels with the wrong ID // Skip vessels with the wrong ID
if (potentialVessel.id == vessel.id) if (potentialVessel.id == vessel.id)
{ {
continue; continue;
} }
   
// Skip vessels to which we do not have line of sight. // Skip vessels to which we do not have line of sight.
if (requireLineOfSight && !this.vessel.hasLineOfSightTo(potentialVessel, out this._firstOccludingBody)) if (requireLineOfSight && !this.vessel.hasLineOfSightTo(potentialVessel, out this._firstOccludingBody))
{ {
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"Vessel {0} discarded because we do not have line of sight.", "Vessel {0} discarded because we do not have line of sight.",
potentialVessel.vesselName potentialVessel.vesselName
); );
continue; continue;
} }
   
// Find the distance from here to the vessel... // Find the distance from here to the vessel...
double potentialSqrDistance = (potentialVessel.GetWorldPos3D() - vessel.GetWorldPos3D()).sqrMagnitude; double potentialSqrDistance = (potentialVessel.GetWorldPos3D() - vessel.GetWorldPos3D()).sqrMagnitude;
   
/* /*
* ...so that we can skip the vessel if it is further away than Kerbin, our transmit distance, or a * ...so that we can skip the vessel if it is further away than Kerbin, our transmit distance, or a
* vessel we've already checked. * vessel we've already checked.
* */ * */
if ( if (
potentialSqrDistance > Tools.Min( potentialSqrDistance > Tools.Min(
this.maxTransmitDistance * this.maxTransmitDistance, this.maxTransmitDistance * this.maxTransmitDistance,
nearestSqrDistance, nearestSqrDistance,
this.vessel.sqrDistanceTo(Kerbin) this.vessel.sqrDistanceTo(Kerbin)
) )
) )
{ {
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"Vessel {0} discarded because it is out of range, or farther than another relay.", "Vessel {0} discarded because it is out of range, or farther than another relay.",
potentialVessel.vesselName potentialVessel.vesselName
); );
continue; continue;
} }
   
nearestSqrDistance = potentialSqrDistance; nearestSqrDistance = potentialSqrDistance;
   
foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays()) foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
{ {
if (potentialRelay.CanTransmit()) if (potentialRelay.CanTransmit())
{ {
_nearestRelay = potentialRelay; _nearestRelay = potentialRelay;
Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})", Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})",
this.GetType().Name, this.GetType().Name,
_nearestRelay.ToString(), _nearestRelay.ToString(),
_nearestRelay.vessel.id _nearestRelay.vessel.id
)); ));
break; break;
} }
} }
} }
   
// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be // Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
// used next time. // used next time.
RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id); RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
   
// Return the nearest available relay, or null if there are no available relays nearby. // Return the nearest available relay, or null if there are no available relays nearby.
return _nearestRelay; return _nearestRelay;
} }
   
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class. /// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
/// </summary> /// </summary>
/// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param> /// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
public AntennaRelay(IAntennaRelay module) public AntennaRelay(IAntennaRelay module)
{ {
this.moduleRef = module; this.moduleRef = module;
   
this.searchTimer = new System.Diagnostics.Stopwatch(); this.searchTimer = new System.Diagnostics.Stopwatch();
this.millisecondsBetweenSearches = 1250; this.millisecondsBetweenSearches = 5000;
   
// HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start, // HACK: This might not be safe in all circumstances, but since AntennaRelays are not built until Start,
// we hope it is safe enough. // we hope it is safe enough.
if (AntennaRelay.Kerbin == null) this.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");
{  
AntennaRelay.Kerbin = FlightGlobals.Bodies.FirstOrDefault(b => b.name == "Kerbin");  
}  
} }
   
static AntennaRelay() static AntennaRelay()
{ {
var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>(); var config = KSP.IO.PluginConfiguration.CreateForType<AntennaRelay>();
   
config.load(); config.load();
   
AntennaRelay.requireLineOfSight = config.GetValue<bool>("requireLineOfSight", false); AntennaRelay.requireLineOfSight = config.GetValue<bool>("requireLineOfSight", false);
   
config.save(); config.save();
} }
} }
} }
   
   
file:b/toolbarIcon.xcf (new)
 Binary files /dev/null and b/toolbarIcon.xcf differ