Пример #1
0
        private void HandleLocalPlayerOperation()
        {
            // update local data
            CurrentRoundStatus.SetCurrentPlaceIndex(OperationPlayerIndex);
            int placeIndex = CurrentRoundStatus.CurrentPlaceIndex;

            Assert.IsTrue(placeIndex == 0);
            SetRoundStatusData();
            // update ui elements
            controller.TableTilesManager.SetMelds(placeIndex, HandData.OpenMelds);
            // if not kong, start timer
            if (Operation.Type != OutTurnOperationType.Kong)
            {
                controller.TurnTimeController.StartCountDown(CurrentRoundStatus.GameSetting.BaseTurnTime, BonusTurnTime, () =>
                {
                    Debug.Log("Time out, automatically discard rightmost tile");
                    CurrentRoundStatus.SetRichiing(false);
                    var tile = HandData.HandTiles[HandData.HandTiles.Length - 1];
                    ClientBehaviour.Instance.OnDiscardTile(tile, false, 0);
                });
            }
        }
Пример #2
0
        private void HandleFourRichis()
        {
            // Get waiting tiles for each player
            var waitingDataArray = new WaitingData[players.Count];

            for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
            {
                var hand = CurrentRoundStatus.HandTiles(playerIndex);
                var open = CurrentRoundStatus.Melds(playerIndex);
                waitingDataArray[playerIndex] = new WaitingData
                {
                    HandTiles    = hand,
                    WaitingTiles = MahjongLogic.WinningTiles(hand, open).ToArray()
                };
            }

            // Get messages
            for (int i = 0; i < players.Count; i++)
            {
                infos[i] = new EventMessages.RoundDrawInfo
                {
                    RoundDrawType = RoundDrawType,
                    WaitingData   = waitingDataArray
                };
            }

            next  = waitingDataArray[CurrentRoundStatus.OyaPlayerIndex].WaitingTiles.Length > 0;
            extra = true;
            // Get point transfers
            for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
            {
                var waitingTiles = waitingDataArray[playerIndex].WaitingTiles;
                if (waitingTiles.Length > 0)
                {
                    continue;
                }
                GetTransfersForFalseRichi(playerIndex, transfers);
            }
        }
Пример #3
0
        public override void OnServerStateEnter()
        {
            PhotonNetwork.AddCallbackTarget(this);
            if (CurrentRoundStatus.CurrentPlayerIndex != CurrentPlayerIndex)
            {
                Debug.LogError("[Server] currentPlayerIndex does not match, this should not happen");
                CurrentRoundStatus.CurrentPlayerIndex = CurrentPlayerIndex;
            }

            UpdateRoundStatus();
            Debug.Log($"[Server] CurrentRoundStatus: {CurrentRoundStatus}");
            responds   = new bool[players.Count];
            operations = new OutTurnOperation[players.Count];
            var rivers = CurrentRoundStatus.Rivers;

            // Get messages and send them to players
            for (int i = 0; i < players.Count; i++)
            {
                var info = new EventMessages.DiscardOperationInfo
                {
                    PlayerIndex            = i,
                    CurrentTurnPlayerIndex = CurrentPlayerIndex,
                    IsRichiing             = IsRichiing,
                    DiscardingLastDraw     = DiscardLastDraw,
                    Tile          = DiscardTile,
                    BonusTurnTime = CurrentRoundStatus.GetBonusTurnTime(i),
                    Zhenting      = CurrentRoundStatus.IsZhenting(i),
                    Operations    = GetOperations(i),
                    HandTiles     = CurrentRoundStatus.HandTiles(i),
                    Rivers        = rivers
                };
                var player = CurrentRoundStatus.GetPlayer(i);
                ClientBehaviour.Instance.photonView.RPC("RpcDiscardOperation", player, info);
            }

            firstSendTime = Time.time;
            serverTimeOut = gameSettings.BaseTurnTime + CurrentRoundStatus.MaxBonusTurnTime
                            + ServerConstants.ServerTimeBuffer;
        }
