private int GetTP(KPDatabaseDataSet.InteractionsRow ws, IEnumerable <KPDatabaseDataSet.ChatMessagesRow> tpEchoes) { if (ws == null) { throw new ArgumentNullException("ws"); } if (tpEchoes == null) { throw new ArgumentNullException("tpEchoes"); } // Find anything +/- 5 seconds from the ws var localTPSet = tpEchoes.Where(c => Math.Abs((c.Timestamp - ws.Timestamp).TotalSeconds) <= 5); // If none, no valid TP if (localTPSet.Count() == 0) { return(-1); } // If only one, return that if (localTPSet.Count() == 1) { return(GetTPValue(localTPSet.First().Message)); } // If there's more than one, sort them by how close they are to the weaponskill, // picking only those with validly parsed results. var proximityTPSet = from e in localTPSet let tpAmount = GetTPValue(e.Message) where tpAmount >= 0 && tpAmount < 100 orderby Math.Abs((e.Timestamp - ws.Timestamp).TotalSeconds) select e; // If no validly formatted results, return unknown. if (proximityTPSet.Count() == 0) { return(-1); } // Otherwise take the first (closest to weaponskill) echo in the list. return(GetTPValue(proximityTPSet.First().Message)); }
/// <summary> /// An extension method to determine whether a given InteractionsRow passes the filter /// check set by the specified MobFilter object. Checks the filter against the battle /// during which the action occurs /// </summary> /// <param name="mobFilter">The filter to check against.</param> /// <param name="rowToCheck">The InteractionsRow to check.</param> /// <returns>Returns true if the row passes the filter test, otherwise false.</returns> public static bool CheckFilterMobBattle(this MobFilter mobFilter, KPDatabaseDataSet.InteractionsRow rowToCheck) { if (mobFilter.AllMobs == true) { // If no battle attached, include it as "between battles" actions; valid if (rowToCheck.IsBattleIDNull() == true) { return(true); } if (mobFilter.Exclude0XPMobs) { return(rowToCheck.BattlesRow.ExperiencePoints > 0); } else { return(true); } } if (mobFilter.CustomSelection == true) { if (rowToCheck.IsBattleIDNull() == true) { return(false); } return(mobFilter.CustomBattleIDs.Contains(rowToCheck.BattleID)); } if (mobFilter.GroupMobs == false) { if ((rowToCheck.IsBattleIDNull() == false) && (rowToCheck.BattleID == mobFilter.FightNumber)) { return(true); } else { return(false); } } if (mobFilter.MobName == string.Empty) { return(false); } if (rowToCheck.IsBattleIDNull() == true) { return(false); } if (rowToCheck.BattlesRow.IsEnemyIDNull() == true) { return(false); } if (rowToCheck.BattlesRow.CombatantsRowByEnemyCombatantRelation.CombatantName == mobFilter.MobName) { if (mobFilter.MobXP == -1) { return(true); } if (mobFilter.MobXP == MobXPHandler.Instance.GetBaseXP(rowToCheck.BattleID)) { return(true); } } return(false); }
/// <summary> /// Process the attack set generated by the mob collection functions /// </summary> /// <param name="attackSet"></param> private void ProcessAttackSet(IEnumerable <AttackGroup> attackSet) { foreach (var player in attackSet) { var sataActions = player.Ability.Where( a => a.IsActionIDNull() == false && (a.ActionsRow.ActionName == lsParsedSneakAttack || a.ActionsRow.ActionName == lsParsedTrickAttack || a.ActionsRow.ActionName == lsParsedHide)); if (sataActions.Any()) { List <KPDatabaseDataSet.InteractionsRow> sataWeaponskills = new List <KPDatabaseDataSet.InteractionsRow>(); AppendText(player.Name + "\n", Color.Red, true, false); SATAEvents.Clear(); sataActions = sataActions.OrderBy(a => a.InteractionID); var avgNonCritSet = player.Melee.Where(m => ((DefenseType)m.DefenseType == DefenseType.None) && ((DamageModifier)m.DamageModifier == DamageModifier.None)); double avgNonCrit = 0; if (avgNonCritSet.Any()) { // Limit the average calculation to the first 200 hits var avgNonCritSubset = avgNonCritSet.Take(200); avgNonCrit = avgNonCritSubset.Average(m => m.Amount); } double critThreshold = avgNonCrit * 4; double nonCritThreshold = avgNonCrit * 2.75; while (sataActions.Any()) { var firstAction = sataActions.First(); sataActions = sataActions.Skip(1); SATATypes firstActionType = GetSATAType(firstAction.ActionsRow.ActionName); SATAEvent sataEvent = new SATAEvent(); sataEvent.SATAActions = new HashSet <SATATypes>(); SATAEvents.Add(sataEvent); sataEvent.SATAActions.Add(firstActionType); DateTime preTime = firstAction.Timestamp.AddSeconds(-4); DateTime postTime = firstAction.Timestamp.AddSeconds(4); DateTime minutePost = firstAction.Timestamp.AddMinutes(1); var nextMelee = player.Melee.FirstOrDefault(m => m.Timestamp >= firstAction.Timestamp && m.Timestamp <= firstAction.Timestamp.AddMinutes(1)); var nextWS = player.WSkill.FirstOrDefault(w => w.Timestamp >= firstAction.Timestamp && w.Timestamp <= firstAction.Timestamp.AddMinutes(1)); KPDatabaseDataSet.InteractionsRow sataDamage = null; // First check next melee hit for abnormally high values. // If present, indicates a successeful JA hit. if (sataDamage == null) { if (nextMelee != null) { if ((DamageModifier)nextMelee.DamageModifier == DamageModifier.Critical) { if (nextMelee.Amount > critThreshold) { sataDamage = nextMelee; sataEvent.ActionType = ActionType.Melee; sataEvent.SATASuccess = true; } } else { if (firstActionType == SATATypes.TrickAttack) { if (nextMelee.Amount > nonCritThreshold) { sataDamage = nextMelee; sataEvent.ActionType = ActionType.Melee; sataEvent.SATASuccess = true; } } } } } // If it's not the next melee hit, check all nearby melee hits // in case of out-of-order text. if (sataDamage == null) { var nearMelee = player.Melee.Where(m => m.Timestamp >= preTime && m.Timestamp <= postTime); var beforeMelee = nearMelee.TakeWhile(m => m.Timestamp < firstAction.Timestamp); var afterMelee = nearMelee.SkipWhile(m => m.Timestamp < firstAction.Timestamp); if (afterMelee.Any(m => ((DamageModifier)m.DamageModifier == DamageModifier.Critical && m.Amount > critThreshold) || (m.Amount > nonCritThreshold))) { sataDamage = afterMelee.First(m => ((DamageModifier)m.DamageModifier == DamageModifier.Critical && m.Amount > critThreshold) || (m.Amount > nonCritThreshold)); sataEvent.ActionType = ActionType.Melee; sataEvent.SATASuccess = true; } else if (beforeMelee.Any(m => ((DamageModifier)m.DamageModifier == DamageModifier.Critical && m.Amount > critThreshold) || (m.Amount > nonCritThreshold))) { sataDamage = beforeMelee.First(m => ((DamageModifier)m.DamageModifier == DamageModifier.Critical && m.Amount > critThreshold) || (m.Amount > nonCritThreshold)); } } // If it's not a melee hit, check for nearby weaponskills if (sataDamage == null) { var nearWS = player.WSkill.Where(m => m.Timestamp >= firstAction.Timestamp.AddSeconds(-4) && m.Timestamp <= firstAction.Timestamp.AddSeconds(4)); if (nearWS.Any()) { sataEvent.ActionType = ActionType.Weaponskill; sataEvent.SATASuccess = true; if (nearWS.Any(w => w.Timestamp >= firstAction.Timestamp)) { sataDamage = nearWS.Where(w => w.Timestamp >= firstAction.Timestamp).First(); } else { sataDamage = nearWS.First(); } } } // If no nearby weaponskills, look for weaponskill within the next minute if (sataDamage == null) { if (nextWS != null) { // But only if we have a melee action to check against if (nextMelee != null) { if ((nextWS.Timestamp < nextMelee.Timestamp) || (nextMelee.Timestamp == firstAction.Timestamp)) { sataDamage = nextWS; sataEvent.ActionType = ActionType.Weaponskill; sataEvent.SATASuccess = true; } } else { // If no melee, and we have a weaponskill within a minute, // use the weaponskill sataDamage = nextWS; sataEvent.ActionType = ActionType.Weaponskill; sataEvent.SATASuccess = true; } } } // No exception crits or normal hits, and no weaponskills. Must // be a miss, if there's a melee hit within the time limit. if (sataDamage == null) { if (nextMelee != null) { sataDamage = nextMelee; sataEvent.SATASuccess = false; } } if (sataDamage == null) { sataEvent.SATASuccess = false; sataEvent.ActionType = ActionType.Unknown; sataEvent.ActionName = lsFailed; } else { sataEvent.DamageTimestamp = sataDamage.Timestamp; sataEvent.ActionType = (ActionType)sataDamage.ActionType; if ((ActionType)sataDamage.ActionType == ActionType.Melee) { sataEvent.DamageModifier = (DamageModifier)sataDamage.DamageModifier; } else if ((ActionType)sataDamage.ActionType == ActionType.Weaponskill) { sataEvent.WeaponskillName = sataDamage.ActionsRow.ActionName; sataWeaponskills.Add(sataDamage); } sataEvent.DamageAmount = sataDamage.Amount; while (sataActions.Any()) { var nextAction = sataActions.First(); if ((nextAction.Timestamp <= sataDamage.Timestamp) || (nextAction.InteractionID < sataDamage.InteractionID)) { sataActions = sataActions.Skip(1); if ((nextAction.ActionsRow.ActionName == lsParsedHide) && (FailedActionType)nextAction.FailedActionType == FailedActionType.Discovered) { continue; } sataEvent.SATAActions.Add(GetSATAType(nextAction.ActionsRow.ActionName)); } else { break; } } if (sataEvent.SATAActions.Contains(SATATypes.Hide)) { sataEvent.UsedHide = true; } if ((DefenseType)sataDamage.DefenseType != DefenseType.None) { sataEvent.SATASuccess = false; } else if (sataEvent.SATAActions.Intersect(SATASet).Count() == 0) { sataEvent.SATASuccess = false; } sataEvent.ActionName = sataEvent.ActionType.ToString(); } } // Finished building event list // Now try to display data var SATAList = SATAEvents.Where(s => s.SATAActions.IsSupersetOf(SATASet)); var SAList = SATAEvents.Where(s => s.SATAActions.IsSupersetOf(SASet)).Except(SATAList); var TAList = SATAEvents.Where(s => s.SATAActions.IsSupersetOf(TASet)).Except(SATAList); PrintOutput(lsSneakAndTrick, SATAList); PrintOutput(lsSneakAttack, SAList); PrintOutput(lsTrickAttack, TAList); var soloWeaponskills = from w in player.WSkill.Except(sataWeaponskills) select new SATAEvent { ActionName = lsWeaponskill, DamageAmount = w.Amount, ActionType = ActionType.Weaponskill, WeaponskillName = w.ActionsRow.ActionName }; PrintOutput(lsSoloWS, soloWeaponskills); } } }