⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 boardctrl.cs

📁 C#开发的运行于windows mobile PDA上的游戏
💻 CS
字号:
////////////////////////////////////////////////
// 
// Project: Lines.NET
// Version: 1.1
// Author:  Vladimir L.
// 
// homepage: http://www.boomsoft.org
// e-mail:   support@boomsoft.org
// 
// Copyright (c) 2003-2004, Boomsoft.org
// 

using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using Lines.Core;

namespace Lines.GUI
{
	/// <summary>
	/// Represents the game board on the screen.
	/// </summary>
	/// <remarks>
	/// <p>This control renders the game board on the screen using <see cref="BallCtrl.OnPaint(PaintEventArgs)"/> of 
	/// <see cref="BallCtrl"/> class to render balls in appropriate locations.</p>
	/// <p>It is also responsible for handling user interaction. The user actions are transformed to 
	/// <see cref="BoardCtrl.CellClick"/> and <see cref="BoardCtrl.BallClick"/> events in response to
	/// appropriate player activity.</p>
	/// </remarks>
	public class BoardCtrl : GenericCtrl
	{
		/// <summary>
		/// Represents the method that will handle eventes related to the field (board control).
		/// </summary>
		public delegate void FildEventHandler(BoardCtrl board);
		/// <summary>
		/// Represents the method that will handle <b>CellClick</b> event of a board.
		/// </summary>
		public delegate void CellEventHandler(BoardCtrl board, Point cell);
		/// <summary>
		/// Represents the method that will handle <b>BallClick</b> event of a board.
		/// </summary>
		public delegate void BallEventHandler(BoardCtrl board, BallCtrl ball);

		#region Events
		/// <summary>
		/// Occurs when player has clicked on an empty cell (board square).
		/// </summary>
		public event CellEventHandler CellClick;
		/// <summary>
		/// Occurs when player has clicked on a ball that is placed on the board.
		/// </summary>
		public event BallEventHandler BallClick;
		#endregion

		/// <summary>
		/// Holds references to all ball controls that are currently displayed.
		/// </summary>
		private ArrayList balls = new ArrayList();

		/// <summary>
		/// Creates an instance of BoardCtrl class.
		/// </summary>
		public BoardCtrl()
		{
			this.MouseDown += new MouseEventHandler(this.OnMouseDown);
		}

		/// <summary>
		/// Renders the control on the screen.
		/// </summary>
		/// <param name="e">Used to get reference to the Graphics object.</param>
		protected override void OnPaint(PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			
			if (e.ClipRectangle.Width < ((int)GetCellSize() + 1))
			{
				// Redraw clip rectangle only
				
				// Draw field lines (has to be done)
				//DrawFieldLines(g);

				// Draw balls
				g.FillRectangle(new SolidBrush(Preferences.FieldBgColor), e.ClipRectangle);
				for (int i = 0; i < balls.Count; i++)
				{
					BallCtrl ball = (BallCtrl)balls[i];
					if (e.ClipRectangle.IntersectsWith(ball.Bounds))
					{
						ball.OnPaint(e);
					}
				}

				// Draw virtual balls
				if (Game.VirtualBalls != null)
				{
					for (int i = 0; i < Game.VirtualBalls.Length; i++)
					{
						Rectangle rect = GetCellRect(Game.VirtualBalls[i].Position);
						if (e.ClipRectangle.IntersectsWith(rect))
						{
							DrawVirtualBall(g, Game.VirtualBalls[i]);
						}
					}
				}
			}
			else
			{
				// Draw frame
				Rectangle frame = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
				g.DrawRectangle(new Pen(Color.DimGray), frame); // Color.RosyBrown
				
				// Draw field frame
				Pen fieldPen = new Pen(Preferences.FieldFgColor);
				Rectangle fieldRect = GetFieldRect();
				g.FillRectangle(new SolidBrush(Preferences.FieldBgColor), fieldRect);
				g.DrawRectangle(fieldPen, fieldRect);

				// Draw field lines
				DrawFieldLines(g);

				// Draw balls
				for (int i = 0; i < balls.Count; i++)
				{
					((BallCtrl)balls[i]).OnPaint(e);
				}

				// Draw virtual balls
				if (Game.VirtualBalls != null)
				{
					for (int i = 0; i < Game.VirtualBalls.Length; i++)
					{
						DrawVirtualBall(g, Game.VirtualBalls[i]);
					}
				}
			}
		}

		/// <summary>
		/// Helper method to draw lines that copose the playing field.
		/// </summary>
		/// <param name="g">The reference to graphics object to paint on.</param>
		private void DrawFieldLines(Graphics g)
		{
			Size gameSize = Game.Board.BoardSize;
			Rectangle fieldRect = GetFieldRect();
			Pen fieldPen = new Pen(Preferences.FieldFgColor);
			float size = GetCellSize();

			// Draw horizontal lines
			for (int i = 0; i < gameSize.Width; i++)
			{
				g.DrawLine(
					fieldPen, 
					(int)(i * size) + fieldRect.Left, 
					fieldRect.Top, 
					(int)(i * size) + fieldRect.Left, 
					fieldRect.Bottom);
			}
			// Draw vertical lines
			for (int i = 0; i < gameSize.Height; i++)
			{
				g.DrawLine(
					fieldPen, 
					fieldRect.Left, 
					(int)(i * size) + fieldRect.Top, 
					fieldRect.Right, 
					(int)(i * size) + fieldRect.Top);
			}
		}