Пример #4
0
        private void NextState()
        {
            if (outTurnOperations.All(op => op.Type == OutTurnOperationType.Skip))
            {
                // no one claimed a rob kong
                var turnDoraAfterDiscard = Kong.Side != MeldSide.Self;
                CurrentRoundStatus.BreakOneShotsAndFirstTurn();
                ServerBehaviour.Instance.DrawTile(CurrentPlayerIndex, true, turnDoraAfterDiscard);
                return;
            }

            if (outTurnOperations.Any(op => op.Type == OutTurnOperationType.Rong))
            {
                var discardingTile = GetTileFromKong();
                ServerBehaviour.Instance.TurnEnd(CurrentPlayerIndex, discardingTile, false, outTurnOperations, true,
                                                 false);
                return;
            }

            Debug.LogError(
                $"[Server] Logically cannot reach here, operations are {string.Join("|", outTurnOperations)}");
        }
Пример #5
0
        private OutTurnOperation[] GetOperations(int playerIndex)
        {
            if (playerIndex == CurrentPlayerIndex)
            {
                return new OutTurnOperation[]
                       {
                           new OutTurnOperation {
                               Type = OutTurnOperationType.Skip
                           }
                       }
            }
            ;
            // other players' operations
            var operations = new List <OutTurnOperation>
            {
                new OutTurnOperation {
                    Type = OutTurnOperationType.Skip
                }
            };

            // test rong
            TestRong(playerIndex, DiscardTile, operations);
            if (!CurrentRoundStatus.RichiStatus(playerIndex))
            {
                // get side
                var side = GetSide(playerIndex, CurrentPlayerIndex, CurrentRoundStatus.TotalPlayers);

                var handTiles = CurrentRoundStatus.HandTiles(playerIndex);
                // test kong
                TestKongs(handTiles, DiscardTile, side, operations);
                // test pong
                TestPongs(handTiles, DiscardTile, side, operations);
                // test chow
                TestChows(handTiles, DiscardTile, side, operations);
            }

            return(operations.ToArray());
        }
Пример #6
0
        public override void OnServerStateEnter()
        {
            MahjongSet.Reset();
            // throwing dice
            var dice = Random.Range(CurrentRoundStatus.GameSettings.DiceMin,
                                    CurrentRoundStatus.GameSettings.DiceMax + 1);

            CurrentRoundStatus.NextRound(dice, NextRound, ExtraRound, KeepSticks);
            // draw initial tiles
            DrawInitial();
            Debug.Log("[Server] Initial tiles distribution done");
            CurrentRoundStatus.SortHandTiles();
            CurrentRoundStatus.SetBonusTurnTime(gameSettings.BonusTurnTime);
            responds = new bool[players.Count];
            var room = PhotonNetwork.CurrentRoom;

            for (int index = 0; index < players.Count; index++)
            {
                var tiles = CurrentRoundStatus.HandTiles(index);
                Debug.Log($"[Server] Hand tiles of player {index}: {string.Join("", tiles)}");
                var info = new EventMessages.RoundStartInfo
                {
                    PlayerIndex      = index,
                    Field            = CurrentRoundStatus.Field,
                    Dice             = CurrentRoundStatus.Dice,
                    Extra            = CurrentRoundStatus.Extra,
                    RichiSticks      = CurrentRoundStatus.RichiSticks,
                    OyaPlayerIndex   = CurrentRoundStatus.OyaPlayerIndex,
                    Points           = CurrentRoundStatus.Points.ToArray(),
                    InitialHandTiles = tiles,
                    MahjongSetData   = MahjongSet.Data
                };
                var player = CurrentRoundStatus.GetPlayer(index);
                ClientBehaviour.Instance.photonView.RPC("RpcRoundStart", player, info);
            }

            firstTime = Time.time;
        }
