Jelajahi Sumber

Fix Dealer sync with human players.

Dominique Louis 1 bulan lalu
induk
melakukan
278eeb6607

+ 25 - 0
CardsStarterKit/Core/Game/Blackjack.Core.Android.csproj

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net9.0</TargetFramework>
+    <RootNamespace>Blackjack.Core</RootNamespace>
+    <AssemblyTitle>Blackjack</AssemblyTitle>
+    <AssemblyProduct>Blackjack</AssemblyProduct>
+    <AssemblyDescription></AssemblyDescription>
+    <AssemblyCompany>Microsoft</AssemblyCompany>
+    <AssemblyCopyright>Copyright © Microsoft 2010</AssemblyCopyright>
+    <AssemblyVersion>1.0.0.0</AssemblyVersion>
+    <Authors>CartBlanche</Authors>
+    <Company>MonoGame Foundation</Company>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*">
+      <PrivateAssets>All</PrivateAssets>
+    </PackageReference>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Framework\Cards.Framework.csproj" />
+    <ProjectReference Include="..\..\..\MonoGame.Xna.Framework.Net\MonoGame.Xna.Framework.Net.csproj" />
+  </ItemGroup>
+</Project>

+ 237 - 1
CardsStarterKit/Core/Game/Blackjack/Game/BlackjackCardGame.cs

@@ -58,6 +58,7 @@ namespace Blackjack
 
         BetGameComponent betGameComponent;
         AnimatedHandGameComponent dealerHandComponent;
+        public int LocalPlayerIndex { get; set; } = -1;
         DeckDisplayComponent deckDisplay;
         Dictionary<string, Button> buttons = new Dictionary<string, Button>();
         Button newGame;
@@ -1797,6 +1798,240 @@ namespace Blackjack
             showInsurance = false;
         }
 
