Example #1
0
        /// <summary>
        /// Calculates statistics for an encounter, such as damage done...
        /// </summary>
        /// <param name="log">The processed log.</param>
        /// <param name="apiData">Data from the GW2 API, may be null, some statistics won't be calculated.</param>
        public LogStatistics GetStatistics(Log log, GW2ApiData apiData)
        {
            var encounter = GetEncounter(log);

            var phaseStats = new List <PhaseStats>();

            var might         = log.Skills.FirstOrDefault(x => x.Id == SkillIds.Might);
            var vulnerability = log.Skills.FirstOrDefault(x => x.Id == SkillIds.Vulnerability);

            if (might != null)
            {
                BuffSimulator.TrackBuff(might, BuffSimulationType.Intensity, 25);
            }
            if (vulnerability != null)
            {
                BuffSimulator.TrackBuff(vulnerability, BuffSimulationType.Intensity, 25);
            }

            var buffData = BuffSimulator.SimulateBuffs(log.Agents, log.Events.OfType <BuffEvent>(),
                                                       encounter.GetPhases().Last().EndTime);

            var players = GetPlayers(log).ToArray();

            foreach (var phase in encounter.GetPhases())
            {
                var  targetDamageData = new List <TargetSquadDamageData>();
                long phaseDuration    = phase.EndTime - phase.StartTime;
                foreach (var target in phase.ImportantEnemies)
                {
                    var damageData = GetDamageData(
                        players,
                        phase.Events
                        .OfType <DamageEvent>()
                        .Where(x => x.Defender == target),
                        phase.Events.OfType <SkillCastEvent>(),
                        buffData,
                        log.Skills,
                        phaseDuration);
                    targetDamageData.Add(new TargetSquadDamageData(target, phaseDuration, damageData.Values));
                }

                var totalDamageData = new SquadDamageData(phaseDuration,
                                                          GetDamageData(
                                                              players,
                                                              phase.Events.OfType <DamageEvent>(),
                                                              phase.Events.OfType <SkillCastEvent>(),
                                                              buffData,
                                                              log.Skills,
                                                              phaseDuration).Values
                                                          );

                phaseStats.Add(new PhaseStats(phase.Name, phase.StartTime, phase.EndTime, targetDamageData,
                                              totalDamageData));
            }

            var fightTime = encounter.GetPhases().Sum(x => x.EndTime - x.StartTime);
            var fullFightSquadDamageData = new SquadDamageData(fightTime,
                                                               GetDamageData(players, log.Events.OfType <DamageEvent>(),
                                                                             log.Events.OfType <SkillCastEvent>(), buffData, log.Skills, fightTime).Values);

            var fullFightTargetDamageData = new List <TargetSquadDamageData>();

            foreach (var target in encounter.ImportantEnemies)
            {
                var damageData = GetDamageData(
                    players,
                    log.Events
                    .OfType <DamageEvent>()
                    .Where(x => x.Defender == target),
                    log.Events.OfType <SkillCastEvent>(),
                    buffData,
                    log.Skills,
                    fightTime);
                fullFightTargetDamageData.Add(new TargetSquadDamageData(target, fightTime, damageData.Values));
            }

            var eventCounts = new Dictionary <Type, int>();

            foreach (var e in log.Events)
            {
                var type = e.GetType();
                if (!eventCounts.ContainsKey(type))
                {
                    eventCounts[type] = 0;
                }

                eventCounts[type]++;
            }

            var eventCountsByName =
                eventCounts.Select(x => (x.Key.Name, x.Value)).ToDictionary(x => x.Item1, x => x.Item2);

            var playerData = GetPlayerData(log, apiData);

            return(new LogStatistics(log.StartTime.ServerTime, log.PointOfView, playerData, phaseStats, fullFightSquadDamageData,
                                     fullFightTargetDamageData, buffData, encounter.GetResult(), encounter.GetName(),
                                     log.EVTCVersion, eventCountsByName, log.Agents, log.Skills));
        }
Example #2
0
 /// <summary>
 /// Creates a new instance of a log analyzer for a provided log.
 /// </summary>
 /// <param name="log">The processed log that will be analyzed.</param>
 /// <param name="apiData">Data from the GW2 API, may be null, some statistics won't be calculated.</param>
 public LogAnalyzer(Log log, GW2ApiData apiData = null)
 {
     this.log = log;
     ApiData  = apiData;
 }