Пример #7
0
        private InTurnOperation[] GetOperations(int playerIndex)
        {
            var operations = new List <InTurnOperation> {
                new InTurnOperation {
                    Type = InTurnOperationType.Discard
                }
            };

            // test tsumo
            TestTsumo(playerIndex, justDraw, operations);
            var handTiles = CurrentRoundStatus.HandTiles(playerIndex);
            var openMelds = CurrentRoundStatus.Melds(playerIndex);

            // test round draw
            Test9Orphans(handTiles, operations);
            // test richi
            TestRichi(playerIndex, handTiles, openMelds, operations);
            // test kongs
            TestKongs(playerIndex, handTiles, operations);
            // test bei
            TestBei(playerIndex, handTiles, operations);
            return(operations.ToArray());
        }
Пример #8
0
        private void UpdateRoundStatus()
        {
            var lastDraw = (Tile)CurrentRoundStatus.LastDraw;

            CurrentRoundStatus.LastDraw = null;
            CurrentRoundStatus.AddTile(CurrentPlayerIndex, lastDraw);
            if (Kong.IsAdded) // add kong
            {
                CurrentRoundStatus.AddKong(CurrentPlayerIndex, Kong);
                CurrentRoundStatus.RemoveTile(CurrentPlayerIndex, Kong.Extra);
            }
            else // self kong
            {
                CurrentRoundStatus.AddMeld(CurrentPlayerIndex, Kong);
                CurrentRoundStatus.RemoveTile(CurrentPlayerIndex, Kong);
            }
            CurrentRoundStatus.SortHandTiles();
            // turn dora if this is a self kong
            if (Kong.Side == MeldSide.Self)
            {
                MahjongSet.TurnDora();
            }
        }
Пример #9
0
 private void DrawInitial()
 {
     for (int round = 0; round < MahjongConstants.InitialDrawRound; round++)
     {
         // Draw 4 tiles for each player
         for (int index = 0; index < players.Count; index++)
         {
             for (int i = 0; i < MahjongConstants.TilesEveryRound; i++)
             {
                 var tile = MahjongSet.DrawTile();
                 CurrentRoundStatus.AddTile(index, tile);
             }
         }
     }
     // Last round, 1 tile for each player
     for (int index = 0; index < players.Count; index++)
     {
         for (int i = 0; i < MahjongConstants.TilesLastRound; i++)
         {
             var tile = MahjongSet.DrawTile();
             CurrentRoundStatus.AddTile(index, tile);
         }
     }
 }
Пример #10
0
        private void HandleRoundDraw()
        {
            // Get waiting tiles for each player
            var waitingDataArray = new WaitingData[players.Count];

            for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
            {
                var hand = CurrentRoundStatus.HandTiles(playerIndex);
                var open = CurrentRoundStatus.Melds(playerIndex);
                waitingDataArray[playerIndex] = new WaitingData
                {
                    HandTiles    = hand,
                    WaitingTiles = MahjongLogic.WinningTiles(hand, open).ToArray()
                };
            }

            // Get messages
            for (int i = 0; i < players.Count; i++)
            {
                infos[i] = new EventMessages.RoundDrawInfo
                {
                    RoundDrawType = RoundDrawType,
                    WaitingData   = waitingDataArray
                };
            }

            // Get point transfers
            // get player indices of those are ready and not
            var readyIndices    = new List <int>();
            var notReadyIndices = new List <int>();

            for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
            {
                if (waitingDataArray[playerIndex].WaitingTiles.Length > 0)
                {
                    readyIndices.Add(playerIndex);
                }
                else
                {
                    notReadyIndices.Add(playerIndex);
                }
            }

            next  = notReadyIndices.Contains(CurrentRoundStatus.OyaPlayerIndex);
            extra = true;
            // no one is ready or every one is ready
            if (readyIndices.Count == 0 || notReadyIndices.Count == 0)
            {
                return;
            }
            // get transfers according to total count of players
            switch (players.Count)
            {
            case 2:
                GetTransfersFor2(readyIndices, notReadyIndices, transfers);
                break;

            case 3:
                GetTransfersFor3(readyIndices, notReadyIndices, transfers);
                break;

            case 4:
                GetTransfersFor4(readyIndices, notReadyIndices, transfers);
                break;

            default:
                Debug.LogError("This should not happen");
                break;
            }

            // test for false richi
            foreach (var playerIndex in notReadyIndices)
            {
                if (CurrentRoundStatus.RichiStatus(playerIndex))
                {
                    GetTransfersForFalseRichi(playerIndex, transfers);
                }
            }
        }