		/// <summary>
		/// Renders the "virtual" ball, the ball that will appear on the next step of the game.
		/// It appears on the screen (board) as a ball of appropriate color but smaller size.
		/// </summary>
		/// <param name="g">The reference to graphics object to paint on.</param>
		/// <param name="ball">The virtual ball to be rendered.</param>
		private void DrawVirtualBall(Graphics g, Ball ball)
		{
			Rectangle rect = GetCellRect(ball.Position);
			rect.Inflate(-rect.Width / 3, -rect.Height / 3);
			g.FillEllipse(new SolidBrush(Preferences.GetBallColor(ball.Color)), rect);
		}

		/// <summary>
		/// Handles resize event. Recalculates all ball controls' positions.
		/// </summary>
		/// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);
			// recalculate ball positions
			for (int i = 0; i < balls.Count; i++)
			{
				BallCtrl ballCtrl = (BallCtrl)balls[i];
				ballCtrl.Bounds = GetCellRect(ballCtrl.Ball.Position);
			}
		}

		/// <summary>
		/// Adds a new ball to the board control.
		/// </summary>
		/// <param name="ball">The ball to be added and rendered.</param>
		/// <remarks>
		/// This method creates an object of <see cref="BallCtrl"/> class to render the ball on the 
		/// screen, adds this control to the displayed balls collection <see cref="balls"/>, and redraws
		/// the region on the screen where this ball should be placed.
		/// </remarks>
		public void AddNewBall(Ball ball)
		{
			BallCtrl ballCtrl = FindBall(ball);
			if (ballCtrl == null)
			{
				ballCtrl = new BallCtrl(ball, Preferences);
				ballCtrl.Bounds = GetCellRect(ball.Position);
				balls.Add(ballCtrl);
			}
			InvalidateCell(ball.Position);
			//ballCtrl.Invalidate();
		}

		/// <summary>
		/// Removes a ball from the board control.
		/// <seealso cref="RemoveBalls(Ball[])"/>
		/// <seealso cref="RemoveAllBalls()"/>
		/// </summary>
		/// <param name="ball">The ball to be removed.</param>
		/// <remarks>
		/// Removes an instance of <see cref="BallCtrl"/> that corresponds to the ball that should be 
		/// removed, and redraws the region on the screen where this ball was placed.
		/// </remarks>
		public void RemoveBall(Ball ball)
		{
			BallCtrl ballCtrl = FindBall(ball);
			if (ballCtrl != null)
			{
				balls.Remove(ballCtrl);
			}
			InvalidateCell(ball.Position);
		}
		
		/// <summary>
		/// Removes a collection of balls from the board control.
		/// <seealso cref="RemoveBall(Ball)"/>
		/// <seealso cref="RemoveAllBalls()"/>
		/// </summary>
		/// <param name="balls">The collection of balls to be removed.</param>
		/// <remarks>
		/// The regions where the balls were placed are invalidated accordingly.
		/// </remarks>
		public void RemoveBalls(Ball[] balls)
		{
			foreach (Ball ball in balls)
			{
				RemoveBall(ball);
			}
		}

		/// <summary>
		/// Removes all balls from the board.
		/// <seealso cref="RemoveBall(Ball)"/>
		/// <seealso cref="RemoveBalls(Ball[])"/>
		/// </summary>
		/// <remarks>
		/// This method doesn't invalidate screen. You should call <see cref="Control.Invalidate()"/> 
		/// method yourself if it is necessary.
		/// </remarks>
		public void RemoveAllBalls()
		{
			balls.Clear();
		}

		/// <summary>
		/// Calculates a cell size to fit playing field in a best way on the screen.
		/// </summary>
		/// <returns>The size of a cell (width and height have equal size).</returns>
		private float GetCellSize()
		{
			Size gameSize = Game.Board.BoardSize;

			float sizeX = (float)(this.Width - 1) / gameSize.Width;
			float sizeY = (float)(this.Height - 1) / gameSize.Height;

			return (sizeX > sizeY) ? sizeY : sizeX;
		}

		/// <summary>
		/// Returns the rectangle of playing field, location and size of field on the screen.
		/// </summary>
		/// <returns>The rectangle of playing field (not the board control)</returns>
		private Rectangle GetFieldRect()
		{
			Size gameSize = Game.Board.BoardSize;
			float size = GetCellSize();
			Rectangle fieldRect = new Rectangle(0, 0, (int)(size * gameSize.Width), (int)(size * gameSize.Height));
			fieldRect.Offset((this.Width - 1 - fieldRect.Width) / 2, (this.Height - 1 - fieldRect.Height) / 2);
			return fieldRect;
		}

