using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using Bornander.Games.Direct3D;
namespace Bornander.Games.BoardGame.Direct3D
{
/// <summary>
/// This class is a panel that handles moving a piece around with the mouse and basic DirectX setup.
/// </summary>
public class GamePanel : Panel
{
#region Private members
private IBoardGameLogic gameLogic;
private IBoardGameModelRepository boardGameModelRepository;
private Device device = null;
private Camera camera;
private Model selectedPieceModel;
private Vector3 selectedPiecePosition;
private VisualBoard board;
private Move ponderedMove = null;
private List<Move> availableMoves;
private Point previousPoint = Point.Empty;
private float cameraAngle = -((float)Math.PI / 2.0f);
private float cameraElevation = 7.0f;
private float cameraDistanceFactor = 1.5f;
#endregion
public GamePanel()
{
InitializeComponent();
}
/// <summary>
/// Helper method for setting present parameters for the DirectX initialization.
/// </summary>
private static PresentParameters CreatePresentParameters()
{
PresentParameters presentParameters = new PresentParameters();
presentParameters.Windowed = true;
presentParameters.SwapEffect = SwapEffect.Discard;
presentParameters.EnableAutoDepthStencil = true;
presentParameters.AutoDepthStencilFormat = DepthFormat.D16;
return presentParameters;
}
/// <summary>
/// Sets up a single light source and some ambient light. Helper method.
/// </summary>
///
private static void SetupDefaultLight(Device device)
{
device.RenderState.Ambient = Color.FromArgb(0x606060);
device.RenderState.Lighting = true;
device.Lights[0].Enabled = false;
device.Lights[0].Update();
device.Lights[0].Type = LightType.Directional;
device.Lights[0].Diffuse = Color.FromArgb(128, 128, 128);
device.Lights[0].Direction = new Vector3(0.5f, -0.33f, 0.66f);
device.Lights[0].Update();
device.Lights[0].Enabled = true;
}
/// <summary>
/// This methods iniitializes DirectX and the game.
/// </summary>
public void Initialize(IBoardGameLogic gameLogic, IBoardGameModelRepository boardGameModelRepository)
{
try
{
PresentParameters presentParameters = GamePanel.CreatePresentParameters();
device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParameters);
device.RenderState.ZBufferEnable = true;
device.RenderState.Lighting = true;
device.RenderState.ShadeMode = ShadeMode.Flat;
GamePanel.SetupDefaultLight(device);
// Has to do this or the directional light will go out if we resize the window
device.DeviceReset += new EventHandler(OnDeviceReset);
this.gameLogic = gameLogic;
this.boardGameModelRepository = boardGameModelRepository;
this.boardGameModelRepository.Initialize(device);
// Create a camera and position in front of the board looking at the board center.
// (Our board will occupie the area from 0,0,0 to 7,0,7)
camera = new Camera(new Vector3(gameLogic.Columns / 2.0f, 5.0f, -gameLogic.Rows), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(gameLogic.Columns / 2 - 0.5f, 0.0f, gameLogic.Rows / 2 - 0.5f));
// We must wait until the Direct3D device is created since the VisualBoard needs that to create models.
board = new VisualBoard(gameLogic, boardGameModelRepository);
availableMoves = gameLogic.GetAvailableMoves();
SetCameraPosition();
}
catch (DirectXException exception)
{
MessageBox.Show(exception.Message, "Oops!");
}
}
public void InitializeNewGame()
{
gameLogic.Initialize();
availableMoves = gameLogic.GetAvailableMoves();
Render();
}
private void OnDeviceReset(object sender, EventArgs e)
{
device.RenderState.ZBufferEnable = true;
device.RenderState.Lighting = true;
device.RenderState.ShadeMode = ShadeMode.Flat;
GamePanel.SetupDefaultLight(device);
Render();
}
/// <summary>
/// Renders the screen, called whenever we need to redraw the screen
/// </summary>
public void Render()
{
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.SystemColors.Control, 1.0f, 0);
device.BeginScene();
device.Transform.View = camera.ViewMatrix;
device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)Width / (float)Height, 1.0f, 100.0f);
// Render the board
board.Render(device);
// If a piece is selected, render that as well.
if (selectedPieceModel != null)
{
selectedPieceModel.Position = selectedPiecePosition;
selectedPieceModel.Render(device);
}
device.EndScene();
device.Present();
}
protected override void OnPaint(PaintEventArgs e)
{
if (device != null)
Render();
else
base.OnPaint(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (device == null)
base.OnPaintBackground(e);
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// GamePanel
//
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.GamePanel_MouseMove);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.GamePanel_MouseDown);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.GamePanel_MouseUp);
this.ResumeLayout(false);
}
private void CheckForGameOver()
{
// First check for game over, in case of game over display a message and exit the application.
string gameOverStatement;
if (gameLogic.IsGameOver(out gameOverStatement))
{
DialogResult result = MessageBox.Show(gameOverStatement + " Play another game?", "Game Over!", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
gameLogic.Initialize();
}
else
{
Application.Exit();
}
}
}
private void SetCameraPosition()
{
// Calculate a camera position, this is a radius from the center of the board and then cameraElevation up.
float cameraX = gameLogic.Columns / 2.0f + (cameraDistanceFactor * gameLogic.Columns * (float)Math.Cos(cameraAngle));
float cameraZ = gameLogic.Rows / 2.0f + (cameraDistanceFactor * gameLogic.Rows * (float)Math.Sin(cameraAngle));
camera.Position = new Vector3(
cameraX,
cameraElevation,
cameraZ);
}
public void HandleMouseWheel(object sender, MouseEventArgs e)
{
// If the user scroll the mouse wheel we zoom out or in
cameraDistanceFactor = Math.Max(0.0f, cameraDistanceFactor + Math.Sign(e.Delta) / 5.0f);
SetCameraPosition();
Render();
}
private void GamePanel_MouseMove(object sender, MouseEventArgs e)
{
// Dragging using the right mousebutton moves the camera along the X and Y axis.
if (e.Button == MouseButtons.Right)
{
cameraAngle += (e.X - previousPoint.X) / 100.0f;
cameraElevation = Math.Max(0, cameraElevation + (e.Y - previousPoint.Y) / 10.0f);
SetCameraPosition();
previousPoint = e.Location;
}
Square square;
if (e.Button == MouseButtons.Left)
{
if (ponderedMove != null)
{
if (board.GetMouseOverBlockModel(device, e.X, e.Y, out square, ponderedMove.Destinations))
{
// Set the dragged pieces location to the current square
selectedPiecePosition.X = square.Column;
selectedPiecePosition.Z = square.Row;
}
}
}
else
{
board.GetMouseOverBlockModel(device, e.X, e.Y, out square, GamePanel.GetSquaresFromMoves(availableMoves));
}
// Render since we might have moved the camera
Render();
}
private void GamePanel_MouseDown(object sender, MouseEventArgs e)
{
// The previous point has to be set here or the distance dragged can be too big.
previousPoint = e.Location;
// If the mouse is over a block (see GetMouseOverBlockModel) for details on how detemining that
// and the left button is down, try to grab the piece (if there is one at the square and it has valid moves).
if (e.Button == MouseButtons.Left)
{
ponderedMove = null;
Square square;
if (board.GetMouseOverBlockModel(device, e.X, e.Y, out square, null))
{
foreach (Move move in availableMoves)
{
// We have a move and it is started from the square we're over, start dragging a piece
if (square.Equals(move.Origin))
{
selectedPieceModel = board.PickUpPiece(square);
selectedPiecePosition = new Vector3(square.Column, 1.0f, square.Row);
ponderedMove = move;
break;
}
}
}
}
Render();
}
private void GamePanel_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Square square;
if (board.GetMouseOverBlockModel(device, e.X, e.Y, out square, null))
{
// ponderedMove keeps track of the current potential move that will take place
// if we drop the piece onto a valid square, if ponderedMove is not null that means
// we're currently dragging a piece.
if (ponderedMove != null)
{
foreach (Square allowedSquare in ponderedMove.Destinations)
{
// Was it drop on a square that's a legal move?
if (square.Equals(allowedSquare))
{
// Move the piece to the target square
availableMoves = gameLogic.Move(ponderedMove.Origin, allowedSquare);
break;
}
}
}
}
board.DropPiece();
selectedPieceModel = null;
Render();
CheckForGameOver();
}
}
private static List<Square> GetSquaresFromMoves(List<Move> moves)
{
List<Square> squares = new List<Square>();
foreach(Move move in moves)
{
squares.Add(move.Origin);
}
return squares;
}
}
}