Пример #11
0
 public override void OnClientStateExit()
 {
     CurrentRoundStatus.ClearPossibleWaitingTiles();
     controller.InTurnPanelManager.Close();
 }
Пример #12
0
 public override void OnServerStateExit()
 {
     PhotonNetwork.RemoveCallbackTarget(this);
     CurrentRoundStatus.CheckOneShot(CurrentPlayerIndex);
 }
Пример #13
0
        private OutTurnOperationType ChooseOperations()
        {
            Debug.Log($"[Server] Operation before choosing: {string.Join(",", Operations)}");
            // test every circumstances by priority
            // test for rong
            if (Operations.Any(op => op.Type == OutTurnOperationType.Rong))
            {
                // check if 3 rong
                if (CurrentRoundStatus.CheckThreeRongs(Operations))
                {
                    for (int i = 0; i < Operations.Length; i++)
                    {
                        Operations[i] = new OutTurnOperation
                        {
                            Type          = OutTurnOperationType.RoundDraw,
                            RoundDrawType = RoundDrawType.ThreeRong
                        };
                    }

                    return(OutTurnOperationType.RoundDraw);
                }

                for (int i = 0; i < Operations.Length; i++)
                {
                    if (Operations[i].Type != OutTurnOperationType.Rong)
                    {
                        Operations[i] = new OutTurnOperation {
                            Type = OutTurnOperationType.Skip
                        }
                    }
                    ;
                }

                return(OutTurnOperationType.Rong);
            }

            // check if 4 winds
            if (CurrentRoundStatus.CheckFourWinds())
            {
                Debug.Log($"[Server] Round draw -- Four winds");
                for (int i = 0; i < Operations.Length; i++)
                {
                    Operations[i] = new OutTurnOperation
                    {
                        Type          = OutTurnOperationType.RoundDraw,
                        RoundDrawType = RoundDrawType.FourWinds
                    };
                }

                return(OutTurnOperationType.RoundDraw);
            }

            // check if 4 richis
            if (CurrentRoundStatus.CheckFourRichis())
            {
                Debug.Log($"[Server] Round draw -- Four richis");
                for (int i = 0; i < Operations.Length; i++)
                {
                    Operations[i] = new OutTurnOperation
                    {
                        Type          = OutTurnOperationType.RoundDraw,
                        RoundDrawType = RoundDrawType.FourRichis
                    };
                }

                return(OutTurnOperationType.RoundDraw);
            }

            // check if 4 kongs
            if (CurrentRoundStatus.CheckFourKongs())
            {
                Debug.Log($"[Server] Round draw -- Four kongs");
                for (int i = 0; i < Operations.Length; i++)
                {
                    Operations[i] = new OutTurnOperation
                    {
                        Type          = OutTurnOperationType.RoundDraw,
                        RoundDrawType = RoundDrawType.FourKongs
                    };
                }

                return(OutTurnOperationType.RoundDraw);
            }

            // check if run out of tiles -- leads to a normal round draw
            if (MahjongSet.TilesRemain == gameSettings.MountainReservedTiles)
            {
                // no more tiles to draw and no one choose a rong operation.
                Debug.Log("No more tiles to draw and nobody claims a rong, the round has drawn.");
                for (int i = 0; i < Operations.Length; i++)
                {
                    Operations[i] = new OutTurnOperation
                    {
                        Type          = OutTurnOperationType.RoundDraw,
                        RoundDrawType = RoundDrawType.RoundDraw
                    }
                }
                ;
                return(OutTurnOperationType.RoundDraw);
            }

            // check if some one claimed kong
            if (Operations.Any(op => op.Type == OutTurnOperationType.Kong))
            {
                for (int i = 0; i < Operations.Length; i++)
                {
                    if (Operations[i].Type != OutTurnOperationType.Kong)
                    {
                        Operations[i] = new OutTurnOperation {
                            Type = OutTurnOperationType.Skip
                        }
                    }
                    ;
                }

                return(OutTurnOperationType.Kong);
            }

            // check if some one claimed pong
            if (Operations.Any(op => op.Type == OutTurnOperationType.Pong))
            {
                for (int i = 0; i < Operations.Length; i++)
                {
                    if (Operations[i].Type != OutTurnOperationType.Pong)
                    {
                        Operations[i] = new OutTurnOperation {
                            Type = OutTurnOperationType.Skip
                        }
                    }
                    ;
                }

                return(OutTurnOperationType.Pong);
            }

            // check if some one claimed chow
            if (Operations.Any(op => op.Type == OutTurnOperationType.Chow))
            {
                for (int i = 0; i < Operations.Length; i++)
                {
                    if (Operations[i].Type != OutTurnOperationType.Chow)
                    {
                        Operations[i] = new OutTurnOperation {
                            Type = OutTurnOperationType.Skip
                        }
                    }
                    ;
                }

                return(OutTurnOperationType.Chow);
            }

            // no particular operations -- skip
            return(OutTurnOperationType.Skip);
        }