Example #3
0
        private IEnumerable <PlayerData> GetPlayerData(Log log, GW2ApiData apiData)
        {
            var players = log.Agents.OfType <Player>().ToArray();

            var deathCounts = players.ToDictionary(x => x, x => 0);
            var downCounts  = players.ToDictionary(x => x, x => 0);
            var usedSkills  = players.ToDictionary(x => x, x => new HashSet <Skill>());

            foreach (var deadEvent in log.Events.OfType <AgentDeadEvent>().Where(x => x.Agent is Player))
            {
                var player = (Player)deadEvent.Agent;
                deathCounts[player]++;
            }

            foreach (var downEvent in log.Events.OfType <AgentDownedEvent>().Where(x => x.Agent is Player))
            {
                var player = (Player)downEvent.Agent;
                downCounts[player]++;
            }

            // Buff damage events only tell us which conditions/buffs, not what skill actually applied them
            foreach (var damageEvent in log.Events.OfType <PhysicalDamageEvent>().Where(x => x.Attacker is Player))
            {
                var player = (Player)damageEvent.Attacker;
                usedSkills[player].Add(damageEvent.Skill);
            }

            foreach (var activationEvent in log.Events.OfType <SkillCastEvent>().Where(x => x.Agent is Player))
            {
                var player = (Player)activationEvent.Agent;
                usedSkills[player].Add(activationEvent.Skill);
            }

            var playerData = new List <PlayerData>();

            foreach (var player in players)
            {
                HashSet <SkillData> utilitySkills = null;
                HashSet <SkillData> healingSkills = null;
                HashSet <SkillData> eliteSkills   = null;
                if (apiData != null)
                {
                    utilitySkills = new HashSet <SkillData>();
                    healingSkills = new HashSet <SkillData>();
                    eliteSkills   = new HashSet <SkillData>();
                    foreach (var usedSkill in usedSkills[player])
                    {
                        var skillData = apiData.GetSkillData(usedSkill);

                        // Skills may be also registered as used if they affect other players and do damage through them
                        if (skillData != null && skillData.Professions.Contains(player.Profession))
                        {
                            if (skillData.Slot == SkillSlot.Elite)
                            {
                                eliteSkills.Add(skillData);
                            }
                            else if (skillData.Slot == SkillSlot.Utility)
                            {
                                utilitySkills.Add(skillData);
                            }
                            else if (skillData.Slot == SkillSlot.Heal)
                            {
                                healingSkills.Add(skillData);
                            }
                        }
                    }
                }

                WeaponType land1Weapon1 = WeaponType.Other;
                WeaponType land1Weapon2 = WeaponType.Other;
                WeaponType land2Weapon1 = WeaponType.Other;
                WeaponType land2Weapon2 = WeaponType.Other;
                IEnumerable <SkillData> land1WeaponSkills = null;
                IEnumerable <SkillData> land2WeaponSkills = null;

                // TODO: Dual wield skill handling for Thieves
                if (apiData != null)
                {
                    WeaponSet currentWeaponSet = WeaponSet.Unknown;
                    // We are only interested in land weapons. This may be imperfect if started on an underwater set.
                    var firstWeaponSwap = log.Events.OfType <AgentWeaponSwapEvent>().FirstOrDefault(x =>
                                                                                                    x.NewWeaponSet == WeaponSet.Land1 || x.NewWeaponSet == WeaponSet.Land2);

                    if (firstWeaponSwap == null)
                    {
                        currentWeaponSet = WeaponSet.Land1;
                    }
                    else
                    {
                        // First weapon set is the other one than the first swap swaps to (unless it was an underwater one)
                        currentWeaponSet = firstWeaponSwap.NewWeaponSet == WeaponSet.Land1
                                                        ? WeaponSet.Land2
                                                        : WeaponSet.Land1;
                    }

                    foreach (var logEvent in log.Events)
                    {
                        if (logEvent is AgentWeaponSwapEvent weaponSwapEvent && weaponSwapEvent.Agent == player)
                        {
                            currentWeaponSet = weaponSwapEvent.NewWeaponSet;
                            continue;
                        }

                        SkillData skillData = null;
                        if (logEvent is StartSkillCastEvent castEvent && castEvent.Agent == player)
                        {
                            skillData = apiData.GetSkillData(castEvent.Skill);
                        }

                        if (skillData != null)
                        {
                            if (skillData.Professions.Contains(player.Profession) && skillData.Type == SkillType.Weapon)
                            {
                                if (skillData.WeaponType.IsTwoHanded() || skillData.Slot == SkillSlot.Weapon1 ||
                                    skillData.Slot == SkillSlot.Weapon2 || skillData.Slot == SkillSlot.Weapon3)
                                {
                                    if (currentWeaponSet == WeaponSet.Land1)
                                    {
                                        land1Weapon1 = skillData.WeaponType;
                                    }
                                    else if (currentWeaponSet == WeaponSet.Land2)
                                    {
                                        land2Weapon1 = skillData.WeaponType;
                                    }
                                }

                                if (skillData.WeaponType.IsTwoHanded() || skillData.Slot == SkillSlot.Weapon4 ||
                                    skillData.Slot == SkillSlot.Weapon5)
                                {
                                    if (currentWeaponSet == WeaponSet.Land1)
                                    {
                                        land1Weapon2 = skillData.WeaponType;
                                    }
                                    else if (currentWeaponSet == WeaponSet.Land2)
                                    {
                                        land2Weapon2 = skillData.WeaponType;
                                    }
                                }
                            }
                        }
                    }

                    land1WeaponSkills = GameSkillDataRepository
                                        .GetWeaponSkillIds(player.Profession, land1Weapon1, land1Weapon2)
                                        .Select(x => x == -1 ? null : apiData.GetSkillData(x));
                    land2WeaponSkills = GameSkillDataRepository
                                        .GetWeaponSkillIds(player.Profession, land2Weapon1, land2Weapon2)
                                        .Select(x => x == -1 ? null : apiData.GetSkillData(x));
                }

                if (apiData != null)
                {
                    var skillDetections = SkillDetections.GetSkillDetections(player.Profession).ToArray();
                    foreach (var e in log.Events)
                    {
                        foreach (var detection in skillDetections)
                        {
                            if (detection.Detection.IsDetected(player, e))
                            {
                                var skill = apiData.GetSkillData(detection.SkillId);
                                if (detection.Slot == SkillSlot.Utility)
                                {
                                    utilitySkills.Add(skill);
                                }
                                else if (detection.Slot == SkillSlot.Heal)
                                {
                                    healingSkills.Add(skill);
                                }
                                else if (detection.Slot == SkillSlot.Elite)
                                {
                                    eliteSkills.Add(skill);
                                }
                            }
                        }
                    }
                }

                var ignoredSkills = SkillDetections.GetIgnoredSkillIds(player.Profession);
                healingSkills?.RemoveWhere(x => ignoredSkills.Contains(x.Id));
                utilitySkills?.RemoveWhere(x => ignoredSkills.Contains(x.Id));
                eliteSkills?.RemoveWhere(x => ignoredSkills.Contains(x.Id));

                var specializationDetections =
                    SpecializationDetections.GetSpecializationDetections(player.Profession).ToArray();
                var badges = new List <PlayerBadge>();

                var specializations = new HashSet <CoreSpecialization>();
                foreach (var e in log.Events)
                {
                    foreach (var detection in specializationDetections)
                    {
                        if (detection.Detection.IsDetected(player, e))
                        {
                            specializations.Add(detection.Specialization);
                        }
                    }
                }

                foreach (var spec in specializations.OrderBy(x => x.ToString()))
                {
                    badges.Add(new PlayerBadge(spec.ToString(), BadgeType.Specialization));
                }

                var rotation = RotationCalculator.GetRotation(log, player);

                var data = new PlayerData(player, downCounts[player], deathCounts[player], rotation, usedSkills[player],
                                          healingSkills, utilitySkills, eliteSkills, land1Weapon1, land1Weapon2, land2Weapon1, land2Weapon2,
                                          land1WeaponSkills, land2WeaponSkills, badges);

                playerData.Add(data);
            }

            return(playerData);
        }