public override void handleMouseDown(Point mouseLocation, PlayFrame frame, PitchScreenCoordConverter converter, VisualOverlay overlay) { if (mSelectedPlayer == null) { PointF pitchCoords = converter.screenToPitchCoords(mouseLocation); // No player is yet selected so attempt to find the one closest // to the selected spot. mSelectedPlayer = frame.GetClosestPlayer(pitchCoords, Settings.Default.PlayerDiameter * 2.0f); if (mSelectedPlayer == null) { LinearMovement prevCut = frame.GetClosestCutEnd(pitchCoords, Settings.Default.PlayerDiameter * 2.0f); if (prevCut != null && prevCut != frame.DiscFrameMovement.ReceivingCut) { mSelectedPlayer = prevCut.Player; overlay.DrawingNewCut = true; overlay.CutStart = prevCut; } } else { overlay.DrawingNewCut = true; overlay.CutStart = frame.PlayerMovement[mSelectedPlayer][0]; } } }
/// <summary> /// Clears the overlay so that nothing is displayed on top of the /// design. /// /// Should be called whenever a tool completes. /// </summary> internal void Clear() { TriggerPlayer = null; SelectedTrigger = null; SelectedPlayer = null; CutStart = null; DrawingNewCut = false; DrawingDiscMovement = false; PlacingDisc = false; PlacingTrigger = false; MovingDiscControlPoint = false; }
/// <summary> /// In order to move an item we catch the mouse down event to decide which /// thing has been selected. This function chooses either the selected /// cut, trigger or player. /// </summary> /// <param name="mouseLocation">Screen coordinates.</param> /// <param name="frame">Frame currently being designed.</param> /// <param name="converter">Used to convert from screen to pitch /// coordinates.</param> public override void handleMouseDown(Point mouseLocation, PlayFrame frame, PitchScreenCoordConverter converter, VisualOverlay overlay) { PointF pitchCoords = converter.screenToPitchCoords(mouseLocation); // Choose closest selected item. mSelectedPlayer = frame.GetClosestPlayer(pitchCoords, SELECTION_MAX_DISTANCE); mSelectedCut = frame.GetClosestCutEnd(pitchCoords, SELECTION_MAX_DISTANCE); mSelectedTrigger = frame.GetClosestTrigger(pitchCoords, SELECTION_MAX_DISTANCE); mIsMovingDiscControlPoint = frame.IsNearControlPoint(pitchCoords, SELECTION_MAX_DISTANCE); // Note that the ordering of priorities here MUST be the same as the // ordering on mouse up. if (mSelectedTrigger != null) { overlay.SelectedTrigger = mSelectedTrigger; overlay.TriggerPlayer = mSelectedTrigger.AffectedPlayer; } else if (mSelectedCut != null) { overlay.DrawingNewCut = false; overlay.CutStart = mSelectedCut; } else if (mSelectedPlayer != null) { overlay.SelectedPlayer = mSelectedPlayer; } else if (mIsMovingDiscControlPoint) { overlay.MovingDiscControlPoint = true; } }
/// <summary> /// Given a position on the pitch and a player who is going to be moved we /// are able to check whether the trigger lies on a cut from any other /// player and if so to create a new trigger at that position so that the /// player moves when the trigger is hit. /// </summary> /// <param name="pitchCoords"></param> /// <param name="maxDistanceFromLine"></param> /// <param name="affectedPlayer"></param> /// <returns>True if a trigger was created and false otherwise.</returns> public Boolean MaybeCreateTrigger(PointF pitchCoords, float maxDistanceFromLine, Player affectedPlayer) { Boolean foundCut = false; CutRatio cutRatio = GetClosestCutPoint(pitchCoords, maxDistanceFromLine, affectedPlayer); if (cutRatio != null) { foundCut = true; Trigger trigger = new Trigger(cutRatio, affectedPlayer); Triggers.Add(trigger); } return foundCut; }
/// <summary> /// Retrieves the closest point on any cut to the given coordinates. This /// is used for placing triggers and indicating where the disc should go /// </summary> /// <param name="pitchCoords"></param> /// <param name="selectionMaxDistance"></param> /// <param name="playerToIgnore">Compares the unique ids rather than /// references.</param> /// <param name="selectionMinDistance"></param> /// <returns></returns> public CutRatio GetClosestCutPoint(PointF pitchCoords, float selectionMaxDistance, Player playerToIgnore, float selectionMinDistance = -1.0f) { CutRatio cutRatio = null; LinearMovement previousMovement = null; float minDistanceFromLine = selectionMaxDistance; LinearMovement closestMovement = null; LinearMovement closestPreviousMovement = null; Player closestPlayer = null; PointF triggerPoint = new PointF(); foreach (Player player in PlayerMovement.Keys.Where(player => (playerToIgnore == null) || (player.UniqueId != playerToIgnore.UniqueId))) { List<LinearMovement> playerCut = PlayerMovement[player]; if (playerCut.Count > 1) { previousMovement = playerCut[0]; PointF startCut = playerCut[0].FinalPosition; foreach (LinearMovement cutSection in PlayerMovement[player].GetRange(1, playerCut.Count - 1)) { PointF endCut = cutSection.FinalPosition; PointF closestPoint = GeometryUtils.ClosestPointLineSegment(startCut, endCut, pitchCoords); float distanceFromCut = GeometryUtils.DistBetweenPoints(closestPoint, pitchCoords); if (distanceFromCut < minDistanceFromLine && distanceFromCut > selectionMinDistance) { minDistanceFromLine = distanceFromCut; closestMovement = cutSection; closestPlayer = player; closestPreviousMovement = previousMovement; triggerPoint = closestPoint; } startCut = cutSection.FinalPosition; previousMovement = cutSection; } } } if (closestPlayer != null) { float cutLength = GeometryUtils.DistBetweenPoints(closestMovement.FinalPosition, closestPreviousMovement.FinalPosition); float distanceToTrigger = GeometryUtils.DistBetweenPoints(closestPreviousMovement.FinalPosition, triggerPoint); cutRatio = new CutRatio(); cutRatio.CausingCut = closestMovement; cutRatio.PreviousCut = closestPreviousMovement; cutRatio.RatioAlongCut = Math.Abs(distanceToTrigger / cutLength); cutRatio.Player = closestPlayer; } return cutRatio; }
/// <summary> /// Overriding the onpopup handler (gets called before the menu is /// displayed) to set up the menu based on what was clicked on. /// /// This is to get around the fact that each player and cut is not /// actually a control. /// </summary> /// <param name="e"></param> protected override void OnPopup(EventArgs e) { this.MenuItems.Clear(); if (mViewPanel.IsDesignMode) { // The mouse location comes in here as the location on the entire application // We need to use PointToClient to convert it to mouse location on the view // panel. Point mouseLocation = Cursor.Position; mMouseLocation = mViewPanel.PointToClient(mouseLocation); PointF pitchCoordinates = mViewPanel.Converter.screenToPitchCoords(mMouseLocation); PlayFrame currentFrame = mViewPanel.CurrentFrame; // Find out what was near the right click. mClickedPlayer = currentFrame.GetClosestPlayer(pitchCoordinates, Settings.Default.PlayerDiameter); mClickedTrigger = currentFrame.GetClosestTrigger(pitchCoordinates, Settings.Default.PlayerDiameter); mClickedCutRatio = mViewPanel.CurrentFrame.GetClosestCutPoint(pitchCoordinates, Settings.Default.PlayerDiameter, null); bool discPathClicked = currentFrame.IsOnFlightPath(pitchCoordinates, Settings.Default.PlayerDiameter); if (mClickedPlayer != null) { SetUpPlayerMenu(); } if (mClickedTrigger != null) { SetUpTriggerMenu(); } if (mClickedCutRatio != null) { SetUpCutMenu(); } if (discPathClicked) { SetUpDiscFlightMenu(); } if (currentFrame.CanDrawCut(pitchCoordinates, Settings.Default.PlayerDiameter, 0.0f)) { SetUpNewCutMenu(); } base.OnPopup(e); } }
public LinearMovement(float x, float y, int speedPercentage, Player player) { FinalPosition = new PointF(x, y); SpeedPercentage = speedPercentage; Player = player; }
internal void ClearTrigger(Player clickedPlayer) { viewPanel.CurrentFrame.Triggers.RemoveAll(trigger => trigger.AffectedPlayer == clickedPlayer); viewPanel.Refresh(); ModelChanged(); }
public PlaceCutTool(PictureBox toolPicture, Player player) : this(toolPicture) { mSelectedPlayer = player; }
/// <summary> /// Checks whether a given player is the thrower. /// </summary> /// <param name="player">Any player object.</param> /// <returns>True if the player is the thrower. False is no thrower or /// player is not the thrower</returns> internal bool IsThrower(Player player) { return (DiscFrameMovement.Thrower == player); }
internal CyclicDataModelException(Player player) { mPlayer = player; }
/// <summary> /// Adds a new entry in the player delay table for the given player if /// one does not already exist. /// /// If one DOES exist then it only updates it if this delay is larger. /// </summary> /// <param name="player">The player who is being delayed</param> /// <param name="delay">The number of cycles before the player starts /// moving.</param> /// <returns>True if an addition or update made. False otherwise.</returns> private Boolean AddUpdateDelay(Player player, int delay) { if (mPlayerDelays.ContainsKey(player)) { if (mPlayerDelays[player] < delay) { mPlayerDelays[player] = delay; return true; } else { return false; } } else { mPlayerDelays.Add(player, delay); return true; } }
/// <summary> /// We capture the mouse up event and use it to place whatever it was that /// was selected on the mouse down event. If nothing was selected this does /// nothing. /// </summary> /// <param name="mouseLocation"></param> /// <param name="frame"></param> /// <param name="converter"></param> public override void handleMouseUp(Point mouseLocation, PlayFrame frame, PitchScreenCoordConverter converter, VisualOverlay overlay) { PointF pitchCoords = converter.screenToPitchCoords(mouseLocation); if (mSelectedTrigger != null) { // If we are moving a trigger then only move it if the position it is // being placed is valid. if (frame.MaybeCreateTrigger(pitchCoords, SELECTION_MAX_DISTANCE, mSelectedTrigger.AffectedPlayer)) { frame.RemoveTrigger(mSelectedTrigger); ModelChanged = true; } } else if (mSelectedPlayer != null) { frame.PlayerMovement[mSelectedPlayer][0].FinalPosition = pitchCoords; ModelChanged = true; } else if (mSelectedCut != null) { mSelectedCut.FinalPosition = pitchCoords; ModelChanged = true; } else if (mIsMovingDiscControlPoint) { frame.DiscFrameMovement.ControlPoint = pitchCoords; ModelChanged = true; } // Regardless of whether anything moved the move tool is complete on // mouse up. mSelectedCut = null; mSelectedPlayer = null; mSelectedTrigger = null; IsComplete = true; }
/// <summary> /// Creates a dialog box that allows the user to set the speed of a given /// player within certain limits. /// </summary> /// <param name="player"></param> internal void SetPlayerSpeed(Player player) { using (SpeedDialog dialog = new SpeedDialog(Settings.Default.MinPlayerSpeed, Settings.Default.MaxPlayerSpeed, player.MaxSpeed)) { if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { player.MaxSpeed = dialog.GetSpeed(); } } }
/// <summary> /// Called when we want to delete a specific player. /// </summary> /// <param name="player"></param> internal void DeletePlayer(Player player) { viewPanel.CurrentFrame.RemovePlayer(player); viewPanel.Refresh(); ModelChanged(); }
/// <summary> /// Remove a single player from the frame including anything attached to /// the player /// </summary> /// <param name="player"></param> public void RemovePlayer(Player player) { // If the player being removed was the one with the disc then we need // to remove the disc movement completely. // // If they were the one receiving the disc then we need to reset the // disc movement. if (this.DiscFrameMovement.Thrower != null && this.DiscFrameMovement.Thrower.Equals(player)) { DiscFrameMovement = new DiscMovement(this); } else if (DiscFrameMovement.ReceivingCut != null && DiscFrameMovement.ReceivingCut.Player.Equals(player)) { DiscFrameMovement.ReceivingCut = null; DiscFrameMovement.HasMoved = false; } // Delete any triggers which reference this player at all Triggers.RemoveAll(trigger => trigger.AffectedPlayer == player || trigger.CausingCutRatio.Player == player); PlayerMovement.Remove(player); }
/// <summary> /// This is a utilty function which generates the list of points that a /// player passes through on the single frame. /// /// It includes the padding at the start (as a parameter). /// </summary> /// <param name="player"></param> /// <param name="cycleStart">The number of stationary cycles for this /// player.</param> /// <returns>A list of points that the player passes through.</returns> private List<ItemPlayData> GeneratePlayerPoints(Player player, int cycleStart) { List<ItemPlayData> playerData = new List<ItemPlayData>(); Boolean isFirstCut = true; PointF startPoint = new PointF(); // If the player has a trigger value stored off from previous // calculations then this is the number of stationary cycles. if (mPlayerDelays.ContainsKey(player)) { cycleStart = mPlayerDelays[player]; } foreach (LinearMovement cut in mPlayerMovement[player]) { List<Trigger> applicableTriggers = new List<Trigger>(); List<PointF> playerPoints = new List<PointF>(); // For each cut that we process we need to check if any trigger was // fired during that cut. applicableTriggers.AddRange(mTriggers.Where( trigger => trigger.CausingCutRatio.CausingCut == cut)); // The first cut is actually just a placeholder to indicate where // the player starts out on the field. if (isFirstCut) { startPoint = cut.FinalPosition; playerPoints.Add(startPoint); // Add on the number of cycles for which the player should stay at // the start location. We add on 1 so that the player spends at LEAST // one cycle at their start location. for (int ii = 0; ii < cycleStart + 1; ii++) { playerData.Add(new PlayerPlayData(startPoint, player.PlayerTeam.Sprite, Settings.Default.PlayerDiameter, Settings.Default.PlayerDiameter, player.VisibleID, player.Name, player.PlayerTeam)); } isFirstCut = false; } else { playerPoints = cut.GeneratePoints(startPoint, PlayThread.MAX_FRAME_TIME / 4.0, player.MaxSpeed); foreach (PointF point in playerPoints) { playerData.Add(new PlayerPlayData(point, player.PlayerTeam.Sprite, Settings.Default.PlayerDiameter, Settings.Default.PlayerDiameter, player.VisibleID, player.Name, player.PlayerTeam)); // Compare the point to each of the triggers fired during this cut // noting that since these are PointFs then a comparison can't be // absolute. foreach (Trigger trigger in applicableTriggers) { PointF triggerPoint = trigger.CausingCutRatio.GetAbsolutePosition(); if (GeometryUtils.DistBetweenPoints(triggerPoint, point) < player.MaxSpeed * PlayThread.MAX_FRAME_TIME / 1000.0f) { AddUpdateDelay(trigger.AffectedPlayer, playerData.Count); } } } startPoint = cut.FinalPosition; } } return playerData; }
/// <summary> /// Removes all of the cuts from a given starting location and a given /// player. Also removes and triggers or disc movement to those cuts. /// </summary> /// <param name="clickedPlayer"></param> /// <param name="clickedCut">This cut will be removed as well.</param> internal void ClearCuts(Player clickedPlayer, LinearMovement clickedCut = null) { int totalCuts; int index; if (clickedCut == null) { // -1 because the first in the list is the starting location. index = 1; } else { index = PlayerMovement[clickedPlayer].IndexOf(clickedCut); } totalCuts = PlayerMovement[clickedPlayer].Count - index; // First we remove anything attached to the cuts that we're about to // delete and then we delete the cuts themselves. PlayerMovement[clickedPlayer].GetRange(index, totalCuts).ForEach(ClearSingleCut); PlayerMovement[clickedPlayer].RemoveRange(index, totalCuts); }
public Trigger(CutRatio cutRatio, Player affectedPlayer) { CausingCutRatio = cutRatio; AffectedPlayer = affectedPlayer; UniqueId = NextUniqueId++; }
/// <summary> /// Clears the disc of all movement and resets the speed to default. /// </summary> internal void Clear() { ClearFlightPath(); Thrower = null; }
/// <summary> /// Draw a single player onto the given display object. The conversion /// from pitch to screen coordinates is done internally. /// </summary> /// <param name="player"></param> /// <param name="position"></param> /// <param name="display"></param> /// <param name="converter"></param> /// <param name="isSelected">A selected player shows a slightly different /// sprite to indicate the selection.</param> /// <param name="isOutline">Set to true to draw only the outline of the /// player.</param> internal void DrawPlayer(Player player, PointF position, Graphics display, PitchScreenCoordConverter converter, Boolean isOutline) { Image playerImage = isOutline ? player.PlayerTeam.OutlineSprite : player.PlayerTeam.Sprite; PointF offsetCentre = new PointF(position.X, position.Y); offsetCentre.X -= Settings.Default.PlayerDiameter / 2.0f; offsetCentre.Y -= Settings.Default.PlayerDiameter / 2.0f; DrawSprite(display, converter, playerImage, offsetCentre, Settings.Default.PlayerDiameter, Settings.Default.PlayerDiameter); using (Font font = CreatePlayerIdFont(converter)) { // Draw the player id as an overlay onto the sprite. DrawString(display, converter, player.VisibleID.ToString(CultureInfo.InvariantCulture), offsetCentre, font, Brushes.White); } }
/// <summary> /// Retrieve the upmost parent of the player in the tree. This relies on /// each player being triggered by at most one other player. /// /// The parent of a player is deemed to be the player who triggers the /// current player. /// /// The upmost parent is what we get if we continue this processing up the /// tree until we find a player who has no parent. /// </summary> /// <param name="player">The player to search for.</param> /// <returns>The current playe if no parents.</returns> private Player GetParent(Player player) { Player parent = player; Boolean isFinished; do { isFinished = true; foreach (Trigger trigger in mTriggers) { if (trigger.AffectedPlayer == parent) { parent = trigger.CausingCutRatio.Player; isFinished = false; break; } } } while (player != parent && !isFinished); return parent; }
/// <summary> /// Clear all cuts up the specified one for a given player. /// </summary> /// <param name="clickedPlayer"></param> /// <param name="clickedCut"></param> internal void ClearCuts(Player clickedPlayer, LinearMovement clickedCut = null) { viewPanel.CurrentFrame.ClearCuts(clickedPlayer, clickedCut); viewPanel.Refresh(); ModelChanged(); }
public PlaceTriggerTool(PictureBox toolPicture, Player player) : base(toolPicture) { mSelectedPlayer = player; }
private void ProcessPlayer(Player player) { // Since we process the players in a tree structure if a player is hit // more than once then there is a cycle which will not be possible to // resolve. This should have been prevented at the design level so // we throw an exception. if (mProcessedPlayers.Contains(player)) { throw new CyclicDataModelException(player); } int cycleStart = 0; if (mPlayerDelays.ContainsKey(player)) { cycleStart = mPlayerDelays[player]; } mFramePlayData.PlayData.Add(GeneratePlayerPoints(player, cycleStart)); mProcessedPlayers.Add(player); foreach (Trigger trigger in mTriggers.Where( trigger => trigger.CausingCutRatio.Player == player)) { ProcessPlayer(trigger.AffectedPlayer); } }
/// <summary> /// Render a small square for a player. This is used when we're rendering /// into a small space. /// </summary> /// <param name="graphics"></param> /// <param name="player"></param> /// <param name="location"></param> /// <param name="converter"></param> private void DrawSmallPlayer(Graphics graphics, Player player, PointF location, PitchScreenCoordConverter converter) { Point screenCoords = converter.pitchToScreenCoords(location); Brush playerBrush = player.PlayerTeam.UniqueId == Team.RED_TEAM.UniqueId ? Brushes.Red : Brushes.Blue; graphics.FillRectangle(playerBrush, screenCoords.X, screenCoords.Y, 4, 4); }
/// <summary> /// Adds the disc to the frame on a given player. Sets all of the receiving /// elements to null so that the disc does not move even if it wasn't /// deleted properly previously. /// </summary> /// <param name="player"></param> public void AddDisc(Player player) { DiscFrameMovement.Thrower = player; DiscFrameMovement.ClearFlightPath(); }
public LinearMovement(PointF finalPosition, int speedPercentage, Player player) { FinalPosition = finalPosition; SpeedPercentage = speedPercentage; Player = player; }
/// <summary> /// Adds a single player to the frame and sets the first /// </summary> /// <param name="player"></param> /// <param name="startingPosition"></param> public void AddPlayer(Player player, PointF startingPosition) { PlayerMovement.Add(player, new List<LinearMovement>()); PlayerMovement[player].Add(new LinearMovement(startingPosition, 0, player)); }