Ejemplo n.º 1
0
        private void CalculateConditions()
        {
            foreach (Target target in _log.FightData.Logic.Targets)
            {
                Dictionary <long, FinalTargetBoon>[] stats = new Dictionary <long, FinalTargetBoon> [_statistics.Phases.Count];
                for (int phaseIndex = 0; phaseIndex < _statistics.Phases.Count; phaseIndex++)
                {
                    BoonDistribution boonDistribution                = target.GetBoonDistribution(_log, phaseIndex);
                    Dictionary <long, FinalTargetBoon> rates         = new Dictionary <long, FinalTargetBoon>();
                    Dictionary <long, long>            boonPresence  = target.GetBoonPresence(_log, phaseIndex);
                    Dictionary <long, long>            condiPresence = target.GetCondiPresence(_log, phaseIndex);

                    PhaseData phase         = _statistics.Phases[phaseIndex];
                    long      fightDuration = phase.GetDuration();

                    foreach (Boon boon in target.BoonToTrack)
                    {
                        FinalTargetBoon condition = new FinalTargetBoon(_log.PlayerList);
                        rates[boon.ID] = condition;
                        if (boonDistribution.ContainsKey(boon.ID))
                        {
                            if (boon.Type == Boon.BoonType.Duration)
                            {
                                condition.Uptime = Math.Round(100.0 * boonDistribution.GetUptime(boon.ID) / fightDuration, 1);
                                foreach (Player p in _log.PlayerList)
                                {
                                    long gen = boonDistribution.GetGeneration(boon.ID, p.InstID);
                                    condition.Generated[p]   = Math.Round(100.0 * gen / fightDuration, 1);
                                    condition.Overstacked[p] = Math.Round(100.0 * (boonDistribution.GetOverstack(boon.ID, p.InstID) + gen) / fightDuration, 1);
                                }
                            }
                            else if (boon.Type == Boon.BoonType.Intensity)
                            {
                                condition.Uptime = Math.Round((double)boonDistribution.GetUptime(boon.ID) / fightDuration, 1);
                                foreach (Player p in _log.PlayerList)
                                {
                                    long gen = boonDistribution.GetGeneration(boon.ID, p.InstID);
                                    condition.Generated[p]   = Math.Round((double)gen / fightDuration, 1);
                                    condition.Overstacked[p] = Math.Round((double)(boonDistribution.GetOverstack(boon.ID, p.InstID) + gen) / fightDuration, 1);
                                }
                                if (boonPresence.TryGetValue(boon.ID, out long presenceValueBoon))
                                {
                                    condition.Presence = Math.Round(100.0 * presenceValueBoon / fightDuration, 1);
                                }
                                else if (condiPresence.TryGetValue(boon.ID, out long presenceValueCondi))
                                {
                                    condition.Presence = Math.Round(100.0 * presenceValueCondi / fightDuration, 1);
                                }
                            }

                            rates[boon.ID] = condition;
                        }
                    }
                    stats[phaseIndex] = rates;
                }
                _statistics.TargetConditions[target] = stats;
            }
        }
Ejemplo n.º 2
0
 public PhaseDto(PhaseData phaseData, List <PhaseData> phases, ParsedLog log)
 {
     Name     = phaseData.Name;
     Duration = phaseData.GetDuration();
     Start    = phaseData.Start / 1000.0;
     End      = phaseData.End / 1000.0;
     foreach (Target target in phaseData.Targets)
     {
         Targets.Add(log.FightData.Logic.Targets.IndexOf(target));
     }
     // add phase markup
     MarkupLines = new List <double>();
     MarkupAreas = new List <AreaLabelDto>();
     for (int j = 1; j < phases.Count; j++)
     {
         PhaseData curPhase = phases[j];
         if (curPhase.Start < phaseData.Start || curPhase.End > phaseData.End ||
             (curPhase.Start == phaseData.Start && curPhase.End == phaseData.End))
         {
             continue;
         }
         if (SubPhases == null)
         {
             SubPhases = new List <int>();
         }
         SubPhases.Add(j);
         long start = curPhase.Start - phaseData.Start;
         long end   = curPhase.End - phaseData.Start;
         if (curPhase.DrawStart)
         {
             MarkupLines.Add(start / 1000.0);
         }
         if (curPhase.DrawEnd)
         {
             MarkupLines.Add(end / 1000.0);
         }
         AreaLabelDto phaseArea = new AreaLabelDto
         {
             Start     = start / 1000.0,
             End       = end / 1000.0,
             Label     = curPhase.Name,
             Highlight = curPhase.DrawArea
         };
         MarkupAreas.Add(phaseArea);
     }
     if (MarkupAreas.Count == 0)
     {
         MarkupAreas = null;
     }
     if (MarkupLines.Count == 0)
     {
         MarkupLines = null;
     }
 }
