internal static void UpdateStats(PlayerSubStats stats, HitRecord record, double beginTime) { switch (record.Type) { case Labels.BANE: stats.BaneHits++; stats.Hits += 1; break; case Labels.MISS: stats.Misses++; stats.MeleeAttempts += 1; break; case Labels.PROC: case Labels.DOT: case Labels.DD: case Labels.HOT: case Labels.HEAL: case Labels.DS: stats.Hits += 1; break; default: stats.Hits += 1; stats.MeleeHits++; stats.MeleeAttempts += 1; break; } if (record.Total > 0) { stats.Total += record.Total; stats.Max = Math.Max(stats.Max, record.Total); } if (record.Total > 0 && record.OverTotal > 0) { stats.Extra += (record.OverTotal - record.Total); } LineModifiersParser.Parse(record, stats); stats.BeginTime = double.IsNaN(stats.BeginTime) ? beginTime : stats.BeginTime; stats.LastTime = beginTime; }
private static DamageRecord ParseDamage(string actionPart) { DamageRecord record = null; ParseType parseType = ParseType.UNKNOWN; string withoutMods = actionPart; int modifiersIndex = -1; if (actionPart[actionPart.Length - 1] == ')') { // using 4 here since the shortest modifier should at least be 3 even in the future. probably. modifiersIndex = actionPart.LastIndexOf('(', actionPart.Length - 4); if (modifiersIndex > -1) { withoutMods = actionPart.Substring(0, modifiersIndex); } } int pointsIndex = -1; int forIndex = -1; int fromIndex = -1; int byIndex = -1; int takenIndex = -1; int hitIndex = -1; int extraIndex = -1; int isAreIndex = -1; bool nonMelee = false; List <string> nameList = new List <string>(); StringBuilder builder = new StringBuilder(); var data = withoutMods.Split(' '); SpellResist resist = SpellResist.UNDEFINED; for (int i = 0; i < data.Length; i++) { switch (data[i]) { case "taken": takenIndex = i; int test1 = i - 1; if (test1 > 0 && data[test1] == "has") { parseType = ParseType.HASTAKEN; int test2 = i + 2; if (data.Length > test2 && data[test2] == "extra" && data[test2 - 1] == "an") { extraIndex = test2; } } else if (test1 >= 1 && data[test1] == "have" && data[test1 - 1] == "You") { parseType = ParseType.YOUHAVETAKEN; } break; case "by": byIndex = i; break; case "non-melee": nonMelee = true; break; case "is": case "are": isAreIndex = i; break; case "for": int next = i + 1; if (data.Length > next && data[next].Length > 0 && char.IsNumber(data[next][0])) { forIndex = i; } break; case "from": fromIndex = i; break; case "points": int ofIndex = i + 1; if (ofIndex < data.Length && data[ofIndex] == "of") { parseType = ParseType.POINTSOF; pointsIndex = i; int resistIndex = ofIndex + 1; if (resistIndex < data.Length && SpellResistMap.TryGetValue(data[resistIndex], out SpellResist value)) { resist = value; nonMelee = true; } } break; default: if (HitMap.ContainsKey(data[i])) { hitIndex = i; } break; } } if (parseType == ParseType.POINTSOF && forIndex > -1 && forIndex < pointsIndex && hitIndex > -1) { record = ParsePointsOf(data, nonMelee, forIndex, byIndex, hitIndex, builder, nameList); } else if (parseType == ParseType.HASTAKEN && takenIndex < fromIndex && fromIndex > -1) { record = ParseHasTaken(data, takenIndex, fromIndex, byIndex, builder); } else if (parseType == ParseType.POINTSOF && extraIndex > -1 && takenIndex > -1 && takenIndex < fromIndex) { record = ParseExtra(data, takenIndex, extraIndex, fromIndex, nameList); } // there are more messages without a specificied attacker or spell but do these first else if (parseType == ParseType.YOUHAVETAKEN && takenIndex > -1 && fromIndex > -1 && byIndex > fromIndex) { record = ParseYouHaveTaken(data, takenIndex, fromIndex, byIndex, builder); } else if (parseType == ParseType.POINTSOF && isAreIndex > -1 && byIndex > isAreIndex && forIndex > byIndex) { record = ParseDS(data, isAreIndex, byIndex, forIndex); } if (record != null && modifiersIndex > -1) { record.ModifiersMask = LineModifiersParser.Parse(actionPart.Substring(modifiersIndex + 1, actionPart.Length - 1 - modifiersIndex - 1)); } return(ValidateDamage(record, resist)); }
private static DamageRecord ParseMiss(string actionPart, int index) { // [Mon Aug 05 02:05:12 2019] An enchanted Syldon stalker tries to crush YOU, but misses! (Strikethrough) // [Sat Aug 03 00:20:57 2019] You try to crush a Kar`Zok soldier, but miss! (Riposte Strikethrough) DamageRecord record = null; string withoutMods = actionPart; int modifiersIndex = -1; if (actionPart[actionPart.Length - 1] == ')') { // using 4 here since the shortest modifier should at least be 3 even in the future. probably. modifiersIndex = actionPart.LastIndexOf('(', actionPart.Length - 4); if (modifiersIndex > -1) { withoutMods = actionPart.Substring(0, modifiersIndex); } } int tryStartIndex; int hitStartIndex = -1; int missesIndex = index - LineParsing.ACTIONINDEX; if (withoutMods[0] == 'Y' && withoutMods[1] == 'o' && withoutMods[2] == 'u') { tryStartIndex = 3; hitStartIndex = 11; } else { tryStartIndex = withoutMods.IndexOf(" tries to ", StringComparison.Ordinal); if (tryStartIndex > -1) { hitStartIndex = tryStartIndex + 10; } } if (tryStartIndex > -1 && hitStartIndex > -1 && tryStartIndex < missesIndex) { int hitEndIndex = withoutMods.IndexOf(" ", hitStartIndex, StringComparison.Ordinal); if (hitEndIndex > -1) { string hit = withoutMods.Substring(hitStartIndex, hitEndIndex - hitStartIndex); string subType = GetTypeFromHit(hit, out bool additional); if (subType != null) { int hitLength = hit.Length + (additional ? 3 : 0); // check for pets string attacker = withoutMods.Substring(0, tryStartIndex); int defStart = hitStartIndex + hitLength + 1; int missesEnd = missesIndex - defStart; if (missesEnd > 0) { string defender = withoutMods.Substring(defStart, missesEnd); HasOwner(attacker, out string attackerOwner); HasOwner(defender, out string defenderOwner); if (attacker != null && defender != null) { record = BuildRecord(attacker, defender, 0, attackerOwner, defenderOwner, subType, Labels.MISS); } if (record != null && modifiersIndex > -1) { record.ModifiersMask = LineModifiersParser.Parse(actionPart.Substring(modifiersIndex + 1, actionPart.Length - 1 - modifiersIndex - 1)); } } } } } return(ValidateDamage(record)); }
private static HealRecord HandleHealed(ProcessLine pline) { // [Sun Feb 24 21:00:58 2019] Foob's promised interposition is fulfilled Foob healed himself for 44238 hit points by Promised Interposition Heal V. (Lucky Critical) // [Sun Feb 24 21:01:01 2019] Rowanoak is soothed by Brell's Soothing Wave. Farzi healed Rowanoak for 524 hit points by Brell's Sacred Soothing Wave. // [Sun Feb 24 21:00:52 2019] Kuvani healed Tolzol over time for 11000 hit points by Spirit of the Wood XXXIV. // [Sun Feb 24 21:00:52 2019] Kuvani healed Foob over time for 9409 (11000) hit points by Spirit of the Wood XXXIV. // [Sun Feb 24 21:00:58 2019] Fllint healed Foob for 11820 hit points by Blessing of the Ancients III. // [Sun Feb 24 21:01:00 2019] Tolzol healed itself for 548 hit points. // [Sun Feb 24 21:01:01 2019] Piemastaj`s pet has been healed for 15000 hit points by Enhanced Theft of Essence Effect X. // [Sun Feb 24 23:30:51 2019] Piemastaj`s pet glows with holy light. Findawenye healed Piemastaj`s pet for 2823 (78079) hit points by Mending Splash Rk. III. (Critical) // [Mon Feb 18 21:21:12 2019] Nylenne has been healed over time for 8211 hit points by Roar of the Lion 6. // [Mon Feb 18 21:20:39 2019] You have been healed over time for 1063 (8211) hit points by Roar of the Lion 6. // [Mon Feb 18 21:17:35 2019] Snowzz healed Malkatar over time for 8211 hit points by Roar of the Lion 6. // [Wed Nov 06 14:19:54 2019] Your ward heals you as it breaks! You healed Niktaza for 8970 (86306) hit points by Healing Ward. (Critical) HealRecord record = null; string part = pline.ActionPart; int optional = pline.OptionalIndex; string test = part.Substring(0, optional); bool done = false; string healer = ""; string healed = ""; string spell = null; string type = Labels.HEAL; uint heal = 0; uint overHeal = 0; int previous = test.Length >= 2 ? test.LastIndexOf(" ", test.Length - 2, StringComparison.Ordinal) : -1; if (previous > -1) { if (test.IndexOf("are ", previous + 1, StringComparison.Ordinal) > -1) { done = true; } else if (previous - 1 >= 0 && (test[previous - 1] == '.' || test[previous - 1] == '!') || previous - 9 > 0 && test.IndexOf("fulfilled", previous - 9, StringComparison.Ordinal) > -1) { healer = test.Substring(previous + 1); } else if (previous - 4 >= 0 && test.IndexOf("has been", previous - 3, StringComparison.Ordinal) > -1) { healed = test.Substring(0, previous - 4); if (part.Length > optional + 17 && part.IndexOf("over time", optional + 8, 9, StringComparison.Ordinal) > -1) { type = Labels.HOT; } } else if (previous - 5 >= 0 && test.IndexOf("have been", previous - 4, StringComparison.Ordinal) > -1) { healed = test.Substring(0, previous - 5); if (part.Length > optional + 17 && part.IndexOf("over time", optional + 8, 9, StringComparison.Ordinal) > -1) { type = Labels.HOT; } } } else { healer = test.Substring(0, optional); } if (!done) { int amountIndex = -1; if (healed.Length == 0) { int afterHealed = optional + 8; int forIndex = part.IndexOf(" for ", afterHealed, StringComparison.Ordinal); if (forIndex > 1) { if (forIndex - 9 >= 0 && part.IndexOf("over time", forIndex - 9, StringComparison.Ordinal) > -1) { type = Labels.HOT; healed = part.Substring(afterHealed, forIndex - afterHealed - 10); } else { healed = part.Substring(afterHealed, forIndex - afterHealed); } amountIndex = forIndex + 5; } } else { if (type == Labels.HEAL) { amountIndex = optional + 12; } else if (type == Labels.HOT) { amountIndex = optional + 22; } } if (amountIndex > -1) { int amountEnd = part.IndexOf(" ", amountIndex, StringComparison.Ordinal); if (amountEnd > -1) { uint value = StatsUtil.ParseUInt(part.Substring(amountIndex, amountEnd - amountIndex)); if (value != uint.MaxValue) { heal = value; } int overEnd = -1; if (part.Length > amountEnd + 1 && part[amountEnd + 1] == '(') { overEnd = part.IndexOf(")", amountEnd + 2, StringComparison.Ordinal); if (overEnd > -1) { uint value2 = StatsUtil.ParseUInt(part.Substring(amountEnd + 2, overEnd - amountEnd - 2)); if (value2 != uint.MaxValue) { overHeal = value2; } } } int rest = overEnd > -1 ? overEnd : amountEnd; int byIndex = part.IndexOf(" by ", rest, StringComparison.Ordinal); if (byIndex > -1) { int periodIndex = part.LastIndexOf(".", StringComparison.Ordinal); if (periodIndex > -1 && periodIndex - byIndex - 4 > 0) { spell = part.Substring(byIndex + 4, periodIndex - byIndex - 4); } } } } if (healed.Length > 0) { // check for pets int possessive = healed.IndexOf("`s ", StringComparison.Ordinal); if (possessive > -1) { if (PlayerManager.Instance.IsVerifiedPlayer(healed.Substring(0, possessive))) { PlayerManager.Instance.AddVerifiedPet(healed); } // dont count swarm pets healer = ""; heal = 0; } if (healer.Length > 0 && heal != 0) { record = new HealRecord() { Total = heal, OverTotal = overHeal, Healer = string.Intern(healer), Healed = string.Intern(healed), Type = string.Intern(type), ModifiersMask = -1 }; record.SubType = string.IsNullOrEmpty(spell) ? Labels.SELFHEAL : string.Intern(spell); if (part[part.Length - 1] == ')') { // using 4 here since the shortest modifier should at least be 3 even in the future. probably. int firstParen = part.LastIndexOf('(', part.Length - 4); if (firstParen > -1) { record.ModifiersMask = LineModifiersParser.Parse(part.Substring(firstParen + 1, part.Length - 1 - firstParen - 1)); if (LineModifiersParser.IsTwincast(record.ModifiersMask)) { PlayerManager.Instance.AddVerifiedPlayer(record.Healer); } } } } } } return(record); }
private static bool CreateDamageRecord(LineData lineData, string[] split, int stop, string attacker, string defender, uint damage, string type, string subType, SpellResist resist = SpellResist.UNDEFINED, bool attackerIsSpell = false) { bool success = false; if (damage != uint.MaxValue && !string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(subType) && !InIgnoreList(defender)) { // Needed to replace 'You' and 'you', etc defender = PlayerManager.Instance.ReplacePlayer(defender, defender); if (string.IsNullOrEmpty(attacker)) { attacker = subType; } else if (attacker.EndsWith("'s corpse", StringComparison.Ordinal)) { attacker = attacker.Substring(0, attacker.Length - 9); } else { // Needed to replace 'You' and 'you', etc attacker = PlayerManager.Instance.ReplacePlayer(attacker, attacker); } if (resist != SpellResist.UNDEFINED && ConfigUtil.PlayerName == attacker && defender != attacker) { DataManager.Instance.UpdateNpcSpellResistStats(defender, resist); } // check for pets HasOwner(attacker, out string attackerOwner); HasOwner(defender, out string defenderOwner); DamageRecord record = new DamageRecord { Attacker = string.Intern(FixName(attacker)), Defender = string.Intern(FixName(defender)), Type = string.Intern(type), SubType = string.Intern(subType), Total = damage, AttackerOwner = attackerOwner != null?string.Intern(attackerOwner) : null, DefenderOwner = defenderOwner != null?string.Intern(defenderOwner) : null, ModifiersMask = -1, AttackerIsSpell = attackerIsSpell }; var currentTime = DateUtil.ParseLogDate(lineData.Line, out string timeString); if (split.Length > stop + 1) { // improve this later so maybe the string doesn't have to be re-joined string modifiers = string.Join(" ", split, stop + 1, split.Length - stop - 1); record.ModifiersMask = LineModifiersParser.Parse(record.Attacker, modifiers.Substring(1, modifiers.Length - 2), currentTime); } if (!double.IsNaN(currentTime)) { // handle old style crits for eqemu if (LastCrit != null && LastCrit.Attacker == record.Attacker && LastCrit.LineData.LineNumber == (lineData.LineNumber - 1)) { var critTime = DateUtil.ParseLogDate(LastCrit.LineData.Line, out string _); if (!double.IsNaN(critTime) && (currentTime - critTime) <= 1) { record.ModifiersMask = (record.ModifiersMask == -1) ? LineModifiersParser.CRIT : record.ModifiersMask | LineModifiersParser.CRIT; } LastCrit = null; } CheckSlainQueue(currentTime); DamageProcessedEvent e = new DamageProcessedEvent() { Record = record, OrigTimeString = timeString, BeginTime = currentTime }; EventsDamageProcessed?.Invoke(record, e); success = true; if (record.Type == Labels.DD && SpecialCodes.Keys.FirstOrDefault(special => !string.IsNullOrEmpty(record.SubType) && record.SubType.Contains(special)) is string key && !string.IsNullOrEmpty(key)) { DataManager.Instance.AddSpecial(new SpecialSpell() { Code = SpecialCodes[key], Player = record.Attacker, BeginTime = currentTime }); } } } return(success); }