		/// <summary>
		/// Returns the rectangle of board cell, location and size of cell on the board.
		/// </summary>
		/// <param name="cell">Defines the cell (its coordinates) to calculate its rectangle.</param>
		/// <returns>The rectangle of cell on the board control.</returns>
		public Rectangle GetCellRect(Point cell)
		{
			Rectangle fieldRect = GetFieldRect();
			float size = GetCellSize();
			Rectangle cellRect = new Rectangle(
					(int)(cell.X * size + fieldRect.Left + 1),
					(int)(cell.Y * size + fieldRect.Top + 1),
					(int)size - 1,
					(int)size - 1
				);
			return cellRect;
		}

		/// <summary>
		/// Invalidates a region of playing field that corresponds to a certain cell on the board.
		/// </summary>
		/// <param name="cell">The cell to invalidate.</param>
		public void InvalidateCell(Point cell)
		{
			Invalidate(GetCellRect(cell));
		}

		/// <summary>
		/// Handles the <b>MouseDown</b> event.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
		/// <remarks>
		/// Depending on where the mouse was clicked either fires <see cref="BallClick"/> event, or 
		/// fires <see cref="CellClick"/> event or does nothing.
		/// </remarks>
		protected void OnMouseDown(object sender, MouseEventArgs e)
		{
			Point cell = GetCellFromLocation(e.X, e.Y);
			if ((cell.X >= 0) && (cell.Y >= 0))
			{
				BallCtrl ball = FindBall(cell);
				if (ball != null)
				{
					FireBallClick(ball);
				}
				else
				{
					FireCellClick(cell);
				}
			}
		}

		/// <summary>
		/// Matchs a point on the screen to the cell coordinates of game board.
		/// </summary>
		/// <param name="x">The x-coordinate of a point on the screen.</param>
		/// <param name="y">The y-coordinate of a point on the screen.</param>
		/// <returns>The coordinates of cell on the board, or <c>[-1, -1]</c> coordinates in case the point 
		/// on the screen doesn't fall to a region of any board cell.</returns>
		protected Point GetCellFromLocation(int x, int y)
		{
			Size gameSize = Game.Board.BoardSize;
			float size = GetCellSize();

			Rectangle field = new Rectangle(0, 0, (int)(size * gameSize.Width), (int)(size * gameSize.Height));
			field.Offset((this.Width - 1 - field.Width) / 2, (this.Height - 1 - field.Height) / 2);

			if (field.Contains(x, y))
			{
				return new Point(
					(int)((x - field.Left) / size),
					(int)((y - field.Top) / size));
			}

			return new Point(-1, -1);
		}

		/// <summary>
		/// Takes care of drawing a next phase of jumping ball.
		/// </summary>
		public void DrawJumpingBall()
		{
			// Find and repaint Jumping ball
			Ball ball = Game.Board.GetSelectedBall();
			if (ball != null)
			{
				BallCtrl ballCtrl = FindBall(ball);
				if (ballCtrl != null)
				{
					ballCtrl.NexJumpPosition();
					InvalidateCell(ball.Position);
				}
			}
		}

		/// <summary>
		/// Gets the ball control that corresponds to a board position.
		/// </summary>
		/// <param name="pos">The ball position on the board.</param>
		/// <returns>A reference to <see cref="BallCtrl"/> object that corresponds to the position 
		/// if such one exists, <c>null</c> in case such a control wasn't found.</returns>
		public BallCtrl FindBall(Point pos)
		{
			for (int i = 0; i < balls.Count; i++)
			{
				BallCtrl ball = (BallCtrl)balls[i];
				if (ball.Ball.Position == pos)
				{
					return ball;
				}
			}

			return null;
		}

		/// <summary>
		/// Gets the ball contrall that refers to a <see cref="Ball"/> object.
		/// </summary>
		/// <param name="ball">The ball the corresponding ball contral should be found.</param>
		/// <returns>A reference to <see cref="BallCtrl"/> object that corresponds to the <see cref="Ball"/>
		/// object if such control exists, <c>null</c> in case such one wasn't found.</returns>
		public BallCtrl FindBall(Ball ball)
		{
			return FindBall(ball.Position);
		}

		/// <summary>
		/// Moves ball from one position to another.
		/// </summary>
		/// <param name="ball">Defines a ball the corresponding to it ball control should be moved.</param>
		/// <param name="oldPos">The old position of a ball on the board.</param>
		/// <param name="newPos">The new position of a ball on the board.</param>
		public void MoveBall(Ball ball, Point oldPos, Point newPos)
		{
			// Move
			BallCtrl ballCtrl = FindBall(ball);
			ballCtrl.Bounds = GetCellRect(newPos);
			
			// Redraw
			InvalidateCell(oldPos);
			InvalidateCell(newPos);
		}

		/// <summary>
		/// Fires <see cref="CellClick"/> event with a reference to this board control.
		/// </summary>
		/// <param name="cell">The board cell that was clicked.</param>
		public void FireCellClick(Point cell)
		{
			CellClick(this, cell);
		}

		/// <summary>
		/// Fires <see cref="BallClick"/> event with a reference to this board control.
		/// </summary>
		/// <param name="ball">The ball on the board that was clicked.</param>
		public void FireBallClick(BallCtrl ball)
		{
			BallClick(this, ball);
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -