private static (long timeCasting, int casts, int timeSaved, int timeWasted) GetCastValues(IReadOnlyList <AbstractCastEvent> clList, PhaseData phase)
        {
            long timeCasting = 0;
            int  casts = 0, timeWasted = 0, timeSaved = 0;

            foreach (AbstractCastEvent cl in clList)
            {
                if (phase.InInterval(cl.Time))
                {
                    casts++;
                    switch (cl.Status)
                    {
                    case AbstractCastEvent.AnimationStatus.Interrupted:
                        timeWasted += cl.SavedDuration;
                        break;

                    case AbstractCastEvent.AnimationStatus.Reduced:
                        timeSaved += cl.SavedDuration;
                        break;
                    }
                }
                timeCasting += Math.Min(cl.EndTime, phase.End) - Math.Max(cl.Time, phase.Start);
            }
            return(timeCasting, casts, timeSaved, timeWasted);
        }
Example #2
0
        private static List <List <object> > GetMechanicChartPoints(IReadOnlyList <MechanicEvent> mechanicLogs, PhaseData phase, ParsedEvtcLog log, bool enemyMechanic)
        {
            var res = new List <List <object> >();

            if (!enemyMechanic)
            {
                var playerIndex = new Dictionary <AbstractSingleActor, int>();
                for (int p = 0; p < log.Friendlies.Count; p++)
                {
                    playerIndex.Add(log.Friendlies[p], p);
                    res.Add(new List <object>());
                }
                foreach (MechanicEvent ml in mechanicLogs.Where(x => phase.InInterval(x.Time)))
                {
                    double time = (ml.Time - phase.Start) / 1000.0;
                    if (playerIndex.TryGetValue(ml.Actor, out int p))
                    {
                        res[p].Add(time);
                    }
                }
            }
            else
            {
                var targetIndex = new Dictionary <AbstractSingleActor, int>();
                for (int p = 0; p < phase.Targets.Count; p++)
                {
                    targetIndex.Add(phase.Targets[p], p);
                    res.Add(new List <object>());
                }
                res.Add(new List <object>());
                foreach (MechanicEvent ml in mechanicLogs.Where(x => phase.InInterval(x.Time)))
                {
                    double time = (ml.Time - phase.Start) / 1000.0;
                    if (targetIndex.TryGetValue(ml.Actor, out int p))
                    {
                        res[p].Add(time);
                    }
                    else
                    {
                        res[res.Count - 1].Add(new object[] { time, ml.Actor.Character });
                    }
                }
            }
            return(res);
        }
