diff --git a/BDArmory.Core/BDArmorySettings.cs b/BDArmory.Core/BDArmorySettings.cs index 7f51e4035..cc52ebe49 100644 --- a/BDArmory.Core/BDArmorySettings.cs +++ b/BDArmory.Core/BDArmorySettings.cs @@ -58,5 +58,8 @@ public class BDArmorySettings [BDAPersistantSettingsField] public static bool PERFORMANCE_LOGGING = false; [BDAPersistantSettingsField] public static bool AUTOCATEGORIZE_PARTS = true; [BDAPersistantSettingsField] public static bool SHOW_CATEGORIES = false; + [BDAPersistantSettingsField] public static bool RESOURCE_STEAL_ENABLED = false; + [BDAPersistantSettingsField] public static float RESOURCE_STEAL_FUEL_RATION = 0.2f; + [BDAPersistantSettingsField] public static float RESOURCE_STEAL_AMMO_RATION = 0.2f; } } diff --git a/BDArmory/Bullets/PooledBullet.cs b/BDArmory/Bullets/PooledBullet.cs index b96b2026a..2f524d76b 100644 --- a/BDArmory/Bullets/PooledBullet.cs +++ b/BDArmory/Bullets/PooledBullet.cs @@ -538,6 +538,22 @@ private void ApplyDamage(Part hitPart, RaycastHit hit, float multiplier, float p tData.lastPersonWhoHitMe = aName; tData.lastHitTime = Planetarium.GetUniversalTime(); tData.everyoneWhoHitMe.Add(aName); + + if (BDArmorySettings.RESOURCE_STEAL_ENABLED) + { + float fuelRation = BDArmorySettings.RESOURCE_STEAL_FUEL_RATION; + float ammoRation = BDArmorySettings.RESOURCE_STEAL_AMMO_RATION; + Vessel attacker = FlightGlobals.Vessels.Find(v => v.vesselName.Equals(aName)); + Vessel defender = FlightGlobals.Vessels.Find(v => v.vesselName.Equals(tName)); + if (attacker != null && defender != null) + { + StealResource(attacker, defender, "LiquidFuel", fuelRation); + StealResource(attacker, defender, "Oxidizer", fuelRation); + StealResource(attacker, defender, "20x102Ammo", ammoRation); + StealResource(attacker, defender, "30x173Ammo", ammoRation); + StealResource(attacker, defender, "50CalAmmo", ammoRation); + } + } } } @@ -559,6 +575,164 @@ private void ApplyDamage(Part hitPart, RaycastHit hit, float multiplier, float p } } + private class PriorityQueue + { + private Dictionary> partResources = new Dictionary>(); + + public PriorityQueue(HashSet elements) + { + foreach (PartResource r in elements) + { + Add(r); + } + } + + public void Add(PartResource r) + { + int key = r.part.resourcePriorityOffset; + if( partResources.ContainsKey(key) ) + { + List existing = partResources[key]; + existing.Add(r); + partResources[key] = existing; + } + else + { + List newList = new List(); + newList.Add(r); + partResources.Add(key, newList); + } + } + + public List Pop() + { + if( partResources.Count == 0 ) + { + return new List(); + } + int key = partResources.Keys.Max(); + List result = partResources[key]; + partResources.Remove(key); + return result; + } + + public bool HasNext() + { + return partResources.Count != 0; + } + } + + private void StealResource(Vessel src, Vessel dst, string resourceName, double ration) + { + // identify all parts on source vessel with resource + HashSet srcParts = new HashSet(); + foreach (Part p in src.Parts) + { + DeepFind(p, resourceName, srcParts); + } + + // identify all parts on destination vessel with resource + HashSet dstParts = new HashSet(); + foreach (Part p in dst.Parts) + { + DeepFind(p, resourceName, dstParts); + } + + if ( srcParts.Count == 0 || dstParts.Count == 0 ) + { + //Debug.Log(string.Format("[BDArmoryCompetition] Steal resource {0} failed; no parts.", resourceName)); + return; + } + + double remainingAmount = srcParts.Sum(p => p.amount); + double amount = remainingAmount * ration; + + // transfer resource from src->dst parts, honoring their priorities + PriorityQueue sources = new PriorityQueue(srcParts); + PriorityQueue recipients = new PriorityQueue(dstParts); + + List allocations = new List(); + List inputs = null, outputs = null; + while ( amount > 0 ) + { + if (inputs == null) + { + inputs = sources.Pop(); + } + if (outputs == null) + { + outputs = recipients.Pop(); + } + double availability = inputs.Sum(e => e.amount); + double opportunity = outputs.Sum(e => e.maxAmount - e.amount); + double tAmount = Math.Min(availability, Math.Min(opportunity, amount)); + double perPartAmount = tAmount / (inputs.Count * outputs.Count); + foreach (PartResource n in inputs) + { + foreach (PartResource m in outputs) + { + //Debug.Log(string.Format("[BDArmoryCompetition] Allocate {0} of {1} from {2} to {3}", perPartAmount, resourceName, n.part.name, m.part.name)); + ResourceAllocation ra = new ResourceAllocation(n, m.part, perPartAmount); + allocations.Add(ra); + } + } + if( availability < amount ) + { + inputs = null; + } + if( opportunity < amount ) + { + outputs = null; + } + if( tAmount == 0 ) + { + break; + } + amount -= tAmount; + } + if( allocations.Count == 0 ) + { + return; + } + double confirmed = 0; + foreach (ResourceAllocation ra in allocations) + { + confirmed += ra.sourceResource.part.TransferResource(ra.sourceResource, ra.amount, ra.destPart); + } + if (confirmed < 0) + { + Debug.Log(string.Format("[BDArmoryCompetition] Steal completed {0} of {3} from {2} to {1}", -confirmed, src.vesselName, dst.vesselName, resourceName)); + } + } + + private class ResourceAllocation + { + public PartResource sourceResource; + public Part destPart; + public double amount; + public ResourceAllocation(PartResource r, Part p, double a) + { + this.sourceResource = r; + this.destPart = p; + this.amount = a; + } + } + + private void DeepFind(Part p, string resourceName, HashSet accumulator) + { + foreach (PartResource r in p.Resources) + { + if( r.resourceName.Equals(resourceName) ) + { + accumulator.Add(r); + } + } + foreach (Part child in p.children) + { + DeepFind(child, resourceName, accumulator); + } + } + private void CalculateDragNumericalIntegration() { Vector3 dragAcc = currentVelocity * currentVelocity.magnitude * diff --git a/BDArmory/UI/BDArmorySetup.cs b/BDArmory/UI/BDArmorySetup.cs index 7cfe83710..d6a4c1889 100644 --- a/BDArmory/UI/BDArmorySetup.cs +++ b/BDArmory/UI/BDArmorySetup.cs @@ -1413,6 +1413,15 @@ void WindowSettings(int windowID) BDArmorySettings.MAX_NUM_BULLET_DECALS = (int)GUI.HorizontalSlider(SRightRect(line), BDArmorySettings.MAX_NUM_BULLET_DECALS, 1f, 999); line++; line++; + BDArmorySettings.RESOURCE_STEAL_ENABLED = GUI.Toggle(SLeftRect(line), BDArmorySettings.RESOURCE_STEAL_ENABLED, Localizer.Format("#LOC_BDArmory_Settings_ResourceStealEnabled"));//"Resource Steal Enabled" + line++; + GUI.Label(SLeftRect(line), $"{Localizer.Format("#LOC_BDArmory_Settings_FuelStealRation")}: ({BDArmorySettings.RESOURCE_STEAL_FUEL_RATION})", leftLabel);//Fuel Steal Ration + BDArmorySettings.RESOURCE_STEAL_FUEL_RATION = GUI.HorizontalSlider(SRightRect(line), BDArmorySettings.RESOURCE_STEAL_FUEL_RATION, 0f, 1f); + line++; + GUI.Label(SLeftRect(line), $"{Localizer.Format("#LOC_BDArmory_Settings_AmmoStealRation")}: ({BDArmorySettings.RESOURCE_STEAL_AMMO_RATION})", leftLabel);//Ammo Steal Ration + BDArmorySettings.RESOURCE_STEAL_AMMO_RATION = GUI.HorizontalSlider(SRightRect(line), BDArmorySettings.RESOURCE_STEAL_AMMO_RATION, 0f, 1f); + line++; + line++; bool origPm = BDArmorySettings.PEACE_MODE; BDArmorySettings.PEACE_MODE = GUI.Toggle(SLeftRect(line), BDArmorySettings.PEACE_MODE, Localizer.Format("#LOC_BDArmory_Settings_PeaceMode"));//"Peace Mode"