public void ParseLine(string line) { string[] lineContents = line.Split('|'); int.TryParse(lineContents[0], out int messageType); if ((LogMessageType)messageType != LogMessageType.NetworkAOEAbility) { firstAOELine = true; } switch ((LogMessageType)messageType) { case LogMessageType.LogLine: break; case LogMessageType.ChangeZone: zoneName = lineContents[3]; if (ZoneData.zoneInfo.ContainsKey(zoneName)) { bossInformation = ZoneData.zoneInfo[zoneName]; currentEncounter.zoneName = zoneName; currentEncounter.bosses = bossInformation; currentEncounter.combatants.Clear(); Debug.WriteLine($"Entered {zoneName}..."); } else { Debug.WriteLine($"Entered {zoneName}..."); currentEncounter = new Encounter(zoneName); } break; case LogMessageType.ChangePrimaryPlayer: break; case LogMessageType.AddCombatant: Combatant addCombatant = ReadCombatant(lineContents); currentEncounter.combatants.Add(addCombatant); if (addCombatant.ID.ToString("X8").StartsWith("10")) { if (!currentEncounter.partyMemberIDs.Contains(addCombatant.ID)) { currentEncounter.partyMemberIDs.Add(addCombatant.ID); } } foreach (BossInfo boss in currentEncounter.bosses) { if (boss.Name == addCombatant.Name && boss.Level == addCombatant.Level && (boss.MaxHP == addCombatant.Health.MaxHP || boss.MaxHP == addCombatant.Health.CurrentHP)) { currentEncounter.endedEncounter = false; } } break; case LogMessageType.RemoveCombatant: Combatant removeCombatant = ReadCombatant(lineContents); currentEncounter.combatants.Remove(removeCombatant); break; case LogMessageType.AddBuff: break; case LogMessageType.RemoveBuff: break; case LogMessageType.FlyingText: break; case LogMessageType.OutgoingAbility: break; case LogMessageType.IncomingAbility: break; case LogMessageType.PartyList: //If the encounter hasnt started yet form the party memebers //This is to avoid the struggles of someone dcing and changing the party size which could end up crashing this algorithm if (!string.IsNullOrEmpty(currentEncounter.zoneName) && !currentEncounter.startedEncounter) { currentEncounter.partyMemberIDs.Clear(); uint.TryParse(lineContents[2], out uint size); int start = 3; //Start of first memeber and going to start + size try { for (int i = 0; i < size; i++) { currentEncounter.partyMemberIDs.Add(Convert.ToUInt32(lineContents[start + i], 16)); } } catch (OverflowException ex) { Debug.WriteLine("Someone seemed to have disconnected!"); } } break; case LogMessageType.PlayerStats: break; case LogMessageType.CombatantHP: break; case LogMessageType.ParsedPartyMember: break; case LogMessageType.NetworkStartsCasting: NetworkAbilityCast networkAbilityCast = ReadNetworkCast(lineContents); currentEncounter.networkCastingAbilities.Add(networkAbilityCast); Combatant combatant = currentEncounter.GetCombatantFromID(networkAbilityCast.ActorID); //They started casting so we count this as a skill usage even if cancelled or interruped if (combatant != null && !currentEncounter.startedEncounter && currentEncounter.bosses.Any(boss => boss.Name == networkAbilityCast.TargetName)) { currentEncounter.events.Add(new ReportEvent { EventTime = networkAbilityCast.Timestamp.TimeOfDay, EventDescription = $"{networkAbilityCast.ActorName} casts {networkAbilityCast.SkillName} on {networkAbilityCast.TargetName}", EventType = EventType.Summary | EventType.DamageDone }); } if (combatant != null) { if (!combatant.AbilityInfo.ContainsKey(networkAbilityCast.SkillName)) { combatant.AbilityInfo.Add(networkAbilityCast.SkillName, new AbilityInfo()); } combatant.AbilityInfo[networkAbilityCast.SkillName].CastAmount++; } break; case LogMessageType.NetworkAbility: case LogMessageType.NetworkAOEAbility: if (bossInformation.Count == 0) { break; } NetworkAbility ability = ReadAbilityUsed(lineContents); combatant = currentEncounter.GetCombatantFromID(ability.ActorID); //Check if the ability used is a new skill if it is lets keep track of it from the combatant side //Itll be updated in the NetworkEffectResult since thats when the information is able to be deteced if (combatant != null) { if (!combatant.AbilityInfo.ContainsKey(ability.SkillName)) { combatant.AbilityInfo.Add(ability.SkillName, new AbilityInfo()); } if (((LogMessageType)messageType == LogMessageType.NetworkAOEAbility && firstAOELine) || (LogMessageType)messageType == LogMessageType.NetworkAbility) { combatant.AbilityInfo[ability.SkillName].CastAmount++; firstAOELine = false; } } if (!currentEncounter.startedEncounter && bossInformation.Any(boss => boss.Name == ability.TargetName)) { currentEncounter.startTime = ability.Timestamp; Debug.WriteLine($"Start of fight: {ability.Timestamp}"); currentEncounter.startedEncounter = true; currentEncounter.AdjustTimeSpans(); //Makes time negative since actions happened before the start } if (currentEncounter.startedEncounter) { currentEncounter.events.Add(new ReportEvent { EventTime = ability.Timestamp.Subtract(currentEncounter.startTime), EventDescription = $"{ability.ActorName} prepares {ability.SkillName} on {ability.TargetName} {ability.GetAbilityDamageInformation()}", EventType = EventType.Summary | (ability.AbilityState == AbilityState.Damage? EventType.DamageDone : EventType.Healing) }); currentEncounter.abilities.Add(ability); } break; case LogMessageType.NetworkCancelAbility: NetworkAbilityCancel networkAbilityCancel = ReadNetworkSkillCancel(lineContents); break; case LogMessageType.NetworkDoT: break; case LogMessageType.NetworkDeath: NetworkDeath death = ReadNetworkDeath(lineContents); if (currentEncounter.startedEncounter) { currentEncounter.events.Add(new ReportEvent { EventTime = death.Timestamp.Subtract(currentEncounter.startTime), EventDescription = $"{death.ActorName} dies." }); } break; case LogMessageType.NetworkBuff: NetworkBuff networkBuff = ReadNetworkBuff(lineContents); if (currentEncounter.startedEncounter) { currentEncounter.events.Add(new ReportEvent { EventTime = networkBuff.Timestamp.Subtract(currentEncounter.startTime), EventDescription = $"{networkBuff.TargetName} gains {networkBuff.SkillName} from {networkBuff.ActorName}", EventType = EventType.Summary }); } break; case LogMessageType.NetworkTargetIcon: break; case LogMessageType.NetworkTargetMarker: break; case LogMessageType.NetworkBuffRemove: break; case LogMessageType.NetworkGauge: break; case LogMessageType.NetworkWorld: break; case LogMessageType.Network6D: if (currentEncounter.startedEncounter) { if (lineContents[3] == "40000010") //Ending Wipe { currentEncounter.endedEncounter = true; currentEncounter.endTime = DateTime.Parse(lineContents[1]); encounters.Add(currentEncounter); Debug.WriteLine($"A wipe has occurred... It took {currentEncounter.endTime.Subtract(currentEncounter.startTime)}"); currentEncounter.DumpSummaryToFile(encounterDirectoryInfo); currentEncounter = (Encounter)currentEncounter.Clone(); } } break; case LogMessageType.NetworkNameToggle: break; case LogMessageType.NetworkTether: break; case LogMessageType.NetworkLimitBreak: LimitBreak limitBreak = ReadLimitBreak(lineContents); if (currentEncounter.startedEncounter) { currentEncounter.events.Add(new ReportEvent { EventTime = limitBreak.Timestamp.Subtract(currentEncounter.startTime), EventDescription = $"The limit break guage has been updated to {limitBreak.LimitBreakGuage}. {limitBreak.MaxLimitBreakNumber} bars are available.", EventType = EventType.Summary }); } break; case LogMessageType.NetworkEffectResult: NetworkEffectResult result = ReadNetworkEffectResult(lineContents); foreach (BossInfo boss in currentEncounter.bosses) { if (result.ActorName == boss.Name && result.Health.CurrentHP == 0 && boss.HasToDie) { boss.IsDead = true; } } //Figure out the damage the skill inflicted and then go back and credit the ability for that damage if (currentEncounter.startedEncounter && !currentEncounter.endedEncounter && currentEncounter.AreRequiredBossesDead()) { currentEncounter.endedEncounter = true; currentEncounter.endTime = result.Timestamp; currentEncounter.isCleared = true; encounters.Add(currentEncounter); Debug.WriteLine($"Encounter cleared!!! It took {currentEncounter.endTime.Subtract(currentEncounter.startTime)}"); currentEncounter.DumpSummaryToFile(encounterDirectoryInfo); currentEncounter = (Encounter)currentEncounter.Clone(); } if (currentEncounter.startedEncounter) { combatant = currentEncounter.GetCombatantFromID(result.ActorID); if (combatant != null) { int healthChange = combatant.Health - result.Health; combatant.Health.CurrentHP -= (uint)healthChange; NetworkAbility networkAbility = currentEncounter.abilities.Where(ability => ability.Damage == (uint)Math.Abs(healthChange)).FirstOrDefault(); if (networkAbility != null) { currentEncounter.events.Add(new ReportEvent { EventTime = result.Timestamp.Subtract(currentEncounter.startTime), EventDescription = $"{networkAbility.ActorName} {networkAbility.SkillName} {networkAbility.TargetName} {networkAbility.Damage}", EventType = EventType.Summary | (networkAbility.AbilityState == AbilityState.Damage ? EventType.DamageDone : EventType.Healing) }); currentEncounter.abilities.Remove(networkAbility); Combatant caster = currentEncounter.GetCombatantFromID(networkAbility.ActorID); AbilityInfo abilityInfo = caster.AbilityInfo[networkAbility.SkillName]; abilityInfo.HitCount++; if (networkAbility.AbilityState == AbilityState.Damage) { abilityInfo.DamageInformation.TotalDamageDone += (uint)healthChange; abilityInfo.DamageInformation.DPS = abilityInfo.DamageInformation.TotalDamageDone / (result.Timestamp.Subtract(currentEncounter.startTime).TotalSeconds); } else if (networkAbility.AbilityState == AbilityState.Healing) { abilityInfo.HealingInformation.TotalHealingDone += (uint)Math.Abs(healthChange); abilityInfo.HealingInformation.HPS = abilityInfo.HealingInformation.TotalHealingDone / (result.Timestamp.Subtract(currentEncounter.startTime).TotalSeconds); } } } } break; case LogMessageType.NetworkStatusList: NetworkStatusList networkStatusList = ReadNetworkStatusList(lineContents); if (currentEncounter.startedEncounter) { combatant = currentEncounter.GetCombatantFromID(networkStatusList.TargetID); if (combatant != null) { int healthChange = combatant.Health - networkStatusList.Health; combatant.Health.CurrentHP -= (uint)healthChange; } } foreach (BossInfo boss in currentEncounter.bosses) { if (networkStatusList.TargetName == boss.Name && networkStatusList.Health.CurrentHP == 0 && boss.HasToDie) { boss.IsDead = true; } } //Figure out the damage the skill inflicted and then go back and credit the ability for that damage if (currentEncounter.startedEncounter && !currentEncounter.endedEncounter && currentEncounter.AreRequiredBossesDead()) { currentEncounter.endedEncounter = true; currentEncounter.endTime = networkStatusList.Timestamp; currentEncounter.isCleared = true; encounters.Add(currentEncounter); Debug.WriteLine($"Encounter cleared!!! It took {currentEncounter.endTime.Subtract(currentEncounter.startTime)}"); currentEncounter.DumpSummaryToFile(encounterDirectoryInfo); currentEncounter = (Encounter)currentEncounter.Clone(); } break; case LogMessageType.NetworkUpdateHp: combatant = ReadNetworkUpdateHP(lineContents); if (currentEncounter.startedEncounter) { Combatant oldCombatant = currentEncounter.GetCombatantFromID(combatant.ID); if (oldCombatant != null) { oldCombatant.Health = combatant.Health; } } break; case LogMessageType.Settings: string[] settings = lineContents[2].Split(','); //Debug.WriteLine("ACT Settings:"); foreach (string setting in settings) { //Debug.WriteLine(setting.Trim()); } break; case LogMessageType.Process: break; case LogMessageType.Debug: break; case LogMessageType.PacketDump: break; case LogMessageType.Version: //Debug.WriteLine($"You are using {lineContents[2]}"); break; case LogMessageType.Error: break; case LogMessageType.Timer: break; } }