Example #3
0
        public static List <List <object> > GetMechanicChartPoints(List <MechanicEvent> mechanicLogs, PhaseData phase, ParsedLog log, bool enemyMechanic)
        {
            List <List <object> > res = new List <List <object> >();

            if (!enemyMechanic)
            {
                Dictionary <DummyActor, int> playerIndex = new Dictionary <DummyActor, int>();
                for (var p = 0; p < log.PlayerList.Count; p++)
                {
                    playerIndex.Add(log.PlayerList[p], p);
                    res.Add(new List <object>());
                }
                foreach (MechanicEvent ml in mechanicLogs.Where(x => phase.InInterval(x.Time)))
                {
                    double time = (ml.Time - phase.Start) / 1000.0;
                    if (playerIndex.TryGetValue(ml.Actor, out int p))
                    {
                        res[p].Add(time);
                    }
                }
            }
            else
            {
                Dictionary <DummyActor, int> targetIndex = new Dictionary <DummyActor, int>();
                for (var p = 0; p < phase.Targets.Count; p++)
                {
                    targetIndex.Add(phase.Targets[p], p);
                    res.Add(new List <object>());
                }
                res.Add(new List <object>());
                foreach (MechanicEvent ml in mechanicLogs.Where(x => phase.InInterval(x.Time)))
                {
                    double time = (ml.Time - phase.Start) / 1000.0;
                    if (targetIndex.TryGetValue(ml.Actor, out int p))
                    {
                        res[p].Add(time);
                    }
                    else
                    {
                        res[res.Count - 1].Add(new object[] { time, ml.Actor.Character });
                    }
                }
            }
            return(res);
        }
 protected void AddTargetsToPhase(PhaseData phase, List <ushort> ids, ParsedLog log)
 {
     foreach (Boss target in Targets)
     {
         if (ids.Contains(target.ID) && phase.InInterval(target.FirstAware, log.FightData.FightStart))
         {
             phase.Targets.Add(target);
         }
     }
     phase.OverrideTimes(log.FightData.FightStart);
 }
 protected void AddTargetsToPhaseAndFit(PhaseData phase, List <int> ids, ParsedEvtcLog log)
 {
     foreach (NPC target in Targets)
     {
         if (ids.Contains(target.ID) && phase.InInterval(Math.Max(target.FirstAware, 0)))
         {
             phase.AddTarget(target);
         }
     }
     phase.OverrideTimes(log);
 }
 protected void AddTargetsToPhase(PhaseData phase, List <ushort> ids, ParsedLog log)
 {
     foreach (Target target in Targets)
     {
         if (ids.Contains(target.ID) && phase.InInterval(Math.Max(log.FightData.ToFightSpace(target.FirstAware), 0)))
         {
             phase.Targets.Add(target);
         }
     }
     phase.OverrideTimes(log);
 }
        private static List <int[]> GetMechanicData(IReadOnlyCollection <Mechanic> presMech, ParsedEvtcLog log, AbstractActor actor, PhaseData phase)
        {
            var res = new List <int[]>();

            foreach (Mechanic mech in presMech)
            {
                int filterCount = 0;
                int count       = 0;
                if (mech.InternalCooldown > 0)
                {
                    long timeFilter = 0;
                    var  mls        = log.MechanicData.GetMechanicLogs(log, mech).Where(x => x.Actor.Agent == actor.Agent).ToList();
                    foreach (MechanicEvent ml in mls)
                    {
                        bool inInterval = phase.InInterval(ml.Time);
                        if (ml.Time - timeFilter < mech.InternalCooldown)//ICD check
                        {
                            if (inInterval)
                            {
                                filterCount++;
                            }
                        }
                        timeFilter = ml.Time;
                        if (inInterval)
                        {
                            count++;
                        }
                    }
                }
                else
                {
                    count = log.MechanicData.GetMechanicLogs(log, mech).Where(x => x.Actor.Agent == actor.Agent && phase.InInterval(x.Time)).Count();
                }
                res.Add(new int[] { count - filterCount, count });
            }
            return(res);
        }
Example #8
0
        private void CreateMechanicTable(int phaseIndex)
        {
            HashSet <Mechanic> presMech = _log.MechanicData.GetPresentPlayerMechs(_log, phaseIndex);
            //Dictionary<string, HashSet<Mechanic>> presEnemyMech = log.MechanicData.getPresentEnemyMechs(phaseIndex);
            PhaseData phase = _phases[phaseIndex];
            //List<AbstractMasterPlayer> enemyList = log.MechanicData.getEnemyList(phaseIndex);
            int countLines = 0;

            if (presMech.Count > 0)
            {
                WriteCell("Name");
                foreach (Mechanic mech in presMech)
                {
                    WriteCell("\"" + mech.Description + "\"");
                }
                NewLine();

                foreach (Player p in _log.PlayerList)
                {
                    WriteCell(p.Character);
                    foreach (Mechanic mech in presMech)
                    {
                        int count = _log.MechanicData.GetMechanicLogs(_log, mech).Count(x => x.Actor.Agent == p.Agent && phase.InInterval(x.Time));
                        WriteCell(count.ToString());
                    }
                    NewLine();
                    countLines++;
                }
            }
            while (countLines < 15)//so each graph has equal spacing
            {
                NewLine();
                countLines++;
            }
        }
Example #9
0
        public static List <int[]> GetMechanicData(HashSet <Mechanic> presMech, ParsedLog log, AbstractActor actor, PhaseData phase)
        {
            var res = new List <int[]>();

            foreach (Mechanic mech in presMech)
            {
                long timeFilter  = 0;
                int  filterCount = 0;
                var  mls         = log.MechanicData.GetMechanicLogs(log, mech).Where(x => x.Actor.Agent == actor.Agent && phase.InInterval(x.Time)).ToList();
                int  count       = mls.Count;
                foreach (MechanicEvent ml in mls)
                {
                    if (mech.InternalCooldown != 0 && ml.Time - timeFilter < mech.InternalCooldown)//ICD check
                    {
                        filterCount++;
                    }
                    timeFilter = ml.Time;
                }
                res.Add(new int[] { count - filterCount, count });
            }
            return(res);
        }
