public void ProcessState() { // Nothing to salvage in stations if (Cache.Instance.InStation) { return; } var cargo = Cache.Instance.DirectEve.GetShipsCargo(); switch (State) { case SalvageState.TargetWrecks: TargetWrecks(); // Next state State = SalvageState.LootWrecks; break; case SalvageState.LootWrecks: LootWrecks(); State = SalvageState.SalvageWrecks; break; case SalvageState.SalvageWrecks: ActivateTractorBeams(); ActivateSalvagers(); // Default action State = SalvageState.TargetWrecks; if (cargo.IsReady && cargo.Items.Any() && _nextAction < DateTime.Now) { // Check if there are actually duplicates var duplicates = cargo.Items.Where(i => i.Quantity > 0).GroupBy(i => i.TypeId).Any(t => t.Count() > 1); if (duplicates) { State = SalvageState.StackItems; } else { _nextAction = DateTime.Now.AddSeconds(150); } } break; case SalvageState.StackItems: Logging.Log("Salvage: Stacking items"); if (cargo.IsReady) { cargo.StackAll(); } _nextAction = DateTime.Now.AddSeconds(5); State = SalvageState.WaitForStacking; break; case SalvageState.WaitForStacking: // Wait 5 seconds after stacking if (_nextAction > DateTime.Now) { break; } if (Cache.Instance.DirectEve.GetLockedItems().Count == 0) { Logging.Log("Salvage: Done stacking"); State = SalvageState.TargetWrecks; break; } if (DateTime.Now.Subtract(_nextAction).TotalSeconds > 120) { Logging.Log("Salvage: Stacking items timed out, clearing item locks"); Cache.Instance.DirectEve.UnlockItems(); Logging.Log("Salvage: Done stacking"); State = SalvageState.TargetWrecks; break; } break; default: // Unknown state, goto first state State = SalvageState.TargetWrecks; break; } }
/// <summary> /// Loot any wrecks & cargo containers close by /// </summary> private void LootWrecks() { var cargo = Cache.Instance.DirectEve.GetShipsCargo(); if (cargo.Window == null) { // No, command it to open Cache.Instance.DirectEve.ExecuteCommand(DirectCmd.OpenCargoHoldOfActiveShip); return; } // Ship's cargo is not ready yet if (!cargo.IsReady) { return; } var shipsCargo = cargo.Items.Select(i => new ItemCache(i)).ToList(); var freeCargoCapacity = cargo.Capacity - cargo.UsedCapacity; var lootWindows = Cache.Instance.DirectEve.Windows.OfType <DirectContainerWindow>().Where(w => w.Type == "form.LootCargoView"); foreach (var window in lootWindows) { // The window is not ready, then continue if (!window.IsReady) { continue; } // Get the container var containerEntity = Cache.Instance.EntityById(window.ItemId); // Does it no longer exist or is it out of transfer range or its looted if (containerEntity == null || containerEntity.Distance > 2500 || Cache.Instance.LootedContainers.Contains(containerEntity.Id)) { Logging.Log("Salvage: Closing loot window [" + window.ItemId + "]"); window.Close(); continue; } // Get the container that is associated with the cargo container var container = Cache.Instance.DirectEve.GetContainer(window.ItemId); // List its items var items = container.Items.Select(i => new ItemCache(i)); // Build a list of items to loot var lootItems = new List <ItemCache>(); // Walk through the list of items ordered by highest value item first foreach (var item in items.OrderByDescending(i => i.IskPerM3)) { // We pick up loot depending on isk per m3 var isMissionItem = Cache.Instance.MissionItems.Contains((item.Name ?? string.Empty).ToLower()); // Never pick up contraband (unless its the mission item) if (!isMissionItem && item.IsContraband) { continue; } // Do we want to loot other items? if (!isMissionItem && !LootEverything) { continue; } // We are at our max, either make room or skip the item if ((freeCargoCapacity - item.TotalVolume) <= (isMissionItem ? 0 : ReserveCargoCapacity)) { // We can't drop items in this container anyway, well get it after its salvaged if (!isMissionItem && containerEntity.GroupId != (int)Group.CargoContainer) { continue; } // Make a list of items which are worth less List <ItemCache> worthLess; if (isMissionItem) { worthLess = shipsCargo; } else if (item.IskPerM3.HasValue) { worthLess = shipsCargo.Where(sc => sc.IskPerM3.HasValue && sc.IskPerM3 < item.IskPerM3).ToList(); } else { worthLess = shipsCargo.Where(sc => sc.IskPerM3.HasValue).ToList(); } // Remove mission item from this list worthLess.RemoveAll(wl => Cache.Instance.MissionItems.Contains((wl.Name ?? string.Empty).ToLower())); worthLess.RemoveAll(wl => (wl.Name ?? string.Empty).ToLower() == Cache.Instance.BringMissionItem); // Consider dropping ammo if it concerns the mission item! if (!isMissionItem) { worthLess.RemoveAll(wl => Ammo.Any(a => a.TypeId == wl.TypeId)); } // Nothing is worth less then the current item if (worthLess.Count() == 0) { continue; } // Not enough space even if we dumped the crap if ((freeCargoCapacity + worthLess.Sum(wl => wl.TotalVolume)) < item.TotalVolume) { if (isMissionItem) { Logging.Log("Salvage: Not enough space for mission item! Need [" + item.TotalVolume + "] maximum available [" + (freeCargoCapacity + worthLess.Sum(wl => wl.TotalVolume)) + "]"); } continue; } // Start clearing out items that are worth less var moveTheseItems = new List <DirectItem>(); foreach (var wl in worthLess.OrderBy(wl => wl.IskPerM3.HasValue ? wl.IskPerM3.Value : double.MaxValue).ThenByDescending(wl => wl.TotalVolume)) { // Mark this item as moved moveTheseItems.Add(wl.DirectItem); // Substract (now) free volume freeCargoCapacity += wl.TotalVolume; // We freed up enough space? if ((freeCargoCapacity - item.TotalVolume) >= ReserveCargoCapacity) { break; } } if (moveTheseItems.Count > 0) { // If this is not a cargo container, then jettison loot if (containerEntity.GroupId != (int)Group.CargoContainer || isMissionItem) { if (DateTime.Now.Subtract(_lastJettison).TotalSeconds < 185) { return; } Logging.Log("Salvage: Jettisoning [" + moveTheseItems.Count + "] items to make room for the more valuable loot"); // Note: This could (in theory) f**k up with the bot jettison an item and // then picking it up again :/ (granted it should never happen unless // mission item volume > reserved volume cargo.Jettison(moveTheseItems.Select(i => i.ItemId)); _lastJettison = DateTime.Now; return; } // Move items to the cargo container container.Add(moveTheseItems); // Remove it from the ships cargo list shipsCargo.RemoveAll(i => moveTheseItems.Any(wl => wl.ItemId == i.Id)); Logging.Log("Salvage: Moving [" + moveTheseItems.Count + "] items into the cargo container to make room for the more valuable loot"); } } // Update free space freeCargoCapacity -= item.TotalVolume; lootItems.Add(item); } // Mark container as looted Cache.Instance.LootedContainers.Add(containerEntity.Id); // Loot actual items if (lootItems.Count != 0) { Logging.Log("Salvage: Looting container [" + containerEntity.Name + "][" + containerEntity.Id + "], [" + lootItems.Count + "] valuable items"); cargo.Add(lootItems.Select(i => i.DirectItem)); } else { Logging.Log("Salvage: Container [" + containerEntity.Name + "][" + containerEntity.Id + "] contained no valuable items"); } } // Open a container in range foreach (var containerEntity in Cache.Instance.Containers.Where(e => e.Distance <= 2500)) { // Emptry wreck, ignore if (containerEntity.GroupId == (int)Group.Wreck && containerEntity.IsWreckEmpty) { continue; } // We looted this container if (Cache.Instance.LootedContainers.Contains(containerEntity.Id)) { continue; } // We already opened the loot window var window = lootWindows.FirstOrDefault(w => w.ItemId == containerEntity.Id); if (window != null) { continue; } // Ignore open request within 10 seconds if (_openedContainers.ContainsKey(containerEntity.Id) && DateTime.Now.Subtract(_openedContainers[containerEntity.Id]).TotalSeconds < 10) { continue; } // Open the container Logging.Log("Salvage: Opening container [" + containerEntity.Name + "][" + containerEntity.Id + "]"); containerEntity.OpenCargo(); _openedContainers[containerEntity.Id] = DateTime.Now; break; } }
public void ProcessState() { if (!Settings.Instance.UseDrones) { return; } switch (State) { case DroneState.WaitingForTargets: // Are we in the right state ? if (Cache.Instance.ActiveDrones.Count() > 0) { // Apparently not, we have drones out, go into fight mode State = DroneState.Fighting; break; } // Should we launch drones? var launch = true; // Always launch if we're scrambled if (!Cache.Instance.PriorityTargets.Any(pt => pt.IsWarpScramblingMe)) { launch &= Cache.Instance.UseDrones; // Are we done with this mission pocket? launch &= !Cache.Instance.IsMissionPocketDone; // If above minimums launch &= Cache.Instance.DirectEve.ActiveShip.ShieldPercentage >= Settings.Instance.DroneMinimumShieldPct; launch &= Cache.Instance.DirectEve.ActiveShip.ArmorPercentage >= Settings.Instance.DroneMinimumArmorPct; launch &= Cache.Instance.DirectEve.ActiveShip.CapacitorPercentage >= Settings.Instance.DroneMinimumCapacitorPct; // yes if there are targets to kill launch &= Cache.Instance.TargetedBy.Count(e => !e.IsSentry && e.CategoryId == (int)CategoryID.Entity && e.IsNpc && !e.IsContainer && e.GroupId != (int)Group.LargeCollidableStructure && e.Distance < Settings.Instance.DroneControlRange) > 0; // If drones get agro'd within 30 seconds, then wait (5 * _recallCount + 5) seconds since the last recall if (_lastLaunch < _lastRecall && _lastRecall.Subtract(_lastLaunch).TotalSeconds < 30) { if (_lastRecall.AddSeconds(5 * _recallCount + 5) < DateTime.Now) { // Increase recall count and allow the launch _recallCount++; // Never let _recallCount go above 5 if (_recallCount > 5) { _recallCount = 5; } } else { // Do not launch the drones until the delay has passed launch = false; } } else // Drones have been out for more then 30s { _recallCount = 0; } } if (launch) { // Reset launch tries _launchTries = 0; _lastLaunch = DateTime.Now; State = DroneState.Launch; } break; case DroneState.Launch: // Launch all drones _launchTimeout = DateTime.Now; Cache.Instance.DirectEve.ActiveShip.LaunchAllDrones(); State = DroneState.Launching; break; case DroneState.Launching: // We haven't launched anything yet, keep waiting if (Cache.Instance.ActiveDrones.Count() == 0) { if (DateTime.Now.Subtract(_launchTimeout).TotalSeconds > 10) { // Relaunch if tries < 10 if (_launchTries < 10) { _launchTries++; State = DroneState.Launch; break; } else { State = DroneState.OutOfDrones; } } break; } // Are we done launching? if (_lastDroneCount == Cache.Instance.ActiveDrones.Count()) { State = DroneState.Fighting; } break; case DroneState.OutOfDrones: //if (DateTime.Now.Subtract(_launchTimeout).TotalSeconds > 1000) //{ // State = DroneState.WaitingForTargets; //} break; case DroneState.Fighting: // Should we recall our drones? This is a possible list of reasons why we should var recall = false; // Are we done (for now) ? if (Cache.Instance.TargetedBy.Count(e => !e.IsSentry && e.IsNpc && e.Distance < Settings.Instance.DroneControlRange) == 0) { Logging.Log("Drones: Recalling drones because no NPC is targeting us within dronerange"); recall = true; } if (Cache.Instance.IsMissionPocketDone) { Logging.Log("Drones: Recalling drones because we are done with this pocket."); recall = true; } else if (_shieldPctTotal > GetShieldPctTotal()) { Logging.Log("Drones: Recalling drones because we have lost shields! [Old: " + _shieldPctTotal.ToString("N2") + "][New: " + GetShieldPctTotal().ToString("N2") + "]"); recall = true; } else if (_armorPctTotal > GetArmorPctTotal()) { Logging.Log("Drones: Recalling drones because we have lost armor! [Old:" + _armorPctTotal.ToString("N2") + "][New: " + GetArmorPctTotal().ToString("N2") + "]"); recall = true; } else if (_structurePctTotal > GetStructurePctTotal()) { Logging.Log("Drones: Recalling drones because we have lost structure! [Old:" + _structurePctTotal.ToString("N2") + "][New: " + GetStructurePctTotal().ToString("N2") + "]"); recall = true; } else if (Cache.Instance.ActiveDrones.Count() < _lastDroneCount) { // Did we lose a drone? (this should be covered by total's as well though) Logging.Log("Drones: Recalling drones because we have lost a drone! [Old:" + _lastDroneCount + "][New: " + Cache.Instance.ActiveDrones.Count() + "]"); recall = true; } else { // Default to long range recall var lowShieldWarning = Settings.Instance.LongRangeDroneRecallShieldPct; var lowArmorWarning = Settings.Instance.LongRangeDroneRecallArmorPct; var lowCapWarning = Settings.Instance.LongRangeDroneRecallCapacitorPct; if (Cache.Instance.ActiveDrones.Average(d => d.Distance) < (Settings.Instance.DroneControlRange / 2d)) { lowShieldWarning = Settings.Instance.DroneRecallShieldPct; lowArmorWarning = Settings.Instance.DroneRecallArmorPct; lowCapWarning = Settings.Instance.DroneRecallCapacitorPct; } if (Cache.Instance.DirectEve.ActiveShip.ShieldPercentage < lowShieldWarning) { Logging.Log("Drones: Recalling drones due to shield [" + Cache.Instance.DirectEve.ActiveShip.ShieldPercentage + "%] below [" + lowShieldWarning + "%] minimum"); recall = true; } else if (Cache.Instance.DirectEve.ActiveShip.ArmorPercentage < lowArmorWarning) { Logging.Log("Drones: Recalling drones due to armor [" + Cache.Instance.DirectEve.ActiveShip.ArmorPercentage + "%] below [" + lowArmorWarning + "%] minimum"); recall = true; } else if (Cache.Instance.DirectEve.ActiveShip.CapacitorPercentage < lowCapWarning) { Logging.Log("Drones: Recalling drones due to capacitor [" + Cache.Instance.DirectEve.ActiveShip.CapacitorPercentage + "%] below [" + lowCapWarning + "%] minimum"); recall = true; } } if (Cache.Instance.ActiveDrones.Count() == 0) { Logging.Log("Drones: Apparently we have lost all our drones"); recall = true; } else { var isPanicking = false; isPanicking |= Cache.Instance.DirectEve.ActiveShip.ShieldPercentage < Settings.Instance.MinimumShieldPct; isPanicking |= Cache.Instance.DirectEve.ActiveShip.ArmorPercentage < Settings.Instance.MinimumArmorPct; isPanicking |= Cache.Instance.DirectEve.ActiveShip.CapacitorPercentage < Settings.Instance.MinimumCapacitorPct; if (Cache.Instance.PriorityTargets.Any(pt => pt.IsWarpScramblingMe) && recall) { Logging.Log("Drones: Overriding drone recall, we are scrambled!"); recall = false; } } // Recall or engage if (recall) { State = DroneState.Recalling; } else { EngageTarget(); // We lost a drone and did not recall, assume panicking and launch (if any) additional drones if (Cache.Instance.ActiveDrones.Count() < _lastDroneCount) { State = DroneState.Launch; } } break; case DroneState.Recalling: // Are we done? if (Cache.Instance.ActiveDrones.Count() == 0) { _lastRecall = DateTime.Now; State = DroneState.WaitingForTargets; break; } // Give recall command every 5 seconds if (DateTime.Now.Subtract(_lastRecallCommand).TotalSeconds > 5) { Cache.Instance.DirectEve.ExecuteCommand(DirectCmd.CmdDronesReturnToBay); _lastRecallCommand = DateTime.Now; } break; } // Update health values _shieldPctTotal = GetShieldPctTotal(); _armorPctTotal = GetArmorPctTotal(); _structurePctTotal = GetStructurePctTotal(); _lastDroneCount = Cache.Instance.ActiveDrones.Count(); }
/// <summary> /// Target wrecks within range /// </summary> private void TargetWrecks() { // We are jammed, we do not need to log (Combat does this already) if (Cache.Instance.DirectEve.ActiveShip.MaxLockedTargets == 0) { return; } var targets = new List <EntityCache>(); targets.AddRange(Cache.Instance.Targets); targets.AddRange(Cache.Instance.Targeting); var hasSalvagers = Cache.Instance.Modules.Any(m => Salvagers.Contains(m.TypeId)); var wreckTargets = targets.Where(t => (t.GroupId == (int)Group.Wreck || t.GroupId == (int)Group.CargoContainer) && t.CategoryId == (int)CategoryID.Celestial).ToList(); // Check for cargo containers foreach (var wreck in wreckTargets) { if (Cache.Instance.IgnoreTargets.Contains(wreck.Name)) { Logging.Log("Salvage: Cargo Container [" + wreck.Name + "][" + wreck.Id + "] on the ignore list, ignoring."); wreck.UnlockTarget(); continue; } if (hasSalvagers && wreck.GroupId != (int)Group.CargoContainer) { continue; } // Unlock if within loot range if (wreck.Distance < 2500) { Logging.Log("Salvage: Cargo Container [" + wreck.Name + "][" + wreck.Id + "] within loot range, unlocking container."); wreck.UnlockTarget(); } } if (wreckTargets.Count >= MaximumWreckTargets) { return; } var tractorBeams = Cache.Instance.Modules.Where(m => TractorBeams.Contains(m.TypeId)).ToList(); var tractorBeamRange = 0d; if (tractorBeams.Count > 0) { tractorBeamRange = tractorBeams.Min(t => t.OptimalRange); } var wrecks = Cache.Instance.UnlootedContainers; foreach (var wreck in wrecks.Where(w => !Cache.Instance.IgnoreTargets.Contains(w.Name.Trim()))) { // Its already a target, ignore it if (wreck.IsTarget || wreck.IsTargeting) { continue; } if (wreck.Distance > tractorBeamRange) { continue; } if (!wreck.HaveLootRights) { continue; } // No need to tractor a non-wreck within loot range if (wreck.GroupId != (int)Group.Wreck && wreck.Distance < 2500) { continue; } if (wreck.GroupId != (int)Group.Wreck && wreck.GroupId != (int)Group.CargoContainer) { continue; } if (!hasSalvagers) { // Ignore already looted wreck if (Cache.Instance.LootedContainers.Contains(wreck.Id)) { continue; } // Ignore empty wrecks if (wreck.GroupId == (int)Group.Wreck && wreck.IsWreckEmpty) { continue; } } Logging.Log("Salvage: Locking [" + wreck.Name + "][" + wreck.Id + "]"); wreck.LockTarget(); wreckTargets.Add(wreck); if (wreckTargets.Count >= MaximumWreckTargets) { break; } } }
/// <summary> /// Navigate to a solar system /// </summary> /// <param name = "solarSystemId"></param> private void NagivateToBookmarkSystem(long solarSystemId) { if (_nextTravelerAction > DateTime.Now) { return; } var undockBookmark = UndockBookmark; UndockBookmark = undockBookmark; var destination = Cache.Instance.DirectEve.Navigation.GetDestinationPath(); if (destination.Count == 0 || !destination.Any(d => d == solarSystemId)) { // We do not have the destination set var location = Cache.Instance.DirectEve.Navigation.GetLocation(solarSystemId); if (location.IsValid) { Logging.Log("Traveler: Setting destination to [" + location.Name + "]"); location.SetDestination(); } else { Logging.Log("Traveler: Error setting solar system destination [" + solarSystemId + "]"); State = TravelerState.Error; } return; } else { if (!Cache.Instance.InSpace) { if (Cache.Instance.InStation) { Cache.Instance.DirectEve.ExecuteCommand(DirectCmd.CmdExitStation); _nextTravelerAction = DateTime.Now.AddSeconds((int)Time.TravelerExitStationAmIInSpaceYet_seconds); } // We are not yet in space, wait for it return; } // Find the first waypoint var waypoint = destination.First(); // Get the name of the next systems var locationName = Cache.Instance.DirectEve.Navigation.GetLocationName(waypoint); // Find the stargate associated with it var entities = Cache.Instance.EntitiesByName(locationName).Where(e => e.GroupId == (int)Group.Stargate); if (entities.Count() == 0) { // not found, that cant be true?!?!?!?! Logging.Log("Traveler: Error [Stargate (" + locationName + ")] not found, most likely lag waiting 15 seconds."); _nextTravelerAction = DateTime.Now.AddSeconds((int)Time.TravelerNoStargatesFoundRetryDelay_seconds); return; } // Warp to, approach or jump the stargate var entity = entities.First(); if (entity.Distance < (int)Distance.DecloakRange) { Logging.Log("Traveler: Jumping to [" + locationName + "]"); entity.Jump(); _nextTravelerAction = DateTime.Now.AddSeconds((int)Time.TravelerJumpedGateNextCommandDelay_seconds); } else if (entity.Distance < (int)Distance.WarptoDistance) { entity.Approach(); //you could use a negative approach distance here but ultimately that is a bad idea.. Id like to go toward the entity without approaching it so we would end up inside the docking ring (eventually) } else { Logging.Log("Traveler: Warping to [Stargate (" + locationName + ")]"); entity.WarpTo(); _nextTravelerAction = DateTime.Now.AddSeconds((int)Time.TravelerInWarpedNextCommandDelay_seconds); } } }