/// <summary> /// Create a mini gauge image with the given data. /// </summary> /// <param name="count">The side counts that determine the needle angle.</param> /// <param name="doubleScale">If true the needle angle will be exaggerated x 2.</param> /// <returns>An image containing the gauge needle (doesn't include the gauge background).</returns> private Bitmap DrawGauge(SideCount count, bool doubleScale) { const int radius = 25; const int left = 5; const int top = 5; // calculate needle angle float angle; if (count.Allied != 0 || count.Axis != 0) { angle = ((float)count.Axis / (count.Allied + count.Axis)) * 180; } else { angle = 90; } if (doubleScale) { angle += (angle - 90); // exaggerate angle x2 } if (angle < 10) { angle = 10; } if (angle > 170) { angle = 170; } // get points to draw PointF needleCenter = new PointF(left + radius - 0.5F, top + radius - 1); PointF needleStart = Misc.AngleOffset(needleCenter, angle - 90, radius * 0.4); PointF needleEnd = Misc.AngleOffset(needleCenter, angle - 270, radius * 1.1); // init graphics resources Pen penNeedle = new Pen(Color.FromArgb(192, 192, 192), 2F); Bitmap img = new Bitmap(60, 40); Graphics g = Graphics.FromImage(img); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; // draw gauge g.DrawLine(penNeedle, needleStart, needleEnd); g.Dispose(); return(img); }
/// <summary> /// Updates the various server status controls with the given data. /// </summary> public void UpdateWidget() { if (this.game == null) { return; } // calc time ranges DateTime now = DateTime.Now; DateTime dateGraphEnd = new DateTime(now.Year, now.Month, now.Day, now.Hour, (now.Minute / 5) * 5, 0); // interger division = round to last 5 min interval DateTime dateGraphStart = dateGraphEnd.AddHours(-1); DateTime dateGaugeStart = now.AddHours(-1); /* For both capture and death counts below, there are two subtly different * time ranges used between the gauges and the graphs. The gauges are updated * every minute and show counts for the last hour, as normal. The graphs however * are only updated every 5 minutes and contain 12 datapoints each representing * a 5-minute window. To avoid the window changing and jumping around, they are * all aligned to 5 min intervals (0, 5, 10, 15, etc). */ // count captures SideCount countCaptures = new SideCount(); // past 60 mins SideCount[] countCapturesGrouped = new SideCount[12]; // past 5-65 mins foreach (GameEvent gameEvent in game.Events) { ICaptureFacilityGameEvent capGameEvent = gameEvent as ICaptureFacilityGameEvent; if (capGameEvent == null) { continue; } if (gameEvent.EventTime < dateGaugeStart) { continue; } if (capGameEvent.NewOwner.Side == Side.Allied) { countCaptures.Add(1, 0); } else { countCaptures.Add(0, 1); } if (gameEvent.EventTime < dateGraphStart || gameEvent.EventTime > dateGraphEnd) { continue; } int age = (int)Math.Round((dateGraphEnd - gameEvent.EventTime).TotalMinutes / 5) - 1; // 0 - 11 if (age < 0 || age > 11) { continue; // shouldn't happen } if (capGameEvent.NewOwner.Side == Side.Allied) { countCapturesGrouped[age].Add(1, 0); } else { countCapturesGrouped[age].Add(0, 1); } } // count deaths SideCount countDeaths = new SideCount(); // past 60 mins SideCount[] countDeathsGrouped = new SideCount[12]; // past 5-65 mins foreach (MapCell cell in game.MapCells.Values) { countDeaths.Add(cell.Deaths); SideCount[] cellDeaths = cell.DeathsGrouped; for (int i = 0; i < cellDeaths.Length; i++) { countDeathsGrouped[i].Add(cellDeaths[i]); } } // swap deaths for kills (not 100% accurate, but near enough) SideCount countKills = new SideCount(countDeaths.Axis, countDeaths.Allied); SideCount[] countKillsGrouped = new SideCount[countDeathsGrouped.Length]; for (int i = 0; i < countKillsGrouped.Length; i++) { countKillsGrouped[i] = new SideCount(countDeathsGrouped[i].Axis, countDeathsGrouped[i].Allied); } // count cps, fbs, AOs SideCount countChokePoints = new SideCount(); SideCount countFirebases = new SideCount(); SideCount countAOs = new SideCount(); foreach (ChokePoint cp in game.ValidChokePoints) { if (cp.Owner.Side == Side.Allied) { countChokePoints.Allied++; if (cp.HasAO) { countAOs.Axis++; } } else if (cp.Owner.Side == Side.Axis) { countChokePoints.Axis++; if (cp.HasAO) { countAOs.Allied++; } } } foreach (Firebase fb in game.Firebases.Values) { if (!fb.IsOpen) { continue; } if (fb.Link.Side == Side.Allied) { countFirebases.Allied++; } else if (fb.Link.Side == Side.Axis) { countFirebases.Axis++; } } // update controls if (game.Servers.ContainsKey(1)) { lblPopulationValue.Text = Misc.EnumString(game.Servers[1].Population); int pop = (int)game.Servers[1].Population; // 0 - 6 picGaugePopulation.Image = DrawGauge(new SideCount(6 - pop, pop), false); if (game.Servers[1].Online) { picServerState.Image = Resources.server_online; } else if (game.Servers[1].Locked) { picServerState.Image = Resources.server_locked; } else if (game.Servers[1].Offline) { picServerState.Image = Resources.server_offline; } else { picServerState.Image = Resources.server_other; } lblServerState.Text = game.Servers[1].State; GameStatus.ToolTip.SetToolTip(lblServerState, game.Servers[1].StateInfo); GameStatus.ToolTip.SetToolTip(picServerState, game.Servers[1].StateInfo); } lblKillsAllied.Text = countKills.Allied.ToString(); lblKillsAxis.Text = countKills.Axis.ToString(); picGaugeKills.Image = DrawGauge(countKills, true); lblCapturesAllied.Text = countCaptures.Allied.ToString(); lblCapturesAxis.Text = countCaptures.Axis.ToString(); picGaugeCaptures.Image = DrawGauge(countCaptures, true); picGraphKills.Image = DrawGraph(countKillsGrouped, true); picGraphCaptures.Image = DrawGraph(countCapturesGrouped, false); lblChokePointsAllied.Text = countChokePoints.Allied.ToString(); lblChokePointsAxis.Text = countChokePoints.Axis.ToString(); picGaugeChokePoints.Image = DrawGauge(countChokePoints, false); lblFirebasesAllied.Text = countFirebases.Allied.ToString(); lblFirebasesAxis.Text = countFirebases.Axis.ToString(); picGaugeFirebases.Image = DrawGauge(countFirebases, false); lblAOsAllied.Text = countAOs.Allied.ToString(); lblAOsAxis.Text = countAOs.Axis.ToString(); picGaugeAOs.Image = DrawGauge(countAOs, false); }