Пример #14
0
        public override void OnServerStateEnter()
        {
            PhotonNetwork.AddCallbackTarget(this);
            int multiplier = gameSettings.GetMultiplier(CurrentRoundStatus.IsDealer(TsumoPlayerIndex), players.Count);
            var netInfo    = new NetworkPointInfo
            {
                Fu         = TsumoPointInfo.Fu,
                YakuValues = TsumoPointInfo.YakuList.ToArray(),
                Dora       = TsumoPointInfo.Dora,
                UraDora    = TsumoPointInfo.UraDora,
                RedDora    = TsumoPointInfo.RedDora,
                IsQTJ      = TsumoPointInfo.IsQTJ
            };
            var info = new EventMessages.TsumoInfo
            {
                TsumoPlayerIndex  = TsumoPlayerIndex,
                TsumoPlayerName   = CurrentRoundStatus.GetPlayerName(TsumoPlayerIndex),
                TsumoHandData     = CurrentRoundStatus.HandData(TsumoPlayerIndex),
                WinningTile       = WinningTile,
                DoraIndicators    = MahjongSet.DoraIndicators,
                UraDoraIndicators = MahjongSet.UraDoraIndicators,
                IsRichi           = CurrentRoundStatus.RichiStatus(TsumoPlayerIndex),
                TsumoPointInfo    = netInfo,
                TotalPoints       = TsumoPointInfo.BasePoint * multiplier
            };

            // send rpc calls
            ClientBehaviour.Instance.photonView.RPC("RpcTsumo", RpcTarget.AllBufferedViaServer, info);
            // get point transfers
            // todo -- tsumo loss related, now there is tsumo loss by default
            transfers = new List <PointTransfer>();
            for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
            {
                if (playerIndex == TsumoPlayerIndex)
                {
                    continue;
                }
                int amount = TsumoPointInfo.BasePoint;
                if (CurrentRoundStatus.IsDealer(playerIndex))
                {
                    amount *= 2;
                }
                int extraPoints = CurrentRoundStatus.ExtraPoints;
                transfers.Add(new PointTransfer
                {
                    From   = playerIndex,
                    To     = TsumoPlayerIndex,
                    Amount = amount + extraPoints
                });
            }

            // richi-sticks-points
            transfers.Add(new PointTransfer
            {
                From   = -1,
                To     = TsumoPlayerIndex,
                Amount = CurrentRoundStatus.RichiSticksPoints
            });
            responds = new bool[players.Count];
            // determine server time out
            serverTimeOut = ServerMaxTimeOut + ServerConstants.ServerTimeBuffer;
            firstTime     = Time.time;
        }