+        /// <summary>
+        /// Performs a "Hit" action for a specific player (used by host when receiving action from client).
+        /// </summary>
+        public void HitForPlayer(byte playerIndex)
+        {
+            if (playerIndex >= players.Count)
+                return;
+
+            BlackjackPlayer player = (BlackjackPlayer)players[playerIndex];
+
+            // Draw a card to the appropriate hand
+            TraditionalCard card;
+            switch (player.CurrentHandType)
+            {
+                case HandTypes.First:
+                    card = dealer.DealCardToHand(player.Hand);
+                    AddDealAnimation(card, animatedHands[playerIndex], true,
+                        DealDuration, DateTime.Now);
+
+                    // Broadcast card dealt in network games
+                    if (IsNetworkGame && IsHost)
+                    {
+                        BroadcastCardDealt(card, playerIndex, false, HandTypes.First);
+                    }
+                    break;
+                case HandTypes.Second:
+                    card = dealer.DealCardToHand(player.SecondHand);
+                    AddDealAnimation(card, animatedSecondHands[playerIndex], true,
+                        DealDuration, DateTime.Now);
+
+                    // Broadcast card dealt in network games
+                    if (IsNetworkGame && IsHost)
+                    {
+                        BroadcastCardDealt(card, playerIndex, false, HandTypes.Second);
+                    }
+                    break;
+                default:
+                    throw new Exception("Player has an unsupported hand type.");
+            }
+
+            // Auto-stand on 21 if enabled in settings
+            player.CalculateValues();
+            int handValue = player.CurrentHandType == HandTypes.First ? player.FirstValue : player.SecondValue;
+            if (GameSettings.Instance.AutoStandOn21 && handValue == 21)
+            {
+                StandForPlayer(playerIndex);
+            }
+        }
+
+        /// <summary>
+        /// Performs a "Stand" action for a specific player (used by host when receiving action from client).
+        /// </summary>
+        public void StandForPlayer(byte playerIndex)
+        {
+            if (playerIndex >= players.Count)
+                return;
+
+            BlackjackPlayer player = (BlackjackPlayer)players[playerIndex];
+
+            // Execute Stand logic
+            if (player.IsSplit == false)
+            {
+                turnFinishedByPlayer[playerIndex] = true;
+            }
+            else
+            {
+                switch (player.CurrentHandType)
+                {
+                    case HandTypes.First:
+                        if (player.SecondBlackJack)
+                        {
+                            turnFinishedByPlayer[playerIndex] = true;
+                        }
+                        else
+                        {
+                            player.CurrentHandType = HandTypes.Second;
+                        }
+                        break;
+                    case HandTypes.Second:
+                        turnFinishedByPlayer[playerIndex] = true;
+                        break;
+                }
+            }
+
+            // Broadcast to network in network games
+            if (IsNetworkGame && IsHost)
+            {
+                BroadcastStandAction(playerIndex);
+            }
+        }
+
+        /// <summary>
+        /// Performs a "Double" action for a specific player (used by host when receiving action from client).
+        /// </summary>
+        public void DoubleForPlayer(byte playerIndex)
+        {
+            if (playerIndex >= players.Count)
+                return;
+
+            BlackjackPlayer player = (BlackjackPlayer)players[playerIndex];
+
+            // Broadcast to network in network games
+            if (IsNetworkGame && IsHost)
+            {
+                BroadcastDoubleAction((byte)playerIndex);
+            }
+
+            switch (player.CurrentHandType)
+            {
+                case HandTypes.First:
+                    player.Double = true;
+                    float betAmount = player.BetAmount;
+
+                    if (player.IsSplit)
+                    {
+                        betAmount /= 2f;
+                    }
+
+                    betGameComponent.AddChips(playerIndex, betAmount, false, false);
+                    break;
+                case HandTypes.Second:
+                    player.SecondDouble = true;
+                    if (player.Double == false)
+                    {
+                        // The bet is evenly spread between both hands, add one half
+                        betGameComponent.AddChips(playerIndex, player.BetAmount / 2f,
+                            false, true);
+                    }
+                    else
+                    {
+                        // The first hand's bet is double, add one third of the total
+                        betGameComponent.AddChips(playerIndex, player.BetAmount / 3f,
+                            false, true);
+                    }
+                    break;
+                default:
+                    throw new Exception("Player has an unsupported hand type.");
+            }
+
+            // Hit then Stand
+            HitForPlayer(playerIndex);
+            StandForPlayer(playerIndex);
+        }
+
+        /// <summary>
+        /// Performs a "Split" action for a specific player (used by host when receiving action from client).
+        /// </summary>
+        public void SplitForPlayer(byte playerIndex)
+        {
+            if (playerIndex >= players.Count)
+                return;
+
+            BlackjackPlayer player = (BlackjackPlayer)players[playerIndex];
+
+            // Broadcast to network in network games
+            if (IsNetworkGame && IsHost)
+            {
+                BroadcastSplitAction((byte)playerIndex);
+            }
+
+            player.InitializeSecondHand();
+
+            Vector2 sourcePosition = animatedHands[playerIndex].GetCardGameComponent(1).CurrentPosition;
+            Vector2 targetPosition = animatedHands[playerIndex].GetCardGameComponent(0).CurrentPosition +
+                secondHandOffset;
+            // Create an animation moving the top card to the second hand location
+            AnimatedGameComponentAnimation animation = new TransitionGameComponentAnimation(sourcePosition,
+                    targetPosition)
+            {
+                StartTime = DateTime.Now,
+                Duration = TimeSpan.FromSeconds(0.5f)
+            };
+
+            // Actually perform the split
+            player.SplitHand();
+
+            // Add additional chip stack for the second hand
+            betGameComponent.AddChips(playerIndex, player.BetAmount,
+                false, true);
+
+            // Initialize visual representation of the second hand
+            animatedSecondHands[playerIndex] =
+                new BlackjackAnimatedPlayerHandComponent(playerIndex, secondHandOffset,
+                    player.SecondHand, this, screenManager.SpriteBatch, screenManager.GlobalTransformation);
+            Game.Components.Add(animatedSecondHands[playerIndex]);
+
+            AnimatedCardsGameComponent animatedGameComponent = animatedSecondHands[playerIndex].GetCardGameComponent(0);
+            animatedGameComponent.IsFaceDown = false;
+            animatedGameComponent.AddAnimation(animation);
+
+            // Deal an additional card to each of the new hands
+            TraditionalCard card = dealer.DealCardToHand(player.Hand);
+            AddDealAnimation(card, animatedHands[playerIndex], true, DealDuration,
+                DateTime.Now + animation.EstimatedTimeForAnimationCompletion);
+
+            // Broadcast first card dealt in network games
+            if (IsNetworkGame && IsHost)
+            {
+                BroadcastCardDealt(card, (byte)playerIndex, false, HandTypes.First);
+            }
+
+            card = dealer.DealCardToHand(player.SecondHand);
+            AddDealAnimation(card, animatedSecondHands[playerIndex], true, DealDuration,
+                DateTime.Now + animation.EstimatedTimeForAnimationCompletion +
+                DealDuration);
+
+            // Broadcast second card dealt in network games
+            if (IsNetworkGame && IsHost)
+            {
+                BroadcastCardDealt(card, (byte)playerIndex, false, HandTypes.Second);
+            }
+        }
+
+        /// <summary>
+        /// Performs an "Insurance" action for a specific player (used by host when receiving action from client).
+        /// </summary>
+        public void InsuranceForPlayer(byte playerIndex)
+        {
+            if (playerIndex >= players.Count)
+                return;
+
+            BlackjackPlayer player = (BlackjackPlayer)players[playerIndex];
+
+            player.IsInsurance = true;
+            player.Balance -= player.BetAmount / 2f;
+            betGameComponent.AddChips(playerIndex, player.BetAmount / 2, true, false);
+
+            // Broadcast to network in network games
+            if (IsNetworkGame && IsHost)
+            {
+                BroadcastInsuranceAction((byte)playerIndex);
+            }
+        }
+
         /// <summary>
         /// Changes the visiblility of most game buttons.
         /// </summary>
@@ -2373,7 +2608,8 @@ namespace Blackjack
             if (NetworkSession == null || NetworkSession.LocalGamers.Count == 0)
                 return;
 
-            var packet = new Networking.PlayerActionPacket { Action = action };
+            byte playerIndex = (byte)LocalPlayerIndex;
+            var packet = new Networking.PlayerActionPacket { Action = action, PlayerIndex = playerIndex };
             var writer = new Microsoft.Xna.Framework.Net.PacketWriter();
             writer.Write((byte)Networking.PacketType.PlayerAction);
             packet.Serialize(writer);

+ 16 - 13
CardsStarterKit/Core/Game/Blackjack/Networking/Packets.cs

@@ -9,11 +9,11 @@ namespace Blackjack.Networking
         public string Name { get; set; }
         public bool IsAI { get; set; }
     }
-    
+
     public class PlayerListSyncPacket
     {
         public List<PlayerInfo> Players { get; set; }
-        
+
         public void Serialize(PacketWriter writer)
         {
             writer.Write((byte)Players.Count);
@@ -23,7 +23,7 @@ namespace Blackjack.Networking
                 writer.Write(player.IsAI);
             }
         }
-        
+
         public static PlayerListSyncPacket Deserialize(PacketReader reader)
         {
             var packet = new PlayerListSyncPacket { Players = new List<PlayerInfo>() };
@@ -43,15 +43,18 @@ namespace Blackjack.Networking
     public class PlayerActionPacket
     {
         public BlackjackAction Action { get; set; }
+        public byte PlayerIndex { get; set; }
         public void Serialize(PacketWriter writer)
         {
             writer.Write((byte)Action);
+            writer.Write(PlayerIndex);
         }
         public static PlayerActionPacket Deserialize(PacketReader reader)
         {
             return new PlayerActionPacket
             {
-                Action = (BlackjackAction)reader.ReadByte()
+                Action = (BlackjackAction)reader.ReadByte(),
+                PlayerIndex = reader.ReadByte()
             };
         }
     }
@@ -60,13 +63,13 @@ namespace Blackjack.Networking
     {
         public byte PlayerIndex { get; set; }
         public int BetAmount { get; set; }
-        
+
         public void Serialize(PacketWriter writer)
         {
             writer.Write(PlayerIndex);
             writer.Write(BetAmount);
         }
-        
+
         public static BetPlacedPacket Deserialize(PacketReader reader)
         {
             return new BetPlacedPacket
@@ -81,13 +84,13 @@ namespace Blackjack.Networking
     {
         public byte PlayerIndex { get; set; }
         public int ChipValue { get; set; }
-        
+
         public void Serialize(PacketWriter writer)
         {
             writer.Write(PlayerIndex);
             writer.Write(ChipValue);
         }
-        
+
         public static ChipAddedPacket Deserialize(PacketReader reader)
         {
             return new ChipAddedPacket
@@ -117,16 +120,16 @@ namespace Blackjack.Networking
             {
                 var playerIndex = reader.ReadByte();
                 System.Console.WriteLine($"[CardDealtPacket] PlayerIndex: {playerIndex}");
-                
+
                 var card = reader.ReadCard();
                 System.Console.WriteLine($"[CardDealtPacket] Card: {card.Type} {card.Value}");
-                
+
                 var faceDown = reader.ReadBoolean();
                 System.Console.WriteLine($"[CardDealtPacket] FaceDown: {faceDown}");
-                
+
                 var handType = (HandTypes)reader.ReadByte();
                 System.Console.WriteLine($"[CardDealtPacket] HandType: {handType}");
-                
+
                 return new CardDealtPacket
                 {
                     PlayerIndex = playerIndex,
@@ -274,4 +277,4 @@ namespace Blackjack.Networking
             };
         }
     }
-}
+}

+ 8 - 6
CardsStarterKit/Core/Game/Screens/GameplayScreen.cs

@@ -347,22 +347,23 @@ namespace Blackjack
             // Host receives action from client and processes it
             if (networkSession != null && networkSession.IsHost)
             {
+                // Execute the action for the specific player
                 switch (packet.Action)
                 {
                     case Blackjack.Networking.BlackjackAction.Hit:
-                        blackJackGame.Hit();
+                        blackJackGame.HitForPlayer(packet.PlayerIndex);
                         break;
                     case Blackjack.Networking.BlackjackAction.Stand:
-                        blackJackGame.Stand();
+                        blackJackGame.StandForPlayer(packet.PlayerIndex);
                         break;
                     case Blackjack.Networking.BlackjackAction.Double:
-                        blackJackGame.Double();
+                        blackJackGame.DoubleForPlayer(packet.PlayerIndex);
                         break;
                     case Blackjack.Networking.BlackjackAction.Split:
-                        blackJackGame.Split();
+                        blackJackGame.SplitForPlayer(packet.PlayerIndex);
                         break;
                     case Blackjack.Networking.BlackjackAction.Insurance:
-                        blackJackGame.Insurance();
+                        blackJackGame.InsuranceForPlayer(packet.PlayerIndex);
                         break;
                 }
             }
@@ -535,12 +536,13 @@ namespace Blackjack
                 {
                     if (blackJackGame.Players[i].Name.Equals(localGamerTag, StringComparison.OrdinalIgnoreCase))
                     {
-                        // Found the local player - tell BetGameComponent
+                        // Found the local player - tell BetGameComponent and BlackjackCardGame
                         var betComponent = blackJackGame.Game.Components.OfType<BetGameComponent>().FirstOrDefault();
                         if (betComponent != null)
                         {
                             betComponent.LocalPlayerIndex = i;
                         }
+                        blackJackGame.LocalPlayerIndex = i;
                         break;
                     }
                 }

+ 1 - 1
CardsStarterKit/Platforms/Android/BlackJack.Android.csproj

@@ -18,7 +18,7 @@
   </PropertyGroup>
   
   <ItemGroup>
-    <ProjectReference Include="..\..\Core\Game\BlackJack.Core.csproj" />
+    <ProjectReference Include="..\..\Core\Game\BlackJack.Core.Android.csproj" />
     <PackageReference Include="MonoGame.Framework.Android" Version="3.8.*" />
     <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
   </ItemGroup>