/// <summary> /// Displays a series of dummy events for alert testing. /// </summary> public void TestAlert() { this.currentEvents.Clear(); this.currentIndex = 0; DateTime time = DateTime.Now.AddMinutes(-10); Country newOwner = new Country(1, "UK", Language.Country_Name_England, Language.Country_Demonym_England, Side.Allied); Country prevOwner = new Country(4, "DE", Language.Country_Name_Germany, Language.Country_Demonym_Germany, Side.Axis); ChokePoint testCP = new ChokePoint(0, "Berlin", ChokePointType.Generic, prevOwner, new Point(), null); MilitaryFacility testFacility = new MilitaryFacility(0, "Berlin Central Armybase", testCP, FacilityType.Armybase, new Point()); Facility testFacility2 = new Facility(0, "Bilton's House", testCP, FacilityType.City, new Point()); HCUnit testHCUnit = new HCUnit(0, HCUnitLevel.Brigade, "27idkg1", "1.Kampfgruppe", "27.Infanterie 1.Kampfgruppe", null, prevOwner, Branch.Army, MoveType.Land, null, null); this.currentEvents.Add(new FacilityCapturedGameEvent(time, testFacility, newOwner, prevOwner, "xiper")); this.currentEvents.Add(new HCUnitRoutedGameEvent(time, testHCUnit, testCP)); this.currentEvents.Add(new ChokePointCapturedGameEvent(time, testCP, newOwner, prevOwner, DateTime.Now.AddMinutes(-171))); testCP.SetInitState(prevOwner, newOwner, testHCUnit); testCP.Activity = ActivityLevel.Heavy; testFacility.Owner = newOwner; if (tmrAutoNext.Enabled) { tmrAutoNext.Stop(); } if (this.Visible) { this.Visible = false; } DisplayAlerts(); }
// OTHER // event link private void lnkEvents_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { ChokePoint cp = lbCPList.SelectedItem as ChokePoint; if (cp != null) { GameStatus_RevealWidget(WidgetType.CurrentAttacks, cp); } }
/// <summary> /// Returns the number of events currently shown in a specific ChokePoint's event list. /// </summary> /// <param name="cp">The ChokePoint to get the displayed event count for.</param> /// <returns>0 if none or not found, >0 otherwise.</returns> public int NumEvents(ChokePoint cp) { Expando expChokepoint; if (!expandos.TryGetValue(cp, out expChokepoint)) { return(0); // not shown in current attacks list } return(((CAControls)expChokepoint.Tag).dgvEventList.Rows.Count); }
/// <summary> /// Generates a 50% scale capbar, showing the current state of the given ChokePoint. /// </summary> /// <param name="cp">The reference ChokePoint.</param> /// <returns>The mini capbar image.</returns> private Image GenerateMiniCapBar(ChokePoint cp) { Image bitmap = new Bitmap(130, 12); // size of picCapBar Graphics g = Graphics.FromImage(bitmap); const int left = 5; const int right = 125; const int range = right - left; const float y = 3.5F; if (cp.IsContested) { int mid = (int)((100 - cp.PercentOwnership) * range / 100) + left; g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_red_left : Resources.capbar_blue_left, left - 2, y, 2, 5); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_red : Resources.capbar_blue, new RectangleF(left, y, mid - left, 5), new Rectangle(0, 0, 1, 10), GraphicsUnit.Pixel); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue : Resources.capbar_red, new RectangleF(mid, y, right - mid, 5), new Rectangle(0, 0, 1, 10), GraphicsUnit.Pixel); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue_right : Resources.capbar_red_right, right, y, 2, 5); g.DrawImage(Resources.capbar_pointer, mid - 6.75F, y - 1.5F, 13.5F, 11.5F); } else // not contested { g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue_left : Resources.capbar_red_left, left - 2, y, 2, 5); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue : Resources.capbar_red, new RectangleF(left, y, range, 5), new Rectangle(0, 0, 1, 10), GraphicsUnit.Pixel); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue_right : Resources.capbar_red_right, right, y, 2, 5); } g.Dispose(); return(bitmap); }
/// <summary> /// Update the state of all the "Show alerts" checkboxes to reflect the current settings. /// </summary> public void UpdateShowAlertsCheckboxes() { foreach (KeyValuePair <ChokePoint, Expando> keyvalue in this.expandos) { ChokePoint cp = keyvalue.Key; Expando expando = keyvalue.Value; CheckBox cbShowAlerts = ((CAControls)expando.Tag).cbShowAlerts; if (options.Alerts.filterChokePoint == false || // not filtering, or options.Alerts.filterChokePointIDs.Contains(cp.ID)) // filtering and includes this cp { cbShowAlerts.Checked = true; } else { cbShowAlerts.Checked = false; } } UpdateShowAllAlertsCheckbox(); }
// individual "show alerts" checkboxes private void cbShowAlerts_Click(CheckBox cbShowAlerts, ChokePoint cp) { if (cbShowAlerts.Checked) { options.ShowAlerts(cp, true); } else // unchecked { if (!options.Alerts.filterChokePoint) // filter disabled { // enable filter and add remaining selected items (so new cps will be unchecked) options.Alerts.filterChokePoint = true; foreach (KeyValuePair <ChokePoint, Expando> keyvalue in this.expandos) { ChokePoint cp2 = keyvalue.Key; Expando expando = keyvalue.Value; if (((CAControls)expando.Tag).cbShowAlerts.Checked) { options.Alerts.filterChokePointIDs.Add(cp2.ID); } } } else // already filtering { options.Alerts.filterChokePointIDs.Remove(cp.ID); } options.Alerts.RegenerateFlags(); options.Alerts.SaveToRegistry(false); options.AlertsTab = options.Alerts; // update form controls } // update state of "show all alerts" checkbox UpdateShowAllAlertsCheckbox(); }
/// <summary> /// Generate a list of "dummy cps" that surround the outline of normal cps (used for frontline). /// </summary> private void GenerateDummyChokePoints() { this.dummyCPs.Clear(); // create outline hull Frontline outline = new Frontline(Side.Neutral, this.ChokePoints); outline.Update(); // for each cp pair in each hull, create dummy cp between them offset 90deg int id = this.ChokePoints.Length; foreach (List <ChokePoint> hull in outline.Hulls) { ChokePoint prevCp = hull[hull.Count - 1]; // last item foreach (ChokePoint cp in hull) { Point pt1 = prevCp.LocationOctets; Point pt2 = cp.LocationOctets; double distance = Misc.DistanceBetween(pt1, pt2); double angle = Misc.AngleBetween(pt1, pt2); Point ptMid = Misc.AngleOffset(cp.LocationOctets, angle, distance / 2); Point ptMidOffset = Misc.AngleOffset(ptMid, angle + 90, Frontline.DUMMYCP_DISTANCE); this.dummyCPs.Add(new ChokePoint(id, String.Format("({0}-{1})", prevCp, cp), ChokePointType.Dummy, this.Countries[0], ptMidOffset, null)); prevCp = cp; id++; } } }
/// <summary> /// Updates the given ChokePoint's Expando's event list tooltips. /// </summary> /// <param name="cp">The ChokePoint to update.</param> private void UpdateTooltip(ChokePoint cp) { if (!expandos.ContainsKey(cp)) { return; } Expando expando = expandos[cp]; CAControls controls = (CAControls)expando.Tag; foreach (DataGridViewRow row in controls.dgvEventList.Rows) { GameEvent gameEvent = (GameEvent)row.Tag; string tooltip = String.Format(Language.Time_MinsAgo, Misc.MinsAgoLong(gameEvent.EventTime)); int minsDiff = (int)(gameEvent.EventReceived - gameEvent.EventTime).TotalMinutes; if (minsDiff > 5) // received over 5 mins after the event occurred { tooltip += " (" + String.Format(Language.Time_ReceivedMinsAgo, Misc.MinsAgoLong(gameEvent.EventReceived)) + ")"; } row.Cells[1].ToolTipText = tooltip; } }
/// <summary> /// Makes a chokepoint within the Current Attacks widget visible by making sure both /// the widget and chokepoint expandos are expanded, scrolling it into view and flashing /// the chokepoint title. /// </summary> /// <seealso cref="tmrReveal_Tick"/> /// <param name="arg">The ChokePoint to make visible.</param> public void Reveal(object arg) { ChokePoint cp = arg as ChokePoint; if (cp == null) { return; } if (!expandos.ContainsKey(cp)) { return; // not shown in current attacks list } if (tmrReveal.Enabled) { return; // another Reveal() in progress } Expando expChokepoint = expandos[cp]; Expando expCurrentAttacks = (Expando)this.Parent; if (!expChokepoint.CanCollapse) { return; // cp list disabled, no events } if (expCurrentAttacks.Collapsed) { expChokepoint.Expand(); // no animation expCurrentAttacks.Collapsed = false; } if (expChokepoint.Collapsed) { expChokepoint.Collapsed = false; // animate } tmrReveal.Tag = expChokepoint; tmrReveal.Start(); }
/// <summary> /// Makes the Town Status widget visible by making sure it's expanded, scrolling it into /// view, and populating it with the given ChokePoint. /// </summary> /// <seealso cref="tmrReveal_Tick"/> /// <param name="arg">The ChokePoint to display.</param> public void Reveal(object arg) { ChokePoint cp = arg as ChokePoint; if (cp == null) { return; } if (tmrReveal.Enabled) { return; // another Reveal() in progress } CenterSelection(cp); DisplayChokePointInfo(cp); if (this.selectedChokePoint == null) // not found { return; } Expando expTownStatus = (Expando)this.Parent; if (expTownStatus.Collapsed) { expTownStatus.Collapsed = false; // animate tmrReveal.Start(); // scroll into view after animation } else { if (expTownStatus.TaskPane != null) { expTownStatus.TaskPane.ScrollControlIntoView(expTownStatus); } } }
/// <summary> /// Updates the various controls to display information about the given ChokePoint. /// </summary> /// <param name="cp">The ChokePoint to display, or null to clear.</param> private void DisplayChokePointInfo(ChokePoint cp) { this.selectedChokePoint = cp; // if null, clear everything if (cp == null) { picCPFlag.Image = null; GameStatus.ToolTip.SetToolTip(picCPFlag, null); lblCPName.Text = null; lnkEvents.Visible = lnkMap.Visible = false; picCPInfo.Image = null; picCPInfo.RemoveAll(); // remove previous tooltips return; } // hide start tip if (lblStartTip.Visible) { lblStartTip.Visible = false; } // flag picCPFlag.Image = cp.FlagImage; GameStatus.ToolTip.SetToolTip(picCPFlag, cp.FlagTooltip); // title lblCPName.Text = cp.Name; // info picCPInfo.Image = GenerateInfoImage(cp, picCPInfo); picCPInfo.Height = picCPInfo.Image.Height; // event link int displayedEventCount = CurrentAttacks_NumEvents(cp); int actualEventCount = 0; foreach (GameEvent gameEvent in cp.Events) { actualEventCount++; } if (displayedEventCount > 0) { lnkEvents.Text = String.Format(displayedEventCount == 1 ? Language.TownStatus_Event : Language.TownStatus_Events, displayedEventCount); lnkEvents.Visible = true; LnkEventsEnabled = true; } else if (actualEventCount > 0) // events exist but not present in CurrentAttacks { lnkEvents.Text = String.Format(actualEventCount == 1 ? Language.TownStatus_Event : Language.TownStatus_Events, actualEventCount); lnkEvents.Visible = true; LnkEventsEnabled = false; } else { lnkEvents.Visible = false; } // map link lnkMap.Visible = true; // resize parent expando Expando expTownStatus = (Expando)this.Parent; if (expTownStatus.ExpandedHeight != 151 + picCPInfo.Height) { expTownStatus.SuspendLayout(); expTownStatus.ExpandedHeight = 151 + picCPInfo.Height; expTownStatus.Height += 1; // workaround for bug in tskMain ScrollableControl: expTownStatus.Height -= 1; // force the correct scroll height expTownStatus.ResumeLayout(); } }
/// <summary> /// Updates the given ChokePoint's Expando. /// </summary> /// <param name="cp">The ChokePoint to update.</param> private void UpdateChokePoint(ChokePoint cp) { Expando expando = expandos[cp]; CAControls controls = (CAControls)expando.Tag; // update title image expando.TitleImage = cp.FlagImage; // update attack objective/activity level icon Image icon; if (cp.HasAO) { icon = new Bitmap(45, 18); // 21x18 + 20x18 Graphics g = Graphics.FromImage(icon); g.DrawImage(Resources.attack_objective, 0, 0, 21, 18); g.DrawImage(cp.ActivityImage, icon.Width - 20, 0, 20, 18); g.Dispose(); } else { icon = cp.ActivityImage; } expando.CustomHeaderSettings.NormalArrowUp = expando.CustomHeaderSettings.NormalArrowDown = expando.CustomHeaderSettings.NormalArrowUpHot = expando.CustomHeaderSettings.NormalArrowDownHot = icon; // capbar controls.picCapBar.Image = GenerateMiniCapBar(cp); // remember scroll position int prevFirstDisplayedIndex = controls.dgvEventList.FirstDisplayedScrollingRowIndex; if (controls.dgvEventList.Rows.Count > 0 && prevFirstDisplayedIndex >= controls.dgvEventList.Rows.Count - 5) // at bottom, preserve { prevFirstDisplayedIndex = int.MaxValue; } // clear list controls.dgvEventList.Rows.Clear(); // loop over event list (forwards or backwards) for (int i = (this.eventSortOrder == SortOrder.Ascending ? 0 : game.Events.List.Count - 1); i >= 0 && i < game.Events.List.Count; i += (this.eventSortOrder == SortOrder.Ascending ? 1 : -1)) { GameEvent gameEvent = game.Events.List[i]; if (!gameEvent.ChokePoints.Contains(cp)) { continue; // not associated with our chokepoint } if (gameEvent.Type == GameEventType.Factory) { continue; // don't include factory events here } // create & add entry w/ timestamp string entry = String.Format("{0} {1}", Misc.Timestamp(gameEvent.EventTime), gameEvent.Description); int idx = controls.dgvEventList.Rows.Add(gameEvent.Icon, entry); controls.dgvEventList.Rows[idx].Tag = gameEvent; // tooltip string tooltip = String.Format(Language.Time_MinsAgo, Misc.MinsAgoLong(gameEvent.EventTime)); int minsDiff = (int)(gameEvent.EventReceived - gameEvent.EventTime).TotalMinutes; if (minsDiff > 5) // received over 5 mins after the event occurred { tooltip += " (" + String.Format(Language.Time_ReceivedMinsAgo, Misc.MinsAgoLong(gameEvent.EventReceived)) + ")"; } controls.dgvEventList[1, idx].ToolTipText = tooltip; // if special event, make bold if (gameEvent is ChokePointCapturedGameEvent || (gameEvent is ChokePointUnderAttackGameEvent && ((ChokePointUnderAttackGameEvent)gameEvent).NewAttack)) { controls.dgvEventList[1, idx].Style.Font = new Font(controls.dgvEventList[1, idx].InheritedStyle.Font, FontStyle.Bold); } } if (expando.ExpandedHeight > EXPANDO_DEFAULT_HEIGHT) // expanded, update height { SetListExpanded(expando, true); } else // restore scroll position { if (controls.dgvEventList.Rows.Count > 0) { if (prevFirstDisplayedIndex >= 0 && prevFirstDisplayedIndex < controls.dgvEventList.Rows.Count) // valid previous value { controls.dgvEventList.FirstDisplayedScrollingRowIndex = prevFirstDisplayedIndex; } else if (prevFirstDisplayedIndex == int.MaxValue) // stay at end { controls.dgvEventList.FirstDisplayedScrollingRowIndex = controls.dgvEventList.Rows.Count - 1; } else // default: newest end { controls.dgvEventList.FirstDisplayedScrollingRowIndex = this.eventSortOrder == SortOrder.Ascending ? controls.dgvEventList.Rows.Count - 1 : 0; } } } // set enabled/disabled status if changed if (controls.dgvEventList.Rows.Count > 0) // has events { if (!expando.CanCollapse) // currently disabled, enable { expando.CustomHeaderSettings.NormalTitleColor = Color.FromArgb(224, 224, 224); expando.CustomHeaderSettings.NormalGradientEndColor = Color.FromArgb(51, 51, 51); expando.CustomHeaderSettings.NormalGradientStartColor = Color.FromArgb(51, 51, 51); expando.CanCollapse = true; expando.Collapsed = false; // animate GameStatus.ToolTip.SetToolTip(expando, null); } } else // doesn't have events { if (expando.CanCollapse) // currently enabled, disable { expando.CustomHeaderSettings.NormalTitleColor = Color.Gray; expando.CustomHeaderSettings.NormalGradientEndColor = Color.FromArgb(30, 30, 30); expando.CustomHeaderSettings.NormalGradientStartColor = Color.FromArgb(30, 30, 30); expando.Collapse(); // no animation expando.CanCollapse = false; GameStatus.ToolTip.SetToolTip(expando, String.Format(Language.CurrentAttacks_Tooltip_NoEvents, cp)); } } }
/// <summary> /// Create a new Expando control with the appropriate settings and sub-controls, based on the given ChokePoint. /// </summary> /// <param name="cp">The ChokePoint the Expando will represent.</param> /// <returns>The new Expando control</returns> private Expando CreateExpando(ChokePoint cp) { // control container CAControls controls = new CAControls(); // expando Expando expando = new Expando(); expando.Animate = true; expando.Collapse(); expando.CustomHeaderSettings.BackImageHeight = 25; expando.CustomHeaderSettings.NormalGradientEndColor = Color.FromArgb(51, 51, 51); expando.CustomHeaderSettings.NormalGradientStartColor = Color.FromArgb(51, 51, 51); expando.CustomHeaderSettings.NormalTitleColor = Color.FromArgb(224, 224, 224); expando.CustomHeaderSettings.NormalTitleHotColor = Color.White; expando.CustomHeaderSettings.TitleFont = new Font("Arial", 9F, FontStyle.Bold, GraphicsUnit.Point, 0); expando.CustomHeaderSettings.TitleGradient = true; expando.CustomHeaderSettings.TitleRadius = 0; expando.CustomSettings.NormalBackColor = Color.Black; expando.CustomSettings.NormalBorderColor = Color.Black; expando.ExpandedHeight = EXPANDO_DEFAULT_HEIGHT; expando.Text = cp.Name; // event datagridview controls.dgvEventList = new DataGridView(); controls.dgvEventList.AllowUserToAddRows = false; controls.dgvEventList.AllowUserToDeleteRows = false; controls.dgvEventList.AllowUserToResizeColumns = false; controls.dgvEventList.AllowUserToResizeRows = false; controls.dgvEventList.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; controls.dgvEventList.BackgroundColor = Color.Black; controls.dgvEventList.BorderStyle = BorderStyle.None; controls.dgvEventList.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal; controls.dgvEventList.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText; controls.dgvEventList.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; controls.dgvEventList.ColumnHeadersVisible = false; DataGridViewCellStyle cellStyle1 = new DataGridViewCellStyle(); cellStyle1.Alignment = DataGridViewContentAlignment.MiddleLeft; cellStyle1.BackColor = Color.Black; cellStyle1.Font = new Font("Microsoft Sans Serif", 8.25F, FontStyle.Regular, GraphicsUnit.Point, 0); cellStyle1.ForeColor = Color.FromArgb(224, 224, 224); cellStyle1.SelectionBackColor = Color.Black; cellStyle1.SelectionForeColor = Color.FromArgb(224, 224, 224); cellStyle1.WrapMode = DataGridViewTriState.False; controls.dgvEventList.DefaultCellStyle = cellStyle1; controls.dgvEventList.GridColor = Color.FromArgb(64, 64, 64); controls.dgvEventList.Location = new Point(3, 21 + 16); // 21 = header controls.dgvEventList.MultiSelect = false; controls.dgvEventList.RowHeadersVisible = false; controls.dgvEventList.ScrollBars = ScrollBars.Vertical; controls.dgvEventList.SelectionMode = DataGridViewSelectionMode.FullRowSelect; controls.dgvEventList.Size = new Size(330, 128); controls.dgvEventList.StandardTab = true; controls.dgvEventList.DataError += (sender, args) => { }; //// avoid default DataError dialog DataGridViewImageColumn imagecol = new DataGridViewImageColumn(); imagecol.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; DataGridViewCellStyle cellStyle2 = new DataGridViewCellStyle(); cellStyle2.Alignment = DataGridViewContentAlignment.TopLeft; cellStyle2.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0); imagecol.DefaultCellStyle = cellStyle2; imagecol.ImageLayout = DataGridViewImageCellLayout.Zoom; imagecol.ReadOnly = true; imagecol.Resizable = DataGridViewTriState.False; imagecol.Width = 20; controls.dgvEventList.Columns.Add(imagecol); DataGridViewTextBoxColumn textcol = new DataGridViewTextBoxColumn(); textcol.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; DataGridViewCellStyle cellStyle3 = new DataGridViewCellStyle(); cellStyle3.Padding = new System.Windows.Forms.Padding(0, 0, 0, 2); cellStyle3.WrapMode = DataGridViewTriState.True; textcol.DefaultCellStyle = cellStyle3; textcol.ReadOnly = true; textcol.Resizable = DataGridViewTriState.False; textcol.SortMode = DataGridViewColumnSortMode.NotSortable; controls.dgvEventList.Columns.Add(textcol); // focus on mouse enter expando.MouseEnter += delegate { BegmMisc.FocusWithoutScroll(controls.dgvEventList); }; controls.dgvEventList.MouseEnter += delegate { BegmMisc.FocusWithoutScroll(controls.dgvEventList); }; // don't draw focus rectangle controls.dgvEventList.RowPrePaint += delegate(object sender, DataGridViewRowPrePaintEventArgs e) { e.PaintParts &= ~DataGridViewPaintParts.Focus; }; // reveal other widgets on double click controls.dgvEventList.CellDoubleClick += dgvEventList_CellDoubleClick; expando.Items.Add(controls.dgvEventList); // event checkbox controls.cbShowAlerts = new CheckBox(); controls.cbShowAlerts.AutoSize = true; controls.cbShowAlerts.Text = Language.CurrentAttacks_ShowAlerts; if (Application.RenderWithVisualStyles) { controls.cbShowAlerts.Location = new Point(2, 21 + 2); } else { controls.cbShowAlerts.Location = new Point(2, 21 + 4); } if (options.Alerts.filterChokePoint == false || // not filtering, or options.Alerts.filterChokePointIDs.Contains(cp.ID)) // filtering and includes this cp { controls.cbShowAlerts.Checked = true; } controls.cbShowAlerts.Click += delegate { cbShowAlerts_Click(controls.cbShowAlerts, cp); }; expando.Controls.Add(controls.cbShowAlerts); // capbar controls.picCapBar = new PictureBox(); controls.picCapBar.Location = new Point(104, 21 + 2); controls.picCapBar.Size = new Size(130, 12); expando.Controls.Add(controls.picCapBar); // expand button controls.picExpandTown = new PictureBox(); controls.picExpandTown.Cursor = Cursors.Hand; controls.picExpandTown.Image = Resources.icon_down; if (Application.RenderWithVisualStyles) { controls.picExpandTown.Location = new Point(324, 21 + 5); } else { controls.picExpandTown.Location = new Point(324, 21 + 7); } controls.picExpandTown.SizeMode = PictureBoxSizeMode.AutoSize; controls.picExpandTown.MouseClick += delegate(object obj, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { picExpandTown_Click(expando); } }; expando.Controls.Add(controls.picExpandTown); // town status link LinkLabel link = new LinkLabel(); link.ActiveLinkColor = Color.White; link.LinkBehavior = LinkBehavior.HoverUnderline; link.LinkColor = Color.FromArgb(224, 224, 224); link.Text = Language.CurrentAttacks_TownStatus; link.TextAlign = ContentAlignment.TopRight; link.Size = new Size(90, 13); link.VisitedLinkColor = Color.FromArgb(224, 224, 224); if (Application.RenderWithVisualStyles) { link.Location = new Point(235, 21 + 1); } else { link.Location = new Point(235, 21 + 2); } link.LinkClicked += delegate { GameStatus_RevealWidget(WidgetType.TownStatus, cp); }; expando.Controls.Add(link); // store controls for future reference expando.Tag = controls; return(expando); }
/// <summary> /// Generate an info image for the given ChokePoint, showing facility status, /// deployed hcunits, and linked cps. /// </summary> /// <param name="cp">The reference ChokePoint.</param> /// <param name="imgmap">An ImageMap on which to add tooltips & clickable regions.</param> /// <returns>The info image.</returns> private Image GenerateInfoImage(ChokePoint cp, ImageMap.ImageMap imgmap) { #region Init // get data ReadOnlyCollection <Bridge> nearbyBridges = cp.GetNearbyBridges(game.Bridges); SortedList <float, ChokePoint> attackFrom = cp.AttackFrom; // calculate required dimensions const int lineHeight = 14; int linesRequired = 5; if (attackFrom.Count == 1) { linesRequired += 1; } else if (attackFrom.Count > 1) { linesRequired += 1 + attackFrom.Count; } if (cp.IsContested) { linesRequired += 2; // capbar } linesRequired += 1 + cp.Facilities.Count; if (cp.LinkedChokePoints.Count > 0) { linesRequired += 1 + cp.LinkedChokePoints.Count; } if (cp.DeployedHCUnits.Count > 0) { linesRequired++; HCUnit prevDivision = null; foreach (HCUnit hcunit in cp.DeployedHCUnits) { linesRequired++; HCUnit division = hcunit.Level == HCUnitLevel.Division ? hcunit : hcunit.ParentUnit; if (division != prevDivision) { linesRequired++; prevDivision = division; } } } if (nearbyBridges.Count > 0) { linesRequired += 1 + nearbyBridges.Count; } int height = (linesRequired * lineHeight) + 2; const int width = 334; // create bitmap, graphics resources Bitmap bitmap = new Bitmap(width, height); Graphics g = Graphics.FromImage(bitmap); g.SmoothingMode = SmoothingMode.HighQuality; g.InterpolationMode = InterpolationMode.High; Brush brushMain = new SolidBrush(Color.FromArgb(192, 192, 192)); Brush brushHead = new SolidBrush(Color.FromArgb(224, 224, 224)); Brush brushHCUnitAllied = new SolidBrush(Color.FromArgb(128, 204, 255)); // light blue Brush brushHCUnitAxis = new SolidBrush(Color.FromArgb(255, 178, 102)); // orange Pen penDark = new Pen(Color.FromArgb(80, 80, 80)); Font fontMain = new Font("Tahoma", 8.25F); Font fontHead = new Font("Tahoma", 8.25F, FontStyle.Bold); StringFormat alignRight = new StringFormat { Alignment = StringAlignment.Far }; StringFormat formatNoWrap = new StringFormat { FormatFlags = StringFormatFlags.NoWrap }; // prevents odd spacing when line is clipped DateTime oneHourAgo = DateTime.Now.AddHours(-1); DateTime twelveHoursAgo = DateTime.Now.AddHours(-12); imgmap.RemoveAll(); // remove previous tooltips int y = 0; int[] xTab = new[] { 2, 15, 32, 44 }; int stringWidth; #endregion #region General info // get x position for owner/controller values float xFlagPos = 80; // min stringWidth = (int)g.MeasureString(Language.Misc_OwnedBy, fontHead).Width; if (xTab[0] + stringWidth + 2 > xFlagPos) { xFlagPos = xTab[0] + stringWidth + 2; } stringWidth = (int)g.MeasureString(Language.Misc_ControlledBy, fontHead).Width; if (xTab[0] + stringWidth + 2 > xFlagPos) { xFlagPos = xTab[0] + stringWidth + 2; } xFlagPos = (float)Math.Round(xFlagPos); // owner g.DrawString(Language.Misc_OwnedBy, fontHead, brushHead, xTab[0], y); g.DrawImage(cp.Owner.CountryFlag, xFlagPos, y + 3.5F, 12, 7.125F); string captured = null; if (cp.LastOwnerChanged > twelveHoursAgo) // within past 12 hours { captured = " (" + String.Format(Language.Time_CapturedMinsAgo, Misc.MinsAgoLong(cp.LastOwnerChanged)) + ")"; } g.DrawString(String.Format("{0}{1}", cp.Owner, captured), fontMain, brushMain, xFlagPos + 15, y); y += lineHeight; // controller g.DrawString(Language.Misc_ControlledBy, fontHead, brushHead, xTab[0], y); g.DrawImage(cp.Controller.CountryFlag, xFlagPos, y + 3.5F, 12, 7.125F); string gained = null; if (cp.LastControllerChanged > twelveHoursAgo) // within past 12 hours { gained = " (" + String.Format(cp.Controller.Side == cp.Owner.Side ? Language.Time_RegainedMinsAgo : Language.Time_GainedMinsAgo, Misc.MinsAgoLong(cp.LastControllerChanged)) + ")"; } g.DrawString(String.Format("{0}{1}", cp.Controller, gained), fontMain, brushMain, xFlagPos + 15, y); y += lineHeight; // contested if (cp.IsContested) { g.DrawImage(Resources.contested, xTab[1], y + 2, 13, 10.52F); // 21x17 string since = null; if (cp.LastContestedChanged > twelveHoursAgo) // within past 12 hours { since = " (" + String.Format(Language.Time_ForMins, Misc.MinsAgoLong(cp.LastContestedChanged)) + ")"; } g.DrawString(Language.Misc_Contested + since, fontMain, brushMain, xTab[2], y); } else { g.DrawImage(Resources.uncontested, xTab[1], y + 2, 13, 10.52F); // 21x17 string since = null; if (cp.LastContestedChanged > twelveHoursAgo) // within past 12 hours { since = " (" + String.Format(Language.Time_ForMins, Misc.MinsAgoLong(cp.LastContestedChanged)) + ")"; } g.DrawString(Language.Misc_Uncontested + since, fontMain, brushMain, xTab[2], y); } y += lineHeight; // attack objective if (cp.HasAO) { g.DrawImage(Resources.attack_objective, xTab[1], y + 2, 12, 10.29F); // 21x18 string placed = null; if (cp.LastAOChanged > twelveHoursAgo) // within past 12 hours { placed = " (" + String.Format(Language.Time_PlacedMinsAgo, Misc.MinsAgoLong(cp.LastAOChanged)) + ")"; } g.DrawString(Language.TownStatus_AttackObjective + placed, fontMain, brushMain, xTab[2], y); string attackUnit = cp.AttackingHCUnit.Title; if (cp.AttackingHCUnit.DeployedChokePoint != null) { attackUnit += " " + String.Format(Language.Misc_AtChokePoint, cp.AttackingHCUnit.DeployedChokePoint); } string aoTooltip = String.Format(Language.TownStatus_Tooltip_AOPlacedBy, attackUnit); imgmap.AddRectangle(aoTooltip, new Rectangle(xTab[1], y + 2, 100, 10)); y += lineHeight; } else { g.DrawImage(Resources.no_attack_objective, xTab[1], y + 2, 12, 10.29F); // 21x18 string removed = null; if (cp.LastAOChanged > twelveHoursAgo) // within past 12 hours { removed = " (" + String.Format(Language.Time_RemovedMinsAgo, Misc.MinsAgoLong(cp.LastAOChanged)) + ")"; } g.DrawString(Language.TownStatus_NoAttackObjective + removed, fontMain, brushMain, xTab[2], y); y += lineHeight; } // activity level g.DrawImage(cp.ActivityImage, xTab[1], y + 2, 12, 10.8F); // 20x18 g.DrawString(Language.TownStatus_Activity + " " + Misc.EnumString(cp.Activity), fontMain, brushMain, xTab[2], y); y += lineHeight; // attack from if (attackFrom.Count > 0) { if (attackFrom.Count == 1) { g.DrawString(Language.TownStatus_UnderAttackFrom + " " + attackFrom.Values[0].Name, fontMain, brushMain, xTab[2], y); y += lineHeight; } else { g.DrawString(Language.TownStatus_UnderAttackFrom, fontMain, brushMain, xTab[2], y); y += lineHeight; for (int i = attackFrom.Count - 1; i >= 0; i--) { g.DrawString(String.Format("{0} ({1:0}%)", attackFrom.Values[i], attackFrom.Keys[i]), fontMain, brushMain, xTab[3], y); y += lineHeight; } } } #endregion #region Base counts int countArmybase = 0, countAirbase = 0, countNavalbase = 0; foreach (Facility facility in cp.Facilities) { if (facility.Type == FacilityType.Armybase) { countArmybase++; } else if (facility.Type == FacilityType.Airbase) { countAirbase++; } else if (facility.Type == FacilityType.Navalbase) { countNavalbase++; } } int yOffset = 0; if (countArmybase > 0) { g.DrawImage(Resources.armybase, 300, yOffset, 18, 16); g.DrawString("x" + countArmybase, fontMain, brushMain, 334, yOffset + 1, alignRight); yOffset += 18; } if (countAirbase > 0) { g.DrawImage(Resources.airbase, 300, yOffset, 18, 16); g.DrawString("x" + countAirbase, fontMain, brushMain, 334, yOffset + 1, alignRight); yOffset += 20; } if (countNavalbase > 0) { g.DrawImage(Resources.navalbase, 300, yOffset, 18, 16); g.DrawString("x" + countNavalbase, fontMain, brushMain, 334, yOffset + 1, alignRight); } #endregion #region Capbar if (cp.IsContested) { const int left = 19; const int right = 319; int range = right - left; int mid = (int)((100 - cp.PercentOwnership) * range / 100) + left; g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_red_left : Resources.capbar_blue_left, left - 4, y + 9, 4, 10); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_red : Resources.capbar_blue, new Rectangle(left, y + 9, mid - left, 10), new Rectangle(0, 0, 1, 10), GraphicsUnit.Pixel); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue : Resources.capbar_red, new Rectangle(mid, y + 9, right - mid, 10), new Rectangle(0, 0, 1, 10), GraphicsUnit.Pixel); g.DrawImage(cp.Owner.Side == Side.Allied ? Resources.capbar_blue_right : Resources.capbar_red_right, right, y + 9, 4, 10); g.DrawImage(Resources.capbar_pointer, mid - 13, y + 6, 27, 23); y += lineHeight + lineHeight; } #endregion #region Facility list int maxNameWidth = 255; // heading(s) g.DrawString(Language.TownStatus_Section_Facilities, fontHead, brushHead, xTab[0], y); if (cp.HasAO || cp.IsContested) { g.DrawString(Language.TownStatus_Tables, fontMain, brushHead, 252, y); maxNameWidth = 215; } if (cp.IsContested) { g.DrawString(Language.TownStatus_HeldFor, fontMain, brushHead, 250, y, alignRight); maxNameWidth = 160; } g.DrawString(Language.TownStatus_Spawn, fontMain, brushHead, 297, y); // facility loop foreach (Facility facility in cp.Facilities) { y += lineHeight; // owner flag & facility icon g.DrawImage(facility.Owner.CountryFlag, xTab[1], y + 3.5F, 12, 7.125F); g.DrawImage(facility.Icon, xTab[2], y + 2.5F, 11, 8.94F); // facility name g.DrawString(facility.Name, fontMain, brushMain, new RectangleF(xTab[3], y, maxNameWidth, lineHeight), formatNoWrap); // held for column if (cp.IsContested) { if (facility.LastOwnerChanged > twelveHoursAgo) // within past 12 hours { Brush brush; if (facility.LastOwnerChanged < oneHourAgo) { brush = new SolidBrush(Color.FromArgb(128, 128, 128)); } else { brush = new SolidBrush(Misc.GetFadedYellow(facility.LastOwnerChanged, 15, 192)); } g.DrawString(Misc.MinsAgoShort(facility.LastOwnerChanged), fontMain, brush, 250, y, alignRight); imgmap.AddRectangle(Misc.Timestamp(facility.LastOwnerChanged) + " " + String.Format(Language.TownStatus_Tooltip_CapturedBy, facility.LastCapper), new Rectangle(210, y + 2, 38, 10)); } else { g.DrawLine(penDark, 209, y + 6, 247.5F, y + 6); } } // table status column if (cp.HasAO || cp.IsContested) { if (facility.RadioTableUp) { g.DrawImage(Resources.table_up, 269, y + 2, 12, 10); } else { g.DrawImage(Resources.table_down, 269, y + 2, 12, 10); } } // spawnable column Image spawnImage; string spawnTooltip; if (!facility.SpawnableFacility) { spawnImage = Resources.spawn_nonspawnable; spawnTooltip = null; } else if (facility.Owner.Side == cp.Owner.Side) // friendly { if (facility.EnemySpawnable) { spawnImage = Resources.spawn_enemyspawnable; spawnTooltip = Language.TownStatus_Tooltip_SpawnSpawnable; } else if (facility.OwnerCanSpawn) { spawnImage = Resources.spawn_friendly; spawnTooltip = Language.TownStatus_Tooltip_SpawnFriendly; } else { spawnImage = Resources.spawn_closed; spawnTooltip = Language.TownStatus_Tooltip_SpawnClosed; } } else // enemy { if (facility.OwnerCanSpawn) { spawnImage = Resources.spawn_enemy; spawnTooltip = Language.TownStatus_Tooltip_SpawnEnemy; } else { spawnImage = Resources.spawn_closed; spawnTooltip = Language.TownStatus_Tooltip_SpawnClosed; } } g.DrawImage(spawnImage, 311, y + 2, 9, 9); if (spawnTooltip != null) { imgmap.AddRectangle(spawnTooltip, new Rectangle(311, y + 2, 9, 9)); } } // end facility loop #endregion #region Deployed hcunits if (cp.DeployedHCUnits.Count > 0) { int xTimeColumn = new BegmMisc.TextWidth(fontMain, 70, 230).MeasureAll(cp.DeployedHCUnits).MaxWidth + 100; y += lineHeight; g.DrawString(Language.TownStatus_Section_HCUnits, fontHead, brushHead, xTab[0], y); HCUnit prevDivision = null; foreach (HCUnit hcunit in cp.DeployedHCUnits) { y += lineHeight; HCUnit division = hcunit.Level == HCUnitLevel.Division ? hcunit : hcunit.ParentUnit; HCUnit brigade = hcunit.Level == HCUnitLevel.Brigade ? hcunit : hcunit.GetDummyDivHQBrigade(); if (division != prevDivision) // draw division heading w/ tooltip { prevDivision = division; g.DrawImage(division.Country.BrigadeFlag, xTab[1], y + 2.5F, 17.57F, 9); // 41x21 g.DrawString(division.Title, fontMain, brushMain, xTab[2], y); string divTooltip = null; if (division.IsDeployed) { if (division.DeployedChokePoint != cp) { divTooltip = String.Format(Language.Common_Tooltip_DivisionDeployed, division.DeployedChokePoint); } } else { divTooltip = Language.Common_Tooltip_DivisionNotDeployed; } if (divTooltip != null) { stringWidth = (int)g.MeasureString(division.Title, fontMain, width - 32).Width - 2; imgmap.AddRectangle(divTooltip, new Rectangle(xTab[2] + 1, y + 2, stringWidth, 10), true, division); } y += lineHeight; } // draw unit title, time w/ last moved tooltip g.FillEllipse(hcunit.Country.Side == Side.Allied ? brushHCUnitAllied : brushHCUnitAxis, xTab[2] + 4, y + 4, 5, 5); g.DrawString(brigade.Title, fontMain, brushMain, xTab[3], y); if (hcunit.HasLastMoved) { g.DrawString(Misc.MinsAgoShort(hcunit.LastMovedTime), fontMain, brushMain, xTimeColumn, y, alignRight); } string brigTooltip = hcunit.Tooltip; if (brigTooltip != null) { stringWidth = (int)g.MeasureString(brigade.Title, fontMain, width - xTab[3]).Width - 2; imgmap.AddRectangle(brigTooltip, new Rectangle(xTab[3] + 1, y + 2, stringWidth, 10), true, hcunit); } } } #endregion #region Linked cps/firebases if (cp.LinkedChokePoints.Count > 0) { // calc x position for kms column based on max town name length float xKmsColumn = 170; // min foreach (ChokePoint linkedChokePoint in cp.LinkedChokePoints) { stringWidth = (int)g.MeasureString(linkedChokePoint.Name, fontMain).Width; if (stringWidth + 100 > xKmsColumn) { xKmsColumn = stringWidth + 100; } } y += lineHeight; g.DrawString(Language.TownStatus_Section_LinkedTowns, fontHead, brushHead, xTab[0], y); foreach (ChokePoint linkedChokePoint in cp.LinkedChokePoints) { y += lineHeight; // owner flag g.DrawImage(linkedChokePoint.Owner.CountryFlag, xTab[1], y + 3.5F, 12, 7.125F); imgmap.AddRectangle(String.Format(Language.Common_Tooltip_GoToMap, linkedChokePoint.Name), new Rectangle(xTab[1], y + 3, 12, 8), true, linkedChokePoint.ID); // fb icon Firebase linkedFirebase = cp.GetFirebaseFrom(linkedChokePoint); string fbTooltip; if (linkedFirebase == null) { fbTooltip = String.Format(Language.TownStatus_Tooltip_FirebaseNone, cp, linkedChokePoint); } else // draw fb icon { Image fbImage; if (linkedFirebase.Link.State == FirebaseState.Inactive) { fbImage = Resources.facility_firebase; fbTooltip = String.Format(Language.TownStatus_Tooltip_FirebaseInactive, cp, linkedChokePoint); } else if (linkedFirebase.IsOpen) { fbImage = Resources.facility_firebase_open; fbTooltip = String.Format(Language.TownStatus_Tooltip_FirebaseUp, linkedChokePoint, cp, Misc.EnumString(linkedFirebase.Link.State).ToLower(), Misc.EnumString(linkedFirebase.Link.Side)); } else { fbImage = Resources.facility_firebase_closed; fbTooltip = String.Format(Language.TownStatus_Tooltip_FirebaseDown, linkedChokePoint, cp, Misc.EnumString(linkedFirebase.Link.State).ToLower(), Misc.EnumString(linkedFirebase.Link.Side)); } g.DrawImage(fbImage, xTab[2], y + 2.5F, 11, 8.94F); } imgmap.AddRectangle(fbTooltip, new Rectangle(xTab[2], y + 2, 11, 9)); // linked cp name g.DrawString(linkedChokePoint.Name, fontMain, brushMain, xTab[3], y); stringWidth = (int)g.MeasureString(linkedChokePoint.Name, fontMain, width - xTab[3]).Width - 2; imgmap.AddRectangle(String.Format(Language.Common_Tooltip_GoToStatus, linkedChokePoint.Name), new Rectangle(xTab[3] + 1, y + 2, stringWidth, 10), true, linkedChokePoint); // distance double distance = Misc.DistanceBetween(cp.LocationOctets, linkedChokePoint.LocationOctets) * 0.8; // x 800m / 1000m g.DrawString(distance.ToString("0.0 km"), fontMain, brushMain, (int)xKmsColumn, y, alignRight); } } #endregion #region Nearby bridges if (nearbyBridges.Count > 0) { y += lineHeight; g.DrawString(Language.TownStatus_Section_Bridges, fontHead, brushHead, xTab[0], y); foreach (Bridge bridge in nearbyBridges) { y += lineHeight; g.DrawImage(Resources.bridge, xTab[2], y + 3, 11, 9); g.DrawString(bridge.Name, fontMain, brushMain, xTab[3], y); } } #endregion g.Dispose(); return(bitmap); }
/// <summary> /// Updates the CurrentAttacks widget when there is new capture data in GameEvents.List. /// Adds/removes expandos as needed, and updates each event list. /// </summary> /// <param name="init">If false, newly added ChokePoints will be expanded.</param> private void UpdateWidget(bool init) { if (this.game == null) { return; // not loaded } // get sorted list of cp's to display List <ChokePoint> cplist = GetChokePointsToDisplay(); cplist.Sort(); // add any new expandos bool expandosAdded = false; if (init || game.Wiretap.NewCaptureData || game.Wiretap.NewHCUnitData) { foreach (ChokePoint cp in cplist) { if (expandos.ContainsKey(cp)) { continue; } Expando newExpando = CreateExpando(cp); tskAttacks.Expandos.Add(newExpando); expandos.Add(cp, newExpando); if (!init) { Log.AddEntry("Adding {0} to current attacks", cp); } expandosAdded = true; } } // remove any old expandos ChokePoint[] keys = new ChokePoint[expandos.Keys.Count]; expandos.Keys.CopyTo(keys, 0); // must copy as we can't modify while enumerating tmrRemoveChokePoint.Tag = new List <Expando>(); foreach (ChokePoint cp in keys) { if (cplist.Contains(cp)) { continue; } Expando oldExpando = expandos[cp]; expandos.Remove(cp); if (oldExpando.Collapsed && !oldExpando.Animating) // already collapsed, remove { tskAttacks.Expandos.Remove(oldExpando); } else // animate collapsed, start timer to remove when finished { oldExpando.Collapsed = true; ((List <Expando>)tmrRemoveChokePoint.Tag).Add(oldExpando); tmrRemoveChokePoint.Start(); } if (!init) { Log.AddEntry("Removing {0} from current attacks", cp); } } // sort expandos, if we've added any if (expandosAdded) { for (int i = 0; i < cplist.Count; i++) { Expando expando = expandos[cplist[i]]; tskAttacks.Expandos.Move(expando, i); } } // update each chokepoint if (init || game.Wiretap.NewCaptureData || game.Wiretap.NewHCUnitData) // update everything, repopulate event list { foreach (ChokePoint cp in cplist) { UpdateChokePoint(cp); } } else // just refresh "x mins ago" tooltips { foreach (ChokePoint cp in cplist) { UpdateTooltip(cp); } } // update parent expando's animation heights ((Expando)this.Parent).CalcAnimationHeights(); }
/// <summary> /// Calculate the neighbouring ChokePoints for each cp. /// </summary> private void GenerateNearbyChokePoints(IList <ChokePoint> todo, IList <ChokePoint> allCps) { for (int iCurrent = 0; iCurrent < todo.Count; iCurrent++) { if (todo[iCurrent] == null) { continue; } ChokePoint cpCurrent = todo[iCurrent]; // get list of all nearby cps, sorted by angle, and the distance to each SortedList <double, ChokePoint> allnearby = new SortedList <double, ChokePoint>(); Dictionary <ChokePoint, double> distances = new Dictionary <ChokePoint, double>(); // initially populate with linked cps foreach (ChokePoint cpLinked in cpCurrent.LinkedChokePoints) { double distance = Misc.DistanceBetween(cpCurrent.LocationOctets, cpLinked.LocationOctets); double angle = Misc.AngleBetween(cpCurrent.LocationOctets, cpLinked.LocationOctets); if (distance > Frontline.MAX_LINK_DIST) { continue; // don't include eg cross channel links } if (allnearby.ContainsKey(angle)) { angle += 0.001; // allow dupes } allnearby.Add(angle, cpLinked); distances.Add(cpLinked, distance); } // then add any other cps within MAXDISTANCE for (int iNear = 0; iNear < allCps.Count; iNear++) { if (allCps[iNear] == null) { continue; } ChokePoint cpNear = allCps[iNear]; if (cpNear.ID == cpCurrent.ID) { continue; // self } if (distances.ContainsKey(cpNear)) { continue; // linked cp } // get distance double distance = Misc.DistanceBetween(cpCurrent.LocationOctets, cpNear.LocationOctets); if (distance > Frontline.MAX_DISTANCE) { continue; } // check angle double angle = Misc.AngleBetween(cpCurrent.LocationOctets, cpNear.LocationOctets); if (allnearby.ContainsKey(angle)) // dupe angle { ChokePoint cpDupe = allnearby[angle]; if (cpCurrent.LinkedChokePoints.Contains(cpDupe)) { continue; // cpDupe is a linked cp, skip cpNear } else if (distance < distances[cpDupe]) { allnearby.Remove(angle); // cpNear is closer, remove cpDupe } else { continue; // cpDupe is closer, skip cpNear } } // append to lists allnearby.Add(angle, cpNear); distances.Add(cpNear, distance); } // assign result to chokepoint object cpCurrent.NearbyChokePoints = allnearby.Values; } // end cpCurrent // correct all 1-way links to make symmetric foreach (ChokePoint cpCurrent in todo) { if (cpCurrent == null) { continue; } List <ChokePoint> toFix = new List <ChokePoint>(); foreach (ChokePoint cpNear in cpCurrent.NearbyChokePoints) { if (!cpNear.IsNear(cpCurrent)) { toFix.Add(cpNear); } } foreach (ChokePoint cpNear in toFix) { if (cpCurrent.ID >= this.ChokePoints.Length) // dummy cp, add reverse link { cpNear.AddNearCP(cpCurrent); } else // normal cp, remove 1-way link { cpCurrent.RemoveNearCP(cpNear); } } } }
/// <summary> /// Select the given ChokePoint in lbCPList and center it. /// </summary> /// <param name="cp">The ChokePoint to find in the list.</param> private void CenterSelection(ChokePoint cp) { lbCPList.SelectedItem = cp; lbCPList.TopIndex = lbCPList.SelectedIndex < 3 ? 0 : lbCPList.SelectedIndex - 3; }