Пример #15
0
 private void EnableAllTiles()
 {
     CurrentRoundStatus.SetForbiddenTiles(null);
 }
Пример #16
0
        public override void OnServerStateEnter()
        {
            PhotonNetwork.AddCallbackTarget(this);
            responds = new bool[players.Count];
            var playerNames = RongPlayerIndices.Select(
                playerIndex => CurrentRoundStatus.GetPlayerName(playerIndex)
                ).ToArray();
            var handData = RongPlayerIndices.Select(
                playerIndex => CurrentRoundStatus.HandData(playerIndex)
                ).ToArray();
            var richiStatus = RongPlayerIndices.Select(
                playerIndex => CurrentRoundStatus.RichiStatus(playerIndex)
                ).ToArray();
            var multipliers = RongPlayerIndices.Select(
                playerIndex => gameSettings.GetMultiplier(CurrentRoundStatus.IsDealer(playerIndex), players.Count)
                ).ToArray();
            var totalPoints = RongPointInfos.Select((info, i) => info.BasePoint * multipliers[i]).ToArray();
            var netInfos    = RongPointInfos.Select(info => new NetworkPointInfo
            {
                Fu         = info.Fu,
                YakuValues = info.YakuList.ToArray(),
                Dora       = info.Dora,
                UraDora    = info.UraDora,
                RedDora    = info.RedDora,
                BeiDora    = info.BeiDora,
                IsQTJ      = info.IsQTJ
            }).ToArray();

            Debug.Log($"The following players are claiming rong: {string.Join(",", RongPlayerIndices)}, "
                      + $"PlayerNames: {string.Join(",", playerNames)}");
            var rongInfo = new EventMessages.RongInfo
            {
                RongPlayerIndices     = RongPlayerIndices,
                RongPlayerNames       = playerNames,
                HandData              = handData,
                WinningTile           = WinningTile,
                DoraIndicators        = MahjongSet.DoraIndicators,
                UraDoraIndicators     = MahjongSet.UraDoraIndicators,
                RongPlayerRichiStatus = richiStatus,
                RongPointInfos        = netInfos,
                TotalPoints           = totalPoints
            };

            // send rpc calls
            ClientBehaviour.Instance.photonView.RPC("RpcRong", RpcTarget.AllBufferedViaServer, rongInfo);
            // get point transfers
            transfers = new List <PointTransfer>();
            for (int i = 0; i < RongPlayerIndices.Length; i++)
            {
                var rongPlayerIndex = RongPlayerIndices[i];
                var point           = RongPointInfos[i];
                var multiplier      = multipliers[i];
                int pointValue      = point.BasePoint * multiplier;
                int extraPoints     = i == 0 ? CurrentRoundStatus.ExtraPoints * (players.Count - 1) : 0;
                transfers.Add(new PointTransfer
                {
                    From   = CurrentPlayerIndex,
                    To     = rongPlayerIndex,
                    Amount = pointValue + extraPoints
                });
            }

            // richi-sticks-points
            transfers.Add(new PointTransfer
            {
                From   = -1,
                To     = RongPlayerIndices[0],
                Amount = CurrentRoundStatus.RichiSticksPoints
            });
            next = !RongPlayerIndices.Contains(CurrentRoundStatus.OyaPlayerIndex);
            // determine server time out
            serverTimeOut = ServerMaxTimeOut * RongPointInfos.Length + ServerConstants.ServerTimeBuffer;
            firstTime     = Time.time;
        }