Ejemplo n.º 3
0
        private void CreateUptimeTable(List <Boon> listToUse, int phaseIndex)
        {
            //generate Uptime Table table
            PhaseData phase         = _statistics.Phases[phaseIndex];
            long      fightDuration = phase.GetDuration();

            WriteCells(new [] { "Name", "Avg Boons" });
            foreach (Boon boon in listToUse)
            {
                WriteCell(boon.Name);
            }
            NewLine();

            int count = 0;

            foreach (Player player in _log.PlayerList)
            {
                Dictionary <long, Statistics.FinalBuffs> uptimes = _statistics.SelfBuffs[player][phaseIndex];

                WriteCell(player.Character);
                WriteCell(Math.Round(_statistics.StatsAll[player][phaseIndex].AvgBoons, 1).ToString());
                foreach (Boon boon in listToUse)
                {
                    if (uptimes.TryGetValue(boon.ID, out var value))
                    {
                        if (boon.Type == Boon.BoonType.Duration)
                        {
                            WriteCell(value.Uptime + "%");
                        }
                        else if (boon.Type == Boon.BoonType.Intensity)
                        {
                            WriteCell(value.ToString());
                        }
                    }
                    else
                    {
                        WriteCell("0");
                    }
                }
                NewLine();
                count++;
            }
            while (count < 15)//so each graph has equal spacing
            {
                NewLine();
                count++;
            }
        }
        private FinalDPS GetFinalDPS(AbstractPlayer player, int phaseIndex, Boss target)
        {
            PhaseData phase         = _statistics.Phases[phaseIndex];
            double    phaseDuration = (phase.GetDuration()) / 1000.0;
            double    damage;
            double    dps   = 0.0;
            FinalDPS  final = new FinalDPS();

            //DPS
            damage = player.GetDamageLogs(target, _log,
                                          phase.Start, phase.End).Sum(x => x.Damage);

            if (phaseDuration > 0)
            {
                dps = damage / phaseDuration;
            }
            final.Dps    = (int)dps;
            final.Damage = (int)damage;
            //Condi DPS
            damage = player.GetDamageLogs(target, _log,
                                          phase.Start, phase.End).Where(x => x.IsCondi > 0).Sum(x => x.Damage);

            if (phaseDuration > 0)
            {
                dps = damage / phaseDuration;
            }
            final.CondiDps    = (int)dps;
            final.CondiDamage = (int)damage;
            //Power DPS
            damage = final.Damage - final.CondiDamage;
            if (phaseDuration > 0)
            {
                dps = damage / phaseDuration;
            }
            final.PowerDps          = (int)dps;
            final.PowerDamage       = (int)damage;
            final.PlayerPowerDamage = player.GetJustPlayerDamageLogs(target, _log,
                                                                     phase.Start, phase.End).Where(x => x.IsCondi == 0).Sum(x => x.Damage);
            return(final);
        }
        public static void WritePlayerTabBoonGraph(StreamWriter sw, BoonsGraphModel bgm, PhaseData phase)
        {
            long roundedEnd = phase.Start + 1000 * phase.GetDuration("s");
            List <BoonsGraphModel.Segment> bChart = bgm.BoonChart.Where(x => x.End >= phase.Start && x.Start <= roundedEnd).ToList();

            if (bChart.Count == 0 || (bChart.Count == 1 && bChart.First().Value == 0))
            {
                return;
            }
            sw.Write("y: [");
            {
                foreach (BoonsGraphModel.Segment seg in bChart)
                {
                    sw.Write("'" + seg.Value + "',");
                }
                sw.Write("'" + bChart.Last().Value + "'");
            }
            sw.Write("],");
            sw.Write("x: [");
            {
                foreach (BoonsGraphModel.Segment seg in bChart)
                {
                    double segStart = Math.Round(Math.Max(seg.Start - phase.Start, 0) / 1000.0, 3);
                    sw.Write("'" + segStart + "',");
                }
                sw.Write("'" + Math.Round(Math.Min(bChart.Last().End - phase.Start, roundedEnd - phase.Start) / 1000.0, 3) + "'");
            }
            sw.Write("],");
            sw.Write(" yaxis: 'y2'," +
                     " type: 'scatter',");
            //  "legendgroup: '"+Boon.getEnum(bgm.getBoonName()).getPloltyGroup()+"',";
            if (!(bgm.BoonName == "Might" || bgm.BoonName == "Quickness"))
            {
                sw.Write(" visible: 'legendonly',");
            }
            sw.Write(" line: {color:'" + GetLink("Color-" + bgm.BoonName) + "', shape: 'hv'},");
            sw.Write(" fill: 'tozeroy'," +
                     " name: \"" + bgm.BoonName + "\"");
        }
        private FinalDPS GetFinalDPS(AbstractActor player, int phaseIndex, Target target)
        {
            PhaseData phase         = _statistics.Phases[phaseIndex];
            double    phaseDuration = (phase.GetDuration()) / 1000.0;
            int       damage;
            double    dps   = 0.0;
            FinalDPS  final = new FinalDPS();

            //DPS
            damage = player.GetDamageLogs(target, _log,
                                          phase.Start, phase.End).Sum(x => x.Damage);

            if (phaseDuration > 0)
            {
                dps = damage / phaseDuration;
            }
            final.Dps    = (int)Math.Round(dps);
            final.Damage = damage;
            //Condi DPS
            damage = player.GetDamageLogs(target, _log,
                                          phase.Start, phase.End).Sum(x => x.IsCondi ? x.Damage : 0);

            if (phaseDuration > 0)
            {
                dps = damage / phaseDuration;
            }
            final.CondiDps    = (int)Math.Round(dps);
            final.CondiDamage = damage;
            //Power DPS
            damage = final.Damage - final.CondiDamage;
            if (phaseDuration > 0)
            {
                dps = damage / phaseDuration;
            }
            final.PowerDps    = (int)Math.Round(dps);
            final.PowerDamage = damage;
            return(final);
        }
        private FinalStatsAll GetFinalStats(Player p, int phaseIndex)
        {
            PhaseData phase = _statistics.Phases[phaseIndex];
            long      start = _log.FightData.ToLogSpace(phase.Start);
            long      end   = _log.FightData.ToLogSpace(phase.End);
            Dictionary <Target, FinalStats> targetDict = new Dictionary <Target, FinalStats>();

            foreach (Target target in _log.FightData.Logic.Targets)
            {
                if (!_statistics.StatsTarget.ContainsKey(target))
                {
                    _statistics.StatsTarget[target] = new Dictionary <Player, FinalStats[]>();
                }
                Dictionary <Player, FinalStats[]> targetStats = _statistics.StatsTarget[target];
                if (!targetStats.ContainsKey(p))
                {
                    targetStats[p] = new FinalStats[_statistics.Phases.Count];
                }
                targetStats[p][phaseIndex] = new FinalStats();
                targetDict[target]         = targetStats[p][phaseIndex];
            }
            FinalStatsAll final = new FinalStatsAll();

            FillFinalStats(p.GetJustPlayerDamageLogs(null, _log, phase.Start, phase.End), final, targetDict);
            if (p.Account == ":Conjured Sword")
            {
                return(final);
            }
            foreach (CastLog cl in p.GetCastLogs(_log, phase.Start, phase.End))
            {
                if (cl.EndActivation == ParseEnum.Activation.CancelCancel)
                {
                    final.Wasted++;
                    final.TimeWasted += cl.ActualDuration;
                }
                if (cl.EndActivation == ParseEnum.Activation.CancelFire)
                {
                    if (cl.ActualDuration < cl.ExpectedDuration)
                    {
                        final.Saved++;
                        final.TimeSaved += cl.ExpectedDuration - cl.ActualDuration;
                    }
                }
            }
            final.TimeSaved  = Math.Round(final.TimeSaved / 1000.0, 3);
            final.TimeWasted = Math.Round(final.TimeWasted / 1000.0, 3);

            double avgBoons = 0;

            foreach (long duration in p.GetBoonPresence(_log, phaseIndex).Values)
            {
                avgBoons += duration;
            }
            final.AvgBoons = avgBoons / phase.GetDuration();

            double avgCondis = 0;

            foreach (long duration in p.GetCondiPresence(_log, phaseIndex).Values)
            {
                avgCondis += duration;
            }
            final.AvgConditions = avgCondis / phase.GetDuration();


            // Counts
            CombatData combatData = _log.CombatData;

            final.SwapCount = p.GetCastLogs(_log, 0, _log.FightData.FightDuration).Count(x => x.SkillId == SkillItem.WeaponSwapId);

            if (_settings.ParseCombatReplay && _log.FightData.Logic.CanCombatReplay)
            {
                if (_statistics.StackCenterPositions == null)
                {
                    _statistics.StackCenterPositions = new List <Point3D>();
                    List <List <Point3D> > GroupsPosList = new List <List <Point3D> >();
                    foreach (Player player in _log.PlayerList)
                    {
                        if (player.Account == ":Conjured Sword")
                        {
                            continue;
                        }
                        GroupsPosList.Add(player.CombatReplay.GetActivePositions());
                    }
                    for (int time = 0; time < GroupsPosList[0].Count; time++)
                    {
                        float x             = 0;
                        float y             = 0;
                        float z             = 0;
                        int   activePlayers = GroupsPosList.Count;
                        foreach (List <Point3D> points in GroupsPosList)
                        {
                            Point3D point = points[time];
                            if (point != null)
                            {
                                x += point.X;
                                y += point.Y;
                                z += point.Z;
                            }
                            else
                            {
                                activePlayers--;
                            }
                        }
                        x = x / activePlayers;
                        y = y / activePlayers;
                        z = z / activePlayers;
                        _statistics.StackCenterPositions.Add(new Point3D(x, y, z, _settings.PollingRate * time));
                    }
                }
                List <Point3D> positions = p.CombatReplay.Positions.Where(x => x.Time >= phase.Start && x.Time <= phase.End).ToList();
                int            offset    = p.CombatReplay.Positions.Count(x => x.Time < phase.Start);
                if (positions.Count > 1)
                {
                    List <float> distances = new List <float>();
                    for (int time = 0; time < positions.Count; time++)
                    {
                        float deltaX = positions[time].X - _statistics.StackCenterPositions[time + offset].X;
                        float deltaY = positions[time].Y - _statistics.StackCenterPositions[time + offset].Y;
                        //float deltaZ = positions[time].Z - StackCenterPositions[time].Z;


                        distances.Add((float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY));
                    }
                    final.StackDist = distances.Sum() / distances.Count;
                }
                else
                {
                    final.StackDist = -1;
                }
            }
            return(final);
        }
Ejemplo n.º 8
0
        private void CreateDPSTable(int phaseIndex)
        {
            PhaseData phase = _statistics.Phases[phaseIndex];

            WriteLine(new[] { "Sub Group", "Profession", "Role", "Name", "Account", "WepSet1_1", "WepSet1_2", "WepSet2_1", "WepSet2_2",
                              "Boss DPS", "Boss DMG", "Boss Power DPS", "Boss Power DMG", "Boss Condi DPS", "Boss Condi DMG",
                              "All DPS", "All DMG", "All Power DPS", "All Power DMG", "All Condi DPS", "All Condi DMG",
                              "Times Downed", "Time Died", "Percent Alive" });

            int count = 0;

            foreach (Player player in _log.PlayerList)
            {
                Statistics.FinalDPS      dps     = _statistics.DpsAll[player][phaseIndex];
                Statistics.FinalDefenses defense = _statistics.Defenses[player][phaseIndex];
                Statistics.FinalDPS      dpsBoss = _statistics.DpsTarget[_log.LegacyTarget][player][phaseIndex];
                string deathString   = defense.DeadCount.ToString();
                string deadthTooltip = "";
                if (defense.DeadCount > 0)
                {
                    TimeSpan deathDuration = TimeSpan.FromMilliseconds(defense.DeadDuration);
                    deadthTooltip = deathDuration.TotalSeconds + " seconds dead, " + (100.0 - Math.Round((deathDuration.TotalMilliseconds / phase.GetDuration()) * 100, 1)) + "% Alive";
                }
                string[] wep   = player.GetWeaponsArray(_log);
                string   build = "";
                if (player.Condition > 0)
                {
                    build += " Condi:" + player.Condition;
                }
                if (player.Concentration > 0)
                {
                    build += " Concentration:" + player.Concentration;
                }
                if (player.Healing > 0)
                {
                    build += " Healing:" + player.Healing;
                }
                if (player.Toughness > 0)
                {
                    build += " Toughness:" + player.Toughness;
                }
                WriteLine(new [] { player.Group.ToString(), player.Prof, build, player.Character, player.Account.TrimStart(':'), wep[0], wep[1], wep[2], wep[3],
                                   dpsBoss.Dps.ToString(), dpsBoss.Damage.ToString(), dpsBoss.PowerDps.ToString(), dpsBoss.PowerDamage.ToString(), dpsBoss.CondiDps.ToString(), dpsBoss.CondiDamage.ToString(),
                                   dps.Dps.ToString(), dps.Damage.ToString(), dps.PowerDps.ToString(), dps.PowerDamage.ToString(), dps.CondiDps.ToString(), dps.CondiDamage.ToString(),
                                   defense.DownCount.ToString(), deathString, deadthTooltip });
                count++;
            }
            while (count < 15)//so each graph has equal spacing
            {
                NewLine();
                count++;
            }
        }
        private static List <Point> GetDPSGraph(ParsedLog log, AbstractMasterPlayer p, int phaseIndex, PhaseData phase, AbstractPlayer target, GraphMode mode)
        {
            ulong targetId = target != null ? target.Agent : 0;
            int   askedId  = (phaseIndex + "_" + targetId + "_" + mode).GetHashCode();

            if (p.GetDPSGraph(askedId).Count > 0)
            {
                return(p.GetDPSGraph(askedId));
            }

            List <Point>     dmgList    = new List <Point>();
            List <Point>     dmgList1s  = new List <Point>();
            List <Point>     dmgList10s = new List <Point>();
            List <Point>     dmgList30s = new List <Point>();
            List <DamageLog> damageLogs = p.GetDamageLogs(target, log, phase.Start, phase.End);
            // fill the graph, full precision
            List <double> dmgListFull = new List <double>();

            for (int i = 0; i <= phase.GetDuration(); i++)
            {
                dmgListFull.Add(0.0);
            }
            int totalTime   = 1;
            int totalDamage = 0;

            foreach (DamageLog dl in damageLogs)
            {
                int time = (int)(dl.Time - phase.Start);
                // fill
                for (; totalTime < time; totalTime++)
                {
                    dmgListFull[totalTime] = totalDamage;
                }
                totalDamage           += dl.Damage;
                dmgListFull[totalTime] = totalDamage;
            }
            // fill
            for (; totalTime <= phase.GetDuration(); totalTime++)
            {
                dmgListFull[totalTime] = totalDamage;
            }

            /*CombatReplay replay = p.Replay;
             * if (replay != null && dstid == 0 && phaseIndex == 0)
             * {
             *  foreach (int i in replay.GetTimes())
             *  {
             *      int limitId = 0;
             *      replay.AddDPS((int)Math.Round(1000 * (dmgListFull[i] - dmgListFull[limitId]) / (i - limitId)));
             *      if (Settings.Show10s)
             *      {
             *          limitId = Math.Max(i - 10000, 0);
             *          replay.AddDPS10s((int)Math.Round(1000 * (dmgListFull[i] - dmgListFull[limitId]) / (i - limitId)));
             *      }
             *      if (Settings.Show30s)
             *      {
             *          limitId = Math.Max(i - 30000, 0);
             *          replay.AddDPS30s((int)Math.Round(1000 * (dmgListFull[i] - dmgListFull[limitId]) / (i - limitId)));
             *      }
             *  }
             * }*/
            dmgList.Add(new Point(0, 0));
            dmgList1s.Add(new Point(0, 0));
            dmgList10s.Add(new Point(0, 0));
            dmgList30s.Add(new Point(0, 0));
            for (int i = 1; i <= phase.GetDuration("s"); i++)
            {
                int limitId = 0;
                dmgList.Add(new Point(i, (int)Math.Round((dmgListFull[1000 * i] - dmgListFull[1000 * limitId]) / (i - limitId))));
                limitId = i - 1;
                dmgList1s.Add(new Point(i, (int)Math.Round((dmgListFull[1000 * i] - dmgListFull[1000 * limitId]) / (i - limitId))));
                if (Settings.Show10s)
                {
                    limitId = Math.Max(i - 10, 0);
                    dmgList10s.Add(new Point(i, (int)Math.Round((dmgListFull[1000 * i] - dmgListFull[1000 * limitId]) / (i - limitId))));
                }
                if (Settings.Show30s)
                {
                    limitId = Math.Max(i - 30, 0);
                    dmgList30s.Add(new Point(i, (int)Math.Round((dmgListFull[1000 * i] - dmgListFull[1000 * limitId]) / (i - limitId))));
                }
            }
            int id = (phaseIndex + "_" + targetId + "_" + GraphMode.Full).GetHashCode();

            p.DpsGraph[id] = dmgList;
            id             = (phaseIndex + "_" + targetId + "_" + GraphMode.S1).GetHashCode();
            p.DpsGraph[id] = dmgList1s;
            if (Settings.Show10s)
            {
                id             = (phaseIndex + "_" + targetId + "_" + GraphMode.S10).GetHashCode();
                p.DpsGraph[id] = dmgList10s;
            }
            if (Settings.Show30s)
            {
                id             = (phaseIndex + "_" + targetId + "_" + GraphMode.S30).GetHashCode();
                p.DpsGraph[id] = dmgList30s;
            }
            return(p.GetDPSGraph(askedId));
        }
Ejemplo n.º 10
0
        public static List <object> GetDefenseStatData(Statistics.FinalDefenses defenses, PhaseData phase)
        {
            List <object> data = new List <object>
            {
                defenses.DamageTaken,
                defenses.DamageBarrier,
                defenses.BlockedCount,
                defenses.InvulnedCount,
                defenses.InterruptedCount,
                defenses.EvadedCount,
                defenses.DodgeCount
            };

            if (defenses.DownDuration > 0)
            {
                TimeSpan downDuration = TimeSpan.FromMilliseconds(defenses.DownDuration);
                data.Add(defenses.DownCount);
                data.Add(downDuration.TotalSeconds + " seconds downed, " + Math.Round((downDuration.TotalMilliseconds / phase.GetDuration()) * 100, 1) + "% Downed");
            }
            else
            {
                data.Add(0);
                data.Add("0% downed");
            }

            if (defenses.DeadDuration > 0)
            {
                TimeSpan deathDuration = TimeSpan.FromMilliseconds(defenses.DeadDuration);
                data.Add(defenses.DeadCount);
                data.Add(deathDuration.TotalSeconds + " seconds dead, " + (100.0 - Math.Round((deathDuration.TotalMilliseconds / phase.GetDuration()) * 100, 1)) + "% Alive");
            }
            else
            {
                data.Add(0);
                data.Add("100% Alive");
            }
            return(data);
        }
        private void CreateDPSTable(int phaseIndex)
        {
            PhaseData phase = _statistics.Phases[phaseIndex];

            WriteLine(new[] { "Sub Group", "Profession", "Role", "Name", "Account", "WepSet1_1", "WepSet1_2", "WepSet2_1", "WepSet2_2",
                              "Boss DPS", "Boss DMG", "Boss Power DPS", "Boss Power DMG", "Boss Condi DPS", "Boss Condi DMG",
                              "All DPS", "All DMG", "All Power DPS", "All Power DMG", "All Condi DPS", "All Condi DMG",
                              "Times Downed", "Time Died", "Percent Alive" });

            int count = 0;

            foreach (Player player in _log.PlayerList)
            {
                Statistics.FinalDPS      dps       = _statistics.DpsAll[player][phaseIndex];
                Statistics.FinalDPS      dpsBoss   = _statistics.DpsBoss[_log.Boss][player][phaseIndex];
                Statistics.FinalStatsAll stats     = _statistics.StatsAll[player][phaseIndex];
                Statistics.FinalStats    statsBoss = _statistics.StatsBoss[_log.Boss][player][phaseIndex];
                string deathString   = "";
                string deadthTooltip = "";
                if (stats.Died != 0.0)
                {
                    if (stats.Died < 0)
                    {
                        deathString = -stats.Died + " time(s)";
                    }
                    else
                    {
                        TimeSpan timedead = TimeSpan.FromMilliseconds(stats.Died);
                        deathString   = timedead.Minutes + " m " + timedead.Seconds + " s";
                        deadthTooltip = Math.Round((timedead.TotalMilliseconds / phase.GetDuration()) * 100, 1) + "%";
                    }
                }
                else
                {
                    deadthTooltip = "Never died";
                }
                string[] wep   = player.GetWeaponsArray(_log);
                string   build = "";
                if (player.Condition > 0)
                {
                    build += " Condi:" + player.Condition;
                }
                if (player.Concentration > 0)
                {
                    build += " Concentration:" + player.Concentration;
                }
                if (player.Healing > 0)
                {
                    build += " Healing:" + player.Healing;
                }
                if (player.Toughness > 0)
                {
                    build += " Toughness:" + player.Toughness;
                }
                WriteLine(new [] { player.Group.ToString(), player.Prof, build, player.Character, player.Account.TrimStart(':'), wep[0], wep[1], wep[2], wep[3],
                                   dpsBoss.Dps.ToString(), dpsBoss.Damage.ToString(), dpsBoss.PowerDps.ToString(), dpsBoss.PowerDamage.ToString(), dpsBoss.CondiDps.ToString(), dpsBoss.CondiDamage.ToString(),
                                   dps.Dps.ToString(), dps.Damage.ToString(), dps.PowerDps.ToString(), dps.PowerDamage.ToString(), dps.CondiDps.ToString(), dps.CondiDamage.ToString(),
                                   stats.DownCount.ToString(), deathString, deadthTooltip });
                count++;
            }
            while (count < 15)//so each graph has equal spacing
            {
                NewLine();
                count++;
            }
        }
        public static void WriteBossHealthGraph(StreamWriter sw, int maxDPS, PhaseData phase, double[] bossHealth, string yAxis = "")
        {
            int duration = (int)phase.GetDuration("s");

            double[] chart = bossHealth.Skip((int)phase.Start / 1000).Take(duration + 1).ToArray();
            //Boss Health
            //Adding dps axis
            sw.Write("y: [");
            if (maxDPS == 0)
            {
                maxDPS = 1000;
            }
            for (int i = 0; i < chart.Length; i++)
            {
                double health = chart[i];
                if (i == chart.Length - 1)
                {
                    sw.Write("'" + ((health / 100.0) * maxDPS).ToString().Replace(',', '.') + "'");
                }
                else
                {
                    sw.Write("'" + ((health / 100.0) * maxDPS).ToString().Replace(',', '.') + "',");
                }
            }
            sw.Write("],");
            //text axis is boss hp in %
            sw.Write("text: [");
            for (int i = 0; i < chart.Length; i++)
            {
                double health = chart[i];
                if (i == chart.Length - 1)
                {
                    sw.Write("'" + (health + "%").Replace(',', '.') + "'");
                }
                else
                {
                    sw.Write("'" + (health + "%").Replace(',', '.') + "',");
                }
            }

            sw.Write("],");
            //add time axis
            sw.Write("x: [");
            for (int i = 0; i < chart.Length; i++)
            {
                double health = chart[i];
                if (i == chart.Length - 1)
                {
                    sw.Write("'" + i + "'");
                }
                else
                {
                    sw.Write("'" + i + "',");
                }
            }

            sw.Write("],");
            sw.Write(" mode: 'lines'," +
                     " line: {shape: 'spline', dash: 'dashdot'}," +
                     (yAxis.Length > 0 ? " yaxis: '" + yAxis + "',"  : "") +
                     "hoverinfo: 'text'," +
                     " name: 'Boss health'");
        }
        private FinalStatsAll GetFinalStats(Player p, int phaseIndex)
        {
            PhaseData      phase       = _statistics.Phases[phaseIndex];
            long           start       = phase.Start + _log.FightData.FightStart;
            long           end         = phase.End + _log.FightData.FightStart;
            FinalStatsAll  final       = new FinalStatsAll();
            HashSet <long> nonCritable = new HashSet <long>
            {
                9292,
                52370
            };

            foreach (DamageLog dl in p.GetJustPlayerDamageLogs(null, _log, phase.Start, phase.End))
            {
                if (dl.IsCondi == 0)
                {
                    if (dl.Result == ParseEnum.Result.Crit)
                    {
                        final.CriticalRate++;
                        final.CriticalDmg += dl.Damage;
                    }

                    if (dl.IsNinety > 0)
                    {
                        final.ScholarRate++;
                        final.ScholarDmg += (int)(dl.Damage / 11.0); //regular+10% damage
                    }

                    if (dl.IsMoving > 0)
                    {
                        final.MovingRate++;
                        final.MovingDamage += (int)(dl.Damage / 21.0);
                    }

                    final.FlankingRate += dl.IsFlanking;

                    if (dl.Result == ParseEnum.Result.Glance)
                    {
                        final.GlanceRate++;
                    }

                    if (dl.Result == ParseEnum.Result.Blind)
                    {
                        final.Missed++;
                    }

                    if (dl.Result == ParseEnum.Result.Interrupt)
                    {
                        final.Interrupts++;
                    }

                    if (dl.Result == ParseEnum.Result.Absorb)
                    {
                        final.Invulned++;
                    }
                    final.PowerLoopCount++;
                    if (!nonCritable.Contains(dl.SkillId))
                    {
                        final.CritablePowerLoopCount++;
                    }
                }
            }
            if (p.Account == ":Conjured Sword")
            {
                return(final);
            }
            foreach (CastLog cl in p.GetCastLogs(_log, phase.Start, phase.End))
            {
                if (cl.EndActivation == ParseEnum.Activation.CancelCancel)
                {
                    final.Wasted++;
                    final.TimeWasted += cl.ActualDuration;
                }
                if (cl.EndActivation == ParseEnum.Activation.CancelFire)
                {
                    final.Saved++;
                    if (cl.ActualDuration < cl.ExpectedDuration)
                    {
                        final.TimeSaved += cl.ExpectedDuration - cl.ActualDuration;
                    }
                }
            }
            final.TimeSaved  = final.TimeSaved / 1000f;
            final.TimeWasted = final.TimeWasted / 1000f;

            double avgBoons = 0;

            foreach (long duration in p.GetBoonPresence(_log, phaseIndex).Values)
            {
                avgBoons += duration;
            }
            final.AvgBoons = avgBoons / phase.GetDuration();

            double avgCondis = 0;

            foreach (long duration in p.GetCondiPresence(_log, phaseIndex).Values)
            {
                avgCondis += duration;
            }
            final.AvgConditions = avgCondis / phase.GetDuration();


            // Counts
            CombatData combatData = _log.CombatData;

            final.SwapCount  = combatData.GetStates(p.InstID, ParseEnum.StateChange.WeaponSwap, start, end).Count;
            final.DownCount  = combatData.GetStates(p.InstID, ParseEnum.StateChange.ChangeDown, start, end).Count;
            final.DodgeCount = combatData.GetSkillCount(p.InstID, SkillItem.DodgeId, start, end) + combatData.GetBuffCount(p.InstID, 40408, start, end);//dodge = 65001 mirage cloak =40408

            if (_settings.ParseCombatReplay && _log.FightData.Logic.CanCombatReplay)
            {
                if (_statistics.StackCenterPositions == null)
                {
                    _statistics.StackCenterPositions = new List <Point3D>();
                    List <List <Point3D> > GroupsPosList = new List <List <Point3D> >();
                    foreach (Player player in _log.PlayerList)
                    {
                        if (player.Account == ":Conjured Sword")
                        {
                            continue;
                        }
                        GroupsPosList.Add(player.CombatReplay.GetActivePositions());
                    }
                    for (int time = 0; time < GroupsPosList[0].Count; time++)
                    {
                        float x             = 0;
                        float y             = 0;
                        float z             = 0;
                        int   activePlayers = GroupsPosList.Count;
                        foreach (List <Point3D> points in GroupsPosList)
                        {
                            Point3D point = points[time];
                            if (point != null)
                            {
                                x += point.X;
                                y += point.Y;
                                z += point.Z;
                            }
                            else
                            {
                                activePlayers--;
                            }
                        }
                        x = x / activePlayers;
                        y = y / activePlayers;
                        z = z / activePlayers;
                        _statistics.StackCenterPositions.Add(new Point3D(x, y, z, _settings.PollingRate * time));
                    }
                }
                List <Point3D> positions = p.CombatReplay.Positions.Where(x => x.Time >= phase.Start && x.Time <= phase.End).ToList();
                int            offset    = p.CombatReplay.Positions.Count(x => x.Time < phase.Start);
                if (positions.Count > 1)
                {
                    List <float> distances = new List <float>();
                    for (int time = 0; time < positions.Count; time++)
                    {
                        float deltaX = positions[time].X - _statistics.StackCenterPositions[time + offset].X;
                        float deltaY = positions[time].Y - _statistics.StackCenterPositions[time + offset].Y;
                        //float deltaZ = positions[time].Z - StackCenterPositions[time].Z;


                        distances.Add((float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY));
                    }
                    final.StackDist = distances.Sum() / distances.Count;
                }
                else
                {
                    final.StackDist = -1;
                }
            }

            List <CombatItem> dead = combatData.GetStates(p.InstID, ParseEnum.StateChange.ChangeDead, start, end);
            List <CombatItem> up   = combatData.GetStates(p.InstID, ParseEnum.StateChange.ChangeUp, start, end);

            final.Died = 0.0;
            if (dead.Count > 0)
            {
                final.Died = up.Count > 0 && up.Last().Time > dead.Last().Time ? -dead.Count : dead.Last().Time - start;
            }

            List <CombatItem> disconnect = combatData.GetStates(p.InstID, ParseEnum.StateChange.Despawn, start, end);

            final.Dcd = 0.0;
            if (disconnect.Count > 0)
            {
                final.Dcd = disconnect.Last().Time - start;
            }
            return(final);
        }