Example #10
0
        internal override List <PhaseData> GetPhases(ParsedEvtcLog log, bool requirePhases)
        {
            List <PhaseData>    phases = GetInitialPhase(log);
            AbstractSingleActor ca     = Targets.FirstOrDefault(x => x.ID == (int)ArcDPSEnums.TargetID.ConjuredAmalgamate);

            if (ca == null)
            {
                throw new MissingKeyActorsException("Conjured Amalgamate not found");
            }
            phases[0].AddTarget(ca);
            if (!requirePhases)
            {
                return(phases);
            }
            phases.AddRange(GetPhasesByInvul(log, 52255, ca, true, false));
            for (int i = 1; i < phases.Count; i++)
            {
                string    name;
                PhaseData phase = phases[i];
                if (i % 2 == 1)
                {
                    name = "Arm Phase";
                }
                else
                {
                    name = "Burn Phase";
                    phase.AddTarget(ca);
                }
                phase.Name = name;
            }
            AbstractSingleActor leftArm = Targets.FirstOrDefault(x => x.ID == (int)ArcDPSEnums.TargetID.CALeftArm);

            if (leftArm != null)
            {
                List <long> targetables = GetTargetableTimes(log, leftArm);
                for (int i = 1; i < phases.Count; i += 2)
                {
                    PhaseData phase = phases[i];
                    if (targetables.Exists(x => phase.InInterval(x)))
                    {
                        phase.Name = "Left " + phase.Name;
                        phase.AddTarget(leftArm);
                    }
                }
            }
            AbstractSingleActor rightArm = Targets.FirstOrDefault(x => x.ID == (int)ArcDPSEnums.TargetID.CARightArm);

            if (rightArm != null)
            {
                List <long> targetables = GetTargetableTimes(log, rightArm);
                for (int i = 1; i < phases.Count; i += 2)
                {
                    PhaseData phase = phases[i];
                    if (targetables.Exists(x => phase.InInterval(x)))
                    {
                        if (phase.Name.Contains("Left"))
                        {
                            phase.Name = "Both Arms Phase";
                        }
                        else
                        {
                            phase.Name = "Right " + phase.Name;
                        }
                        phase.AddTarget(rightArm);
                    }
                }
            }
            return(phases);
        }
Example #11
0
        private static object[] GetDMGDtoItem(SkillItem skill, List <AbstractHealthDamageEvent> damageLogs, Dictionary <SkillItem, List <AbstractCastEvent> > castLogsBySkill, Dictionary <long, SkillItem> usedSkills, Dictionary <long, Buff> usedBoons, BuffsContainer boons, PhaseData phase)
        {
            int totaldamage       = 0,
                mindamage         = int.MaxValue,
                maxdamage         = int.MinValue,
                hits              = 0,
                crit              = 0,
                critDamage        = 0,
                connectedHits     = 0,
                flank             = 0,
                againstMoving     = 0,
                glance            = 0,
                shieldDamage      = 0;
            bool IsIndirectDamage = false;

            foreach (AbstractHealthDamageEvent dl in damageLogs)
            {
                IsIndirectDamage = IsIndirectDamage || dl is NonDirectHealthDamageEvent;
                int curdmg = dl.HealthDamage;
                totaldamage += curdmg;
                hits        += dl.DoubleProcHit ? 0 : 1;
                if (dl.HasHit)
                {
                    if (curdmg < mindamage)
                    {
                        mindamage = curdmg;
                    }
                    if (curdmg > maxdamage)
                    {
                        maxdamage = curdmg;
                    }
                    connectedHits++;
                    if (dl.HasCrit)
                    {
                        crit++;
                        critDamage += dl.HealthDamage;
                    }
                    if (dl.HasGlanced)
                    {
                        glance++;
                    }

                    if (dl.IsFlanking)
                    {
                        flank++;
                    }
                    if (dl.AgainstMoving)
                    {
                        againstMoving++;
                    }
                }

                shieldDamage += dl.ShieldDamage;
            }
            if (IsIndirectDamage)
            {
                if (!usedBoons.ContainsKey(skill.ID))
                {
                    if (boons.BuffsByIds.TryGetValue(skill.ID, out Buff buff))
                    {
                        usedBoons.Add(buff.ID, buff);
                    }
                    else
                    {
                        SkillItem aux     = skill;
                        var       auxBoon = new Buff(aux.Name, aux.ID, aux.Icon);
                        usedBoons.Add(auxBoon.ID, auxBoon);
                    }
                }
            }
            else
            {
                if (!usedSkills.ContainsKey(skill.ID))
                {
                    usedSkills.Add(skill.ID, skill);
                }
            }

            long timeCasting = 0;
            int  casts = 0, timeWasted = 0, timeSaved = 0;

            if (!IsIndirectDamage && castLogsBySkill != null && castLogsBySkill.TryGetValue(skill, out List <AbstractCastEvent> clList))
            {
                foreach (AbstractCastEvent cl in clList)
                {
                    if (phase.InInterval(cl.Time))
                    {
                        casts++;
                        switch (cl.Status)
                        {
                        case AbstractCastEvent.AnimationStatus.Interrupted:
                            timeWasted += cl.SavedDuration;
                            break;

                        case AbstractCastEvent.AnimationStatus.Reduced:
                            timeSaved += cl.SavedDuration;
                            break;
                        }
                    }
                    timeCasting += Math.Min(cl.EndTime, phase.End) - Math.Max(cl.Time, phase.Start);
                }
            }
            object[] skillItem =
            {
                IsIndirectDamage,
                skill.ID,
                totaldamage,
                mindamage == int.MaxValue ? 0 : mindamage,
                maxdamage == int.MinValue ? 0 : maxdamage,
                IsIndirectDamage ? 0 : casts,
                connectedHits,
                IsIndirectDamage ? 0 : crit,
                IsIndirectDamage ? 0 : flank,
                IsIndirectDamage ? 0 : glance,
                IsIndirectDamage ? 0 : -timeWasted / 1000.0,
                IsIndirectDamage ? 0 : timeSaved / 1000.0,
                shieldDamage,
                IsIndirectDamage ? 0 : critDamage,
                hits,
                IsIndirectDamage ? 0 : timeCasting,
                againstMoving
            };
            return(skillItem);
        }
Example #12
0
        private static List <object[]> BuildDMGDistBodyData(ParsedEvtcLog log, IReadOnlyList <AbstractCastEvent> casting, IReadOnlyList <AbstractHealthDamageEvent> damageLogs, Dictionary <long, SkillItem> usedSkills, Dictionary <long, Buff> usedBuffs, PhaseData phase)
        {
            var list              = new List <object[]>();
            var castLogsBySkill   = casting.GroupBy(x => x.Skill).ToDictionary(x => x.Key, x => x.ToList());
            var damageLogsBySkill = damageLogs.GroupBy(x => x.Skill).ToDictionary(x => x.Key, x => x.ToList());
            var conditionsById    = log.StatisticsHelper.PresentConditions.ToDictionary(x => x.ID);

            foreach (KeyValuePair <SkillItem, List <AbstractHealthDamageEvent> > pair in damageLogsBySkill)
            {
                list.Add(GetDMGDtoItem(pair.Key, pair.Value, castLogsBySkill, usedSkills, usedBuffs, log.Buffs, phase));
            }
            // non damaging
            foreach (KeyValuePair <SkillItem, List <AbstractCastEvent> > pair in castLogsBySkill)
            {
                if (damageLogsBySkill.ContainsKey(pair.Key))
                {
                    continue;
                }

                if (!usedSkills.ContainsKey(pair.Key.ID))
                {
                    usedSkills.Add(pair.Key.ID, pair.Key);
                }
                long timeCasting = 0;
                int  casts = 0;
                int  timeWasted = 0, timeSaved = 0;
                foreach (AbstractCastEvent cl in pair.Value)
                {
                    if (phase.InInterval(cl.Time))
                    {
                        casts++;
                        switch (cl.Status)
                        {
                        case AbstractCastEvent.AnimationStatus.Interrupted:
                            timeWasted += cl.SavedDuration;
                            break;

                        case AbstractCastEvent.AnimationStatus.Reduced:
                            timeSaved += cl.SavedDuration;
                            break;
                        }
                    }
                    timeCasting += Math.Min(cl.EndTime, phase.End) - Math.Max(cl.Time, phase.Start);
                }

                object[] skillData =
                {
                    false,
                    pair.Key.ID,
                    0,
                    -1,
                    0,
                    casts,
                    0,
                    0,
                    0,
                    0,
                    -timeWasted / 1000.0,
                    timeSaved / 1000.0,
                    0,
                    0,
                    0,
                    timeCasting,
                    0
                };
                list.Add(skillData);
            }
            return(list);
        }
        public override List <PhaseData> GetPhases(ParsedLog log, bool requirePhases)
        {
            List <PhaseData> phases = GetInitialPhase(log);
            NPC ca = Targets.Find(x => x.ID == (int)ParseEnum.TargetIDS.ConjuredAmalgamate);

            if (ca == null)
            {
                throw new InvalidOperationException("Error Encountered: Conjured Amalgamate not found");
            }
            phases[0].Targets.Add(ca);
            if (!requirePhases)
            {
                return(phases);
            }
            phases.AddRange(GetPhasesByInvul(log, 52255, ca, true, false));
            for (int i = 1; i < phases.Count; i++)
            {
                string    name;
                PhaseData phase = phases[i];
                if (i % 2 == 1)
                {
                    name = "Arm Phase";
                }
                else
                {
                    name = "Burn Phase";
                    phase.Targets.Add(ca);
                }
                phase.Name = name;
            }
            NPC leftArm = Targets.Find(x => x.ID == (int)ParseEnum.TargetIDS.CALeftArm);

            if (leftArm != null)
            {
                List <long> targetables = GetTargetableTimes(log, leftArm);
                for (int i = 1; i < phases.Count; i += 2)
                {
                    PhaseData phase = phases[i];
                    if (targetables.Exists(x => phase.InInterval(x)))
                    {
                        phase.Name = "Left " + phase.Name;
                        phase.Targets.Add(leftArm);
                    }
                }
            }
            NPC rightArm = Targets.Find(x => x.ID == (int)ParseEnum.TargetIDS.CARightArm);

            if (rightArm != null)
            {
                List <long> targetables = GetTargetableTimes(log, rightArm);
                for (int i = 1; i < phases.Count; i += 2)
                {
                    PhaseData phase = phases[i];
                    if (targetables.Exists(x => phase.InInterval(x)))
                    {
                        if (phase.Name.Contains("Left"))
                        {
                            phase.Name = "Both Arms Phase";
                        }
                        else
                        {
                            phase.Name = "Right " + phase.Name;
                        }
                        phase.Targets.Add(rightArm);
                    }
                }
            }
            return(phases);
        }
Example #14
0
        public override List <PhaseData> GetPhases(ParsedLog log, bool requirePhases)
        {
            long             start  = 0;
            long             end    = 0;
            List <PhaseData> phases = GetInitialPhase(log);
            Target           ca     = Targets.Find(x => x.ID == (ushort)ParseEnum.TargetIDS.ConjuredAmalgamate);

            if (ca == null)
            {
                throw new InvalidOperationException("Conjurate Amalgamate not found");
            }
            phases[0].Targets.Add(ca);
            if (!requirePhases)
            {
                return(phases);
            }
            List <CombatItem> CAInvul = GetFilteredList(log, 52255, ca, false);

            for (int i = 0; i < CAInvul.Count; i++)
            {
                CombatItem invul = CAInvul[i];
                if (invul.IsBuffRemove != ParseEnum.BuffRemove.None)
                {
                    end = Math.Min(log.FightData.ToFightSpace(invul.Time), log.FightData.FightDuration);
                    phases.Add(new PhaseData(start, end));
                    if (i == CAInvul.Count - 1)
                    {
                        phases.Add(new PhaseData(end, log.FightData.FightDuration));
                    }
                }
                else
                {
                    start = Math.Min(log.FightData.ToFightSpace(invul.Time), log.FightData.FightDuration);
                    phases.Add(new PhaseData(end, start));
                    if (i == CAInvul.Count - 1)
                    {
                        phases.Add(new PhaseData(start, log.FightData.FightDuration));
                    }
                }
            }
            phases.RemoveAll(x => x.GetDuration() < 1000);
            for (int i = 1; i < phases.Count; i++)
            {
                string    name;
                PhaseData phase = phases[i];
                if (i % 2 == 1)
                {
                    name = "Arm Phase";
                }
                else
                {
                    name = "Burn Phase";
                    phase.Targets.Add(ca);
                }
                phase.Name = name;
            }
            Target leftArm = Targets.Find(x => x.ID == (ushort)ParseEnum.TargetIDS.CALeftArm);

            if (leftArm != null)
            {
                List <long> leftArmDown = log.GetBoonData(52430).Where(x => x.IsBuffRemove == ParseEnum.BuffRemove.All && x.SrcInstid == leftArm.InstID).Select(x => log.FightData.ToFightSpace(x.Time)).ToList();
                for (int i = 1; i < phases.Count; i += 2)
                {
                    PhaseData phase = phases[i];
                    if (leftArmDown.Exists(x => phase.InInterval(x)))
                    {
                        phase.Name = "Left " + phase.Name;
                        phase.Targets.Add(leftArm);
                    }
                }
            }
            Target rightArm = Targets.Find(x => x.ID == (ushort)ParseEnum.TargetIDS.CARightArm);

            if (rightArm != null)
            {
                List <long> rightArmDown = log.GetBoonData(52430).Where(x => x.IsBuffRemove == ParseEnum.BuffRemove.All && x.SrcInstid == rightArm.InstID).Select(x => log.FightData.ToFightSpace(x.Time)).ToList();
                for (int i = 1; i < phases.Count; i += 2)
                {
                    PhaseData phase = phases[i];
                    if (rightArmDown.Exists(x => phase.InInterval(x)))
                    {
                        if (phase.Name.Contains("Left"))
                        {
                            phase.Name = "Both Arms Phase";
                        }
                        else
                        {
                            phase.Name = "Right " + phase.Name;
                        }
                        phase.Targets.Add(rightArm);
                    }
                }
            }
            return(phases);
        }
        private static object[] GetHealingToItem(SkillItem skill, List <EXTAbstractHealingEvent> healingLogs, Dictionary <SkillItem, List <AbstractCastEvent> > castLogsBySkill, Dictionary <long, SkillItem> usedSkills, Dictionary <long, Buff> usedBoons, BuffsContainer boons, PhaseData phase)
        {
            int totalhealing       = 0,
                totaldownedhealing = 0,
                minhealing         = int.MaxValue,
                maxhealing         = int.MinValue,
                hits = 0;
            bool isIndirectHealing = false;

            foreach (EXTAbstractHealingEvent dl in healingLogs)
            {
                isIndirectHealing = isIndirectHealing || dl is EXTNonDirectHealingEvent;
                int curdmg = dl.HealingDone;
                totalhealing += curdmg;
                hits++;
                if (curdmg < minhealing)
                {
                    minhealing = curdmg;
                }
                if (curdmg > maxhealing)
                {
                    maxhealing = curdmg;
                }
                if (dl.AgainstDowned)
                {
                    totaldownedhealing += dl.HealingDone;
                }
            }
            if (isIndirectHealing)
            {
                if (!usedBoons.ContainsKey(skill.ID))
                {
                    if (boons.BuffsByIds.TryGetValue(skill.ID, out Buff buff))
                    {
                        usedBoons.Add(buff.ID, buff);
                    }
                    else
                    {
                        SkillItem aux     = skill;
                        var       auxBoon = new Buff(aux.Name, aux.ID, aux.Icon);
                        usedBoons.Add(auxBoon.ID, auxBoon);
                    }
                }
            }
            else
            {
                if (!usedSkills.ContainsKey(skill.ID))
                {
                    usedSkills.Add(skill.ID, skill);
                }
            }
            if (castLogsBySkill != null && castLogsBySkill.ContainsKey(skill))
            {
                isIndirectHealing = false;
            }
            long timeCasting = 0;
            int  casts = 0, timeWasted = 0, timeSaved = 0;

            if (!isIndirectHealing && castLogsBySkill != null && castLogsBySkill.TryGetValue(skill, out List <AbstractCastEvent> clList))
            {
                foreach (AbstractCastEvent cl in clList)
                {
                    if (phase.InInterval(cl.Time))
                    {
                        casts++;
                        switch (cl.Status)
                        {
                        case AbstractCastEvent.AnimationStatus.Interrupted:
                            timeWasted += cl.SavedDuration;
                            break;

                        case AbstractCastEvent.AnimationStatus.Reduced:
                            timeSaved += cl.SavedDuration;
                            break;
                        }
                    }
                    timeCasting += Math.Min(cl.EndTime, phase.End) - Math.Max(cl.Time, phase.Start);
                }
            }
            object[] skillItem =
            {
                isIndirectHealing,
                skill.ID,
                totalhealing,
                minhealing == int.MaxValue ? 0 : minhealing,
                maxhealing == int.MinValue ? 0 : maxhealing,
                isIndirectHealing ? 0 : casts,
                isIndirectHealing ? 0 : -timeWasted / 1000.0,
                isIndirectHealing ? 0 : timeSaved / 1000.0,
                hits,
                isIndirectHealing ? 0 : timeCasting,
                totaldownedhealing
            };
            return(skillItem);
        }
 private IEnumerable <AbstractSingleActor> GetHPScarletPhantoms(PhaseData phase)
 {
     return(Targets.Where(x => (x.ID == (int)ArcDPSEnums.TrashID.ScarletPhantomHP || x.ID == (int)ArcDPSEnums.TrashID.ScarletPhantomHP2) && (phase.InInterval(x.FirstAware) || phase.InInterval(x.LastAware))));
 }
        public override List <PhaseData> GetPhases(ParsedLog log, bool requirePhases)
        {
            long             start  = 0;
            long             end    = 0;
            List <PhaseData> phases = GetInitialPhase(log);
            Boss             qadim  = Targets.Find(x => x.ID == (ushort)ParseEnum.BossIDS.Qadim);

            if (qadim == null)
            {
                throw new InvalidOperationException("Qadim not found");
            }
            phases[0].Targets.Add(qadim);
            if (!requirePhases)
            {
                return(phases);
            }
            List <long> moltenArmor = GetFilteredList(log, 52329, qadim).Select(x => x.Time - log.FightData.FightStart).Distinct().ToList();

            for (int i = 1; i < moltenArmor.Count; i++)
            {
                if (i % 2 == 0)
                {
                    end = Math.Min(moltenArmor[i], log.FightData.FightDuration);
                    phases.Add(new PhaseData(start, end));
                    if (i == moltenArmor.Count - 1)
                    {
                        phases.Add(new PhaseData(end, log.FightData.FightDuration));
                    }
                }
                else
                {
                    start = Math.Min(moltenArmor[i], log.FightData.FightDuration);
                    phases.Add(new PhaseData(end, start));
                    if (i == moltenArmor.Count - 1)
                    {
                        phases.Add(new PhaseData(start, log.FightData.FightDuration));
                    }
                }
            }
            string[] names = { "Hydra", "Qadim P1", "Apocalypse", "Qadim P2", "Wyvern", "Qadim P3" };
            for (int i = 1; i < phases.Count; i++)
            {
                PhaseData phase = phases[i];
                phase.Name = names[i - 1];
                switch (i)
                {
                case 2:
                case 4:
                case 6:
                    List <long> pyresFirstAware = log.AgentData.GetAgentsByID((ushort)PyreGuardian).Where(x => phase.InInterval(x.FirstAware - log.FightData.FightStart)).Select(x => x.FirstAware - log.FightData.FightStart).ToList();
                    if (pyresFirstAware.Count > 0 && pyresFirstAware.Max() > phase.Start)
                    {
                        phase.OverrideStart(pyresFirstAware.Max());
                    }
                    phase.Targets.Add(qadim);
                    break;

                default:
                    List <ushort> ids = new List <ushort>
                    {
                        (ushort)WyvernMatriarch,
                        (ushort)WyvernPatriarch,
                        (ushort)AncientInvokedHydra,
                        (ushort)ApocalypseBringer
                    };
                    AddTargetsToPhase(phase, ids, log);
                    phase.DrawArea  = true;
                    phase.DrawEnd   = true;
                    phase.DrawStart = true;
                    break;
                }
            }
            phases.RemoveAll(x => x.Start >= x.End);
            return(phases);
        }