internal void ItemPickupEval(CacheItem item) { if (OnItemPickupEvaluation == null) {//If no event hooked then use default evaluation if (Bot.Settings.ItemRules.UseItemRules) { Interpreter.InterpreterAction action = ItemRulesEval.checkPickUpItem(item, ItemEvaluationType.PickUp); switch (action) { case Interpreter.InterpreterAction.PICKUP: item.ShouldPickup = true; break; case Interpreter.InterpreterAction.IGNORE: item.ShouldPickup = false; break; } } if (!item.ShouldPickup.HasValue) { //Use Giles Scoring or DB Weighting.. item.ShouldPickup = Bot.Settings.ItemRules.ItemRuleGilesScoring ? Backpack.GilesPickupItemValidation(item) : ItemManager.Current.EvaluateItem(item.ref_DiaItem.CommonData, ItemEvaluationType.PickUp); ; } } else { OnItemPickupEvaluation(item); } }
// ********************************************************************************************** // ***** Pickup Validation - Determines what should or should not be picked up ***** // ********************************************************************************************** internal static bool GilesPickupItemValidation(CacheItem item) { // Calculate giles item types and base types etc. GilesItemType thisGilesItemType = DetermineItemType(item.InternalName, item.BalanceData.thisItemType, item.BalanceData.thisFollowerType); GilesBaseItemType thisGilesBaseType = DetermineBaseType(thisGilesItemType); // If it's legendary, we always want it *IF* it's level is right if (item.Itemquality >= ItemQuality.Legendary) { if (Bot.Settings.Loot.MinimumLegendaryItemLevel > 0 && (item.BalanceData.iThisItemLevel >= Bot.Settings.Loot.MinimumLegendaryItemLevel || Bot.Settings.Loot.MinimumLegendaryItemLevel == 1)) return true; return false; } // Error logging for DemonBuddy item mis-reading ItemType gilesDBItemType = GilesToDBItemType(thisGilesItemType); if (gilesDBItemType != item.BalanceData.thisItemType) { Logger.DBLog.InfoFormat("GSError: Item type mis-match detected: Item Internal=" + item.InternalName + ". DemonBuddy ItemType thinks item type is=" + item.BalanceData.thisItemType + ". Giles thinks item type is=" + gilesDBItemType + ". [pickup]", true); } switch (thisGilesBaseType) { case GilesBaseItemType.WeaponTwoHand: case GilesBaseItemType.WeaponOneHand: case GilesBaseItemType.WeaponRange: // Not enough DPS, so analyse for possibility to blacklist if (item.Itemquality < ItemQuality.Magic1) { // White item, blacklist return false; } if (item.Itemquality >= ItemQuality.Magic1 && item.Itemquality < ItemQuality.Rare4) { if (Bot.Settings.Loot.MinimumWeaponItemLevel[0] == 0 || (Bot.Settings.Loot.MinimumWeaponItemLevel[0] != 0 && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumWeaponItemLevel[0])) { // Between magic and rare, and either we want no blues, or this level is higher than the blue level we want return false; } } else { if (Bot.Settings.Loot.MinimumWeaponItemLevel[1] == 0 || (Bot.Settings.Loot.MinimumWeaponItemLevel[1] != 0 && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumWeaponItemLevel[1])) { // Between magic and rare, and either we want no blues, or this level is higher than the blue level we want return false; } } break; case GilesBaseItemType.Armor: case GilesBaseItemType.Offhand: if (item.Itemquality < ItemQuality.Magic1) { // White item, blacklist return false; } if (item.Itemquality >= ItemQuality.Magic1 && item.Itemquality < ItemQuality.Rare4) { if (Bot.Settings.Loot.MinimumArmorItemLevel[0] == 0 || (Bot.Settings.Loot.MinimumArmorItemLevel[0] != 0 && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumArmorItemLevel[0])) { // Between magic and rare, and either we want no blues, or this level is higher than the blue level we want return false; } } else { if (Bot.Settings.Loot.MinimumArmorItemLevel[1] == 0 || (Bot.Settings.Loot.MinimumArmorItemLevel[1] != 0 && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumArmorItemLevel[1])) { // Between magic and rare, and either we want no blues, or this level is higher than the blue level we want return false; } } break; case GilesBaseItemType.Jewelry: if (item.Itemquality < ItemQuality.Magic1) { // White item, blacklist return false; } if (item.Itemquality >= ItemQuality.Magic1 && item.Itemquality < ItemQuality.Rare4) { if (Bot.Settings.Loot.MinimumJeweleryItemLevel[0] == 0 || (Bot.Settings.Loot.MinimumJeweleryItemLevel[0] != 0 && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumJeweleryItemLevel[0])) { // Between magic and rare, and either we want no blues, or this level is higher than the blue level we want return false; } } else { if (Bot.Settings.Loot.MinimumJeweleryItemLevel[1] == 0 || (Bot.Settings.Loot.MinimumJeweleryItemLevel[1] != 0 && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumJeweleryItemLevel[1])) { // Between magic and rare, and either we want no blues, or this level is higher than the blue level we want return false; } } break; case GilesBaseItemType.FollowerItem: if (item.BalanceData.iThisItemLevel < 60 || !Bot.Settings.Loot.PickupFollowerItems || item.Itemquality < ItemQuality.Rare4) { return false; } break; case GilesBaseItemType.Gem: if (item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MinimumGemItemLevel || (thisGilesItemType == GilesItemType.Ruby && !Bot.Settings.Loot.PickupGems[0]) || (thisGilesItemType == GilesItemType.Emerald && !Bot.Settings.Loot.PickupGems[1]) || (thisGilesItemType == GilesItemType.Amethyst && !Bot.Settings.Loot.PickupGems[2]) || (thisGilesItemType == GilesItemType.Topaz && !Bot.Settings.Loot.PickupGems[3]) || (thisGilesItemType == GilesItemType.Diamond && !Bot.Settings.Loot.PickupGemDiamond)) { return false; } break; case GilesBaseItemType.Misc: // Note; Infernal keys are misc, so should be picked up here - we aren't filtering them out, so should default to true at the end of this function if (thisGilesItemType == GilesItemType.CraftingMaterial) { if (item.BalanceData.iThisItemLevel == 63 && Bot.Settings.Loot.PickupDemonicEssence) return true; return Bot.Settings.Loot.PickupCraftMaterials; } if (thisGilesItemType == GilesItemType.CraftTome && !Bot.Settings.Loot.PickupCraftTomes) { return false; } if (thisGilesItemType == GilesItemType.CraftingPlan) { if (!Bot.Settings.Loot.PickupCraftPlans) return false; int gamebalanceID = item.BalanceID.Value; if (item.BalanceData.IsBlacksmithPlanSixProperties && !Bot.Settings.Loot.PickupBlacksmithPlanSix) return false; if (item.BalanceData.IsBlacksmithPlanFiveProperties && !Bot.Settings.Loot.PickupBlacksmithPlanFive) return false; if (item.BalanceData.IsBlacksmithPlanFourProperties && !Bot.Settings.Loot.PickupBlacksmithPlanFour) return false; if (item.BalanceData.IsBlacksmithPlanArchonSpaulders && !Bot.Settings.Loot.PickupBlacksmithPlanArchonSpaulders) return false; if (item.BalanceData.IsBlacksmithPlanArchonGauntlets && !Bot.Settings.Loot.PickupBlacksmithPlanArchonGauntlets) return false; if (item.BalanceData.IsBlacksmithPlanRazorspikes && !Bot.Settings.Loot.PickupBlacksmithPlanRazorspikes) return false; if (item.BalanceData.IsJewelcraftDesignAmulet && !Bot.Settings.Loot.PickupJewelerDesignAmulet) return false; if (item.BalanceData.IsJewelcraftDesignFlawlessStarGem && !Bot.Settings.Loot.PickupJewelerDesignFlawlessStar) return false; if (item.BalanceData.IsJewelcraftDesignMarquiseGem && !Bot.Settings.Loot.PickupJewelerDesignMarquise) return false; if (item.BalanceData.IsJewelcraftDesignPerfectStarGem && !Bot.Settings.Loot.PickupJewelerDesignPerfectStar) return false; if (item.BalanceData.IsJewelcraftDesignRadiantStarGem && !Bot.Settings.Loot.PickupJewelerDesignRadiantStar) return false; } if (thisGilesItemType == GilesItemType.InfernalKey) { if (!Bot.Settings.Loot.PickupInfernalKeys) return false; } // Potion filtering if (thisGilesItemType == GilesItemType.HealthPotion) { if (Bot.Settings.Loot.MaximumHealthPotions <= 0) return false; var Potions = Bot.Character.Data.BackPack.ReturnCurrentPotions(); if (Potions == null || !Potions.Any() || Bot.Character.Data.BackPack.BestPotionToUse == null) return true; if (Bot.Character.Data.BackPack.BestPotionToUse != null && item.BalanceData.iThisItemLevel < Bot.Character.Data.BackPack.BestPotionToUse.Level) return false; if (Potions.Sum(potions => potions.ThisItemStackQuantity) >= Bot.Settings.Loot.MaximumHealthPotions) return false; } if (thisGilesItemType == GilesItemType.MiscBook && item.BalanceData.iThisItemLevel < Bot.Settings.Loot.MiscItemLevel) return false; break; case GilesBaseItemType.HealthGlobe: return false; case GilesBaseItemType.Unknown: return false; default: return false; } // Switch giles base item type // Didn't cancel it, so default to true! return true; }
public void DroppedItemLog(CacheItem i) { CacheBalance thisBalanceData = i.BalanceData; GilesItemType thisGilesItemType = Backpack.DetermineItemType(i.InternalName, thisBalanceData.thisItemType, thisBalanceData.thisFollowerType); if (thisGilesItemType == GilesItemType.InfernalKey) { Bot.Game.CurrentGameStats.CurrentProfile.LootTracker.Keys.Dropped++; // Bot.BotStatistics.ProfileStats.CurrentProfile.ItemStats.stashedItemTotals[(int)LootIndex.Key]++; return; } switch (thisBalanceData.thisItemType) { case ItemType.CraftingPage: case ItemType.CraftingPlan: case ItemType.CraftingReagent: Bot.Game.CurrentGameStats.CurrentProfile.LootTracker.Crafting.Dropped++; // Bot.BotStatistics.ProfileStats.CurrentProfile.ItemStats.stashedItemTotals[(int)LootIndex.Crafting]++; break; case ItemType.Gem: Bot.Game.CurrentGameStats.CurrentProfile.LootTracker.Gems.Dropped++; // Bot.BotStatistics.ProfileStats.CurrentProfile.ItemStats.stashedItemTotals[(int)LootIndex.Gem]++; break; case ItemType.Amulet: case ItemType.Axe: case ItemType.Belt: case ItemType.Boots: case ItemType.Bow: case ItemType.Bracer: case ItemType.CeremonialDagger: case ItemType.Chest: case ItemType.Cloak: case ItemType.Crossbow: case ItemType.Dagger: case ItemType.Daibo: case ItemType.FistWeapon: case ItemType.FollowerSpecial: case ItemType.Gloves: case ItemType.HandCrossbow: case ItemType.Helm: case ItemType.Legs: case ItemType.Mace: case ItemType.MightyBelt: case ItemType.MightyWeapon: case ItemType.Mojo: case ItemType.Orb: case ItemType.Polearm: case ItemType.Quiver: case ItemType.Ring: case ItemType.Shield: case ItemType.Shoulder: case ItemType.Spear: case ItemType.SpiritStone: case ItemType.Staff: case ItemType.Sword: case ItemType.VoodooMask: case ItemType.Wand: case ItemType.WizardHat: if (i.Itemquality.Value == ItemQuality.Legendary) { //Bot.BotStatistics.ProfileStats.CurrentProfile.ItemStats.stashedItemTotals[3]++; Bot.Game.CurrentGameStats.CurrentProfile.LootTracker.Legendary.Dropped++; } else if (i.Itemquality.Value > ItemQuality.Magic3) { // Bot.BotStatistics.ProfileStats.CurrentProfile.ItemStats.stashedItemTotals[2]++; Bot.Game.CurrentGameStats.CurrentProfile.LootTracker.Rare.Dropped++; } else { // Bot.BotStatistics.ProfileStats.CurrentProfile.ItemStats.stashedItemTotals[1]++; Bot.Game.CurrentGameStats.CurrentProfile.LootTracker.Magical.Dropped++; } break; } }
/// <summary> /// /// </summary> /// <param name="name"></param> /// <param name="level"></param> /// <param name="itemQuality"></param> /// <param name="itemBaseType"></param> /// <param name="itemType"></param> /// <param name="isOneHand"></param> /// <param name="isTwoHand"></param> /// <param name="gameBalanceId"></param> private void fillPickupDic(CacheItem item) { object result; itemDic=new Dictionary<string, object>(); // add log unique key itemDic.Add("[KEY]", item.DynamicID.ToString()); // - BASETYPE ---------------------------------------------------------// itemDic.Add("[BASETYPE]", item.BalanceData.thisItemBaseType.ToString()); // - TYPE -------------------------------------------------------------// /// TODO remove this check if it isnt necessary anymore if (item.BalanceData.thisItemType==ItemType.Unknown&&(item.InternalName.Contains("Plan")||item.InternalName.Contains("Design"))) { Logger.DBLog.InfoFormat("There are still buggy itemType infos for craftingPlan around {0} has itemType = {1}", item.InternalName, item.BalanceData.thisItemType); result=ItemType.CraftingPlan.ToString(); } else result=item.BalanceData.thisItemType.ToString(); itemDic.Add("[TYPE]", result); // - QUALITY -------------------------------------------------------// itemDic.Add("[QUALITY]", Regex.Replace(item.Itemquality.ToString(), @"[\d-]", string.Empty)); itemDic.Add("[D3QUALITY]", item.Itemquality.ToString()); // - ROLL ----------------------------------------------------------// float roll; if (float.TryParse(Regex.Replace(item.Itemquality.ToString(), @"[^\d]", string.Empty), out roll)) itemDic.Add("[ROLL]", roll); else itemDic.Add("[ROLL]", 0); // - NAME -------------------------------------------------------------// itemDic.Add("[NAME]", item.InternalName.ToString().Replace(" ", "")); // - LEVEL ------------------------------------------------------------// itemDic.Add("[LEVEL]", (float)item.BalanceData.iThisItemLevel); itemDic.Add("[ONEHAND]", item.BalanceData.bThisOneHand); itemDic.Add("[TWOHAND]", item.BalanceData.bThisTwoHand); itemDic.Add("[UNIDENT]", (bool)true); itemDic.Add("[INTNAME]", item.InternalName); itemDic.Add("[ITEMID]", item.BalanceID.ToString()); }
/// <summary> /// /// </summary> /// <param name="item"></param> /// <returns></returns> internal InterpreterAction checkPickUpItem(CacheItem item, ItemEvaluationType evaluationType) { fillPickupDic(item); return checkItem(evaluationType); }
///<summary> ///Adds/Updates CacheObjects inside collection by Iteration of RactorList ///This is the method that caches all live data about an object! ///</summary> internal static bool UpdateCacheObjectCollection() { HashSet<int> hashDoneThisRactor = new HashSet<int>(); using (ZetaDia.Memory.AcquireFrame(true)) { if (!ZetaDia.IsInGame || ZetaDia.IsLoadingWorld || !ZetaDia.Me.IsValid) return false; foreach (Actor thisActor in ZetaDia.Actors.RActorList) { int tmp_raGUID; DiaObject thisObj; if (!thisActor.IsValid) continue; //Convert to DiaObject thisObj = (DiaObject)thisActor; tmp_raGUID = thisObj.RActorGuid; // See if we've already checked this ractor, this loop if (hashDoneThisRactor.Contains(tmp_raGUID)) continue; hashDoneThisRactor.Add(tmp_raGUID); //Update RactorGUID and check blacklisting.. if (BlacklistCache.IsRAGUIDBlacklisted(tmp_raGUID)) continue; CacheObject tmp_CachedObj; if (!Objects.TryGetValue(tmp_raGUID, out tmp_CachedObj)) { Vector3 tmp_position; int tmp_acdguid; int tmp_SNOID; #region SNO //Lookup SNO try { tmp_SNOID = thisObj.ActorSNO; } catch (NullReferenceException) { //Logger.DBLog.InfoFormat("Failure to get SNO from object! RaGUID: {0}", tmp_raGUID); continue; } #endregion //check our SNO blacklist if (BlacklistCache.IsSNOIDBlacklisted(tmp_SNOID) && !CacheIDLookup.hashSummonedPets.Contains(tmp_SNOID)) continue; #region Position try { tmp_position = thisObj.Position; } catch (NullReferenceException) { //Logger.DBLog.InfoFormat("Failure to get position vector for RAGUID {0}", tmp_raGUID); continue; } #endregion #region AcdGUID try { tmp_acdguid = thisObj.ACDGuid; } catch (NullReferenceException) { //Logger.DBLog.InfoFormat("Failure to get ACDGUID for RAGUID {0}", tmp_raGUID); continue; } #endregion tmp_CachedObj = new CacheObject(tmp_SNOID, tmp_raGUID, tmp_acdguid, tmp_position); } else //Reset unseen var tmp_CachedObj.LoopsUnseen = 0; //Validate try { if (thisObj.CommonData == null || thisObj.CommonData.ACDGuid != thisObj.ACDGuid) continue; } catch (NullReferenceException) { continue; } //Check if this object is a summoned unit by a player... #region SummonedUnits if (tmp_CachedObj.IsSummonedPet) { // Get the summoned-by info, cached if possible if (!tmp_CachedObj.SummonerID.HasValue) { try { tmp_CachedObj.SummonerID = thisObj.CommonData.GetAttribute<int>(ActorAttributeType.SummonedByACDID); } catch (Exception ex) { //Logger.DBLog.InfoFormat("[Funky] Safely handled exception getting summoned-by info [" + tmp_CachedObj.SNOID.ToString(CultureInfo.InvariantCulture) + "]"); //Logger.DBLog.DebugFormat(ex.ToString()); continue; } } //See if this summoned unit was summoned by the bot. if (Bot.Character.Data.iMyDynamicID == tmp_CachedObj.SummonerID.Value) { //Now modify the player data pets count.. if (Bot.Character.Class.AC == ActorClass.Monk) Bot.Character.Data.PetData.MysticAlly++; else if (Bot.Character.Class.AC == ActorClass.DemonHunter) { if (CacheIDLookup.hashDHPets.Contains(tmp_CachedObj.SNOID)) Bot.Character.Data.PetData.DemonHunterPet++; else if (CacheIDLookup.hashDHSpikeTraps.Contains(tmp_CachedObj.SNOID) && tmp_CachedObj.CentreDistance <= 50f) Bot.Character.Data.PetData.DemonHunterSpikeTraps++; } else if (Bot.Character.Class.AC == ActorClass.Witchdoctor) { if (CacheIDLookup.hashZombie.Contains(tmp_CachedObj.SNOID)) Bot.Character.Data.PetData.ZombieDogs++; else if (CacheIDLookup.hashGargantuan.Contains(tmp_CachedObj.SNOID)) Bot.Character.Data.PetData.Gargantuan++; } else if (Bot.Character.Class.AC == ActorClass.Wizard) { //only count when range is within 45f (so we can summon a new one) if (CacheIDLookup.hashWizHydras.Contains(tmp_CachedObj.SNOID) && tmp_CachedObj.CentreDistance <= 45f) Bot.Character.Data.PetData.WizardHydra++; } } //We return regardless if it was summoned by us or not since this object is not anything we want to deal with.. tmp_CachedObj.NeedsRemoved = true; continue; } #endregion //Update any SNO Data. #region SNO_Cache_Update if (tmp_CachedObj.ref_DiaObject == null || tmp_CachedObj.ContainsNullValues()) { if (!tmp_CachedObj.UpdateData(thisObj, tmp_CachedObj.RAGUID)) continue; } else if (!tmp_CachedObj.IsFinalized) {//Finalize this data by recreating it and updating the Sno cache with a new finalized entry, this also clears our all Sno cache dictionaries since we no longer need them! cacheSnoCollection.FinalizeEntry(tmp_CachedObj.SNOID); } #endregion //Special Cache for Interactable Server Objects if (CheckTargetTypeFlag(tmp_CachedObj.targetType.Value, TargetType.ServerInteractable)) { if (!Bot.Game.Profile.InteractableObjectCache.ContainsKey(tmp_CachedObj.RAGUID)) { Bot.Game.Profile.InteractableObjectCache.Add(tmp_CachedObj.RAGUID, tmp_CachedObj); } //Do not add to main cache! continue; } //Objects with static positions already cached don't need to be updated here. if (!tmp_CachedObj.NeedsUpdate) continue; //Obstacles -- (Not an actual object we add to targeting.) if (CheckTargetTypeFlag(tmp_CachedObj.targetType.Value, TargetType.Avoidance) || tmp_CachedObj.IsObstacle || tmp_CachedObj.HandleAsAvoidanceObject) { #region Obstacles CacheObstacle thisObstacle; //Do we have this cached? if (!Obstacles.TryGetValue(tmp_CachedObj.RAGUID, out thisObstacle)) { AvoidanceType AvoidanceType = AvoidanceType.None; if (tmp_CachedObj.IsAvoidance) { AvoidanceType = AvoidanceCache.FindAvoidanceUsingSNOID(tmp_CachedObj.SNOID); if (AvoidanceType == AvoidanceType.None) { AvoidanceType = AvoidanceCache.FindAvoidanceUsingName(tmp_CachedObj.InternalName); if (AvoidanceType == AvoidanceType.None) continue; } } if (tmp_CachedObj.IsAvoidance && tmp_CachedObj.IsProjectileAvoidance) {//Ranged Projectiles require more than simple bounding points.. so we create it as avoidance zone to cache it with properties. //Check for intersection.. try { ActorMovement thisMovement = thisObj.Movement; Vector2 Direction = thisMovement.DirectionVector; Ray R = new Ray(tmp_CachedObj.Position, Direction.ToVector3()); double Speed; //Lookup Cached Speed, or add new entry. if (!dictProjectileSpeed.TryGetValue(tmp_CachedObj.SNOID, out Speed)) { Speed = thisMovement.DesiredSpeed; dictProjectileSpeed.Add(tmp_CachedObj.SNOID, Speed); } thisObstacle = new CacheAvoidance(tmp_CachedObj, AvoidanceType, R, Speed); Obstacles.Add(thisObstacle); } catch { Logger.Write(LogLevel.Cache, "Failed to create projectile avoidance with rotation and speed. {0}", tmp_CachedObj.InternalName); } } else if (tmp_CachedObj.IsAvoidance) { //Poison Gas Can Be Friendly... if (AvoidanceType == AvoidanceType.PoisonGas) { int TeamID = 0; try { TeamID = thisObj.CommonData.GetAttribute<int>(ActorAttributeType.TeamID); } catch { Logger.Write(LogLevel.Cache, "Failed to retrieve TeamID attribute for object {0}", thisObstacle.InternalName); } //ID of 1 means its non-hostile! (-1?) 2?? //if (TeamID==1||TeamID==-1) if (TeamID != 10) { //Logger.Write(LogLevel.None, "Ignoring Avoidance {0} due to Friendly TeamID match!", tmp_CachedObj.InternalName); BlacklistCache.AddObjectToBlacklist(tmp_CachedObj.RAGUID, BlacklistType.Permanent); continue; } } thisObstacle = new CacheAvoidance(tmp_CachedObj, AvoidanceType); Obstacles.Add(thisObstacle); } else { //Obstacles. thisObstacle = new CacheServerObject(tmp_CachedObj); Obstacles.Add(thisObstacle); } } continue; #endregion } if (tmp_CachedObj.ObjectShouldBeRecreated) { //Specific updates if (tmp_CachedObj.Actortype.Value == ActorType.Item) { tmp_CachedObj = new CacheItem(tmp_CachedObj); } else if (tmp_CachedObj.Actortype.Value == ActorType.Monster) { tmp_CachedObj = new CacheUnit(tmp_CachedObj); } else if (tmp_CachedObj.Actortype.Value == ActorType.Gizmo) { if (CheckTargetTypeFlag(tmp_CachedObj.targetType.Value, TargetType.Interactables)) tmp_CachedObj = new CacheInteractable(tmp_CachedObj); else tmp_CachedObj = new CacheDestructable(tmp_CachedObj); } //Update Properties tmp_CachedObj.UpdateProperties(); } if (!tmp_CachedObj.UpdateData()) { if (!tmp_CachedObj.IsStillValid()) tmp_CachedObj.NeedsRemoved = true; continue; } //Obstacle cache if (tmp_CachedObj.Obstacletype.Value != ObstacleType.None && (CheckTargetTypeFlag(tmp_CachedObj.targetType.Value, TargetType.ServerObjects))) { CacheObstacle thisObstacleObj; if (!Obstacles.TryGetValue(tmp_CachedObj.RAGUID, out thisObstacleObj)) { CacheServerObject newobj = new CacheServerObject(tmp_CachedObj); Obstacles.Add(tmp_CachedObj.RAGUID, newobj); //Add nearby objects to our collection (used in navblock/obstaclecheck methods to reduce queries) if (CacheIDLookup.hashSNONavigationObstacles.Contains(newobj.SNOID)) Navigation.MGP.AddCellWeightingObstacle(newobj.SNOID, newobj.Radius); } else { if (thisObstacleObj.targetType.Value == TargetType.Unit) { //Since units position requires updating, we update using the CacheObject thisObstacleObj.Position = tmp_CachedObj.Position; Obstacles[tmp_CachedObj.RAGUID] = thisObstacleObj; } } } //cache it if (Objects.ContainsKey(tmp_CachedObj.RAGUID)) Objects[tmp_CachedObj.RAGUID] = tmp_CachedObj; else Objects.Add(tmp_CachedObj.RAGUID, tmp_CachedObj); } //End of Loop }// End of Framelock //Tally up unseen objects. var UnseenObjects = Objects.Keys.Where(O => !hashDoneThisRactor.Contains(O)).ToList(); if (UnseenObjects.Any()) { for (int i = 0; i < UnseenObjects.Count(); i++) { Objects[UnseenObjects[i]].LoopsUnseen++; } } //Trim our collection every 5th refresh. UpdateLoopCounter++; if (UpdateLoopCounter > 4) { UpdateLoopCounter = 0; //Now flag any objects not seen for 5 loops. Gold/Globe only 1 loop. foreach (var item in Objects.Values.Where(CO => (CO.LoopsUnseen >= 5 || //5 loops max.. (CO.targetType.HasValue && (CheckTargetTypeFlag(CO.targetType.Value, TargetType.Gold | TargetType.Globe)) && CO.LoopsUnseen > 0)))) //gold/globe only 1 loop! { item.NeedsRemoved = true; } } return true; }