private void picMainBoard_Click(object sender, System.EventArgs e) { switch (m_Phase) { case GamePhase.PlacingShips: if (!Client.Board.CanPlaceShip(m_Placing)) { break; } //Client.Board.SetShip(m_PlacingType, m_Placing); picMainBoard.Enabled = false; lblGameStatus.Text = "Hold on..."; Client.SendPlaceShip(m_Placing); break; case GamePhase.OurTurn: if (!m_MousePosition.IsWithinBoard()) { break; } picMainBoard.Enabled = false; lblGameStatus.Text = "Hold on..."; Client.SendShootRequest(m_MousePosition); break; } }
private static void OnPlayerShoot(Packet recv) { // trivial safety measure if (!gameStarted) { return; } // only the current player is allowed to place shots var player = GetPlayerForPeerID(recv.GetSender()); if (player == null || player.IsPlayerOne != whoseTurn) { return; } // to prevent a player from firing multiple shots per turn if (shotLaunchedThisTurn) { return; } shotLaunchedThisTurn = true; var opponent = GetOtherPlayer(player); // whoseTurn ? player2 : player1; Debug.Assert(player != opponent); using (var instream = recv.GetStream()) { var acceptable = true; var pos = new BoardPosition(instream.ReadByte(), instream.ReadByte()); ShotResult result = 0; // Reply to player who placed the shot using (var ms = new MemoryStream()) { using (var outstream = new BinaryWriter(ms, Encoding.UTF8)) { outstream.Write((byte)pos.x); outstream.Write((byte)pos.y); if (!pos.IsWithinBoard() || opponent.Board.Shots.ContainsKey(pos)) { // this shot is invalid, it's either outside the board, or already been shot at before shotLaunchedThisTurn = false; // permit retry acceptable = false; outstream.Write(false); } else { // shot position is acceptable; actually place it and determine result result = opponent.Board.DoIncomingShotAt(pos, out var hitWhat); opponent.Board.SetShotResultAt(pos, result); outstream.Write(true); outstream.Write((byte)result); // if shot hit a ship, also indicate whether the ship was sunk if (result == ShotResult.Hit) { var ship = opponent.Board.Ships[hitWhat]; var sunk = ship.IsSunk(); outstream.Write(sunk); // and only if actually sunk, do we tell the client what ship it was (minor security measure) if (sunk) { outstream.Write((byte)hitWhat); outstream.Write((byte)ship.Position.x); outstream.Write((byte)ship.Position.y); outstream.Write((byte)ship.Orientation); } } } using (var packet = new Packet((PacketCommand)GameCommand.ShootResult, ms.ToArray())) peer.Send(packet, player.NetworkID, PacketOptions.RELIABLE); } } // Inform other player of the shot if (!acceptable) { return; } using (var ms = new MemoryStream()) { using (var outstream = new BinaryWriter(ms, Encoding.UTF8)) { outstream.Write((byte)pos.x); outstream.Write((byte)pos.y); outstream.Write((byte)result); using (var packet = new Packet((PacketCommand)GameCommand.ShootIncoming, ms.ToArray())) peer.Send(packet, opponent.NetworkID, PacketOptions.RELIABLE); } } } // this is a very cheapskate way of pausing the game. I want both players to have some time to view the shot's // results, before the game switches to the next turn. And handling it clientside (in winforms) is too painful. Thread.Sleep(2000); // check if the opponent is now screwed if (opponent.Board.GetAllShipsSunk()) { // game over using (var packet = new Packet((PacketCommand)GameCommand.GameWin, EmptyPayload)) peer.Send(packet, player.NetworkID, PacketOptions.RELIABLE); using (var packet = new Packet((PacketCommand)GameCommand.GameLose, EmptyPayload)) peer.Send(packet, opponent.NetworkID, PacketOptions.RELIABLE); gameStarted = false; } else { BeginNextTurn(); } }