using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Config.AdventureAbility;
using EggLink.DanhengServer.Data.Custom;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Enums.Rogue;
using EggLink.DanhengServer.GameServer.Game.Battle;
using EggLink.DanhengServer.GameServer.Game.ChessRogue.Cell;
using EggLink.DanhengServer.GameServer.Game.ChessRogue.Dice;
using EggLink.DanhengServer.GameServer.Game.ChessRogue.Modifier.ModifierEffect;
using EggLink.DanhengServer.GameServer.Game.Player;
using EggLink.DanhengServer.GameServer.Game.Rogue;
using EggLink.DanhengServer.GameServer.Game.Rogue.Buff;
using EggLink.DanhengServer.GameServer.Game.Rogue.Event;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.ChessRogue;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.RogueModifier;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;

namespace EggLink.DanhengServer.GameServer.Game.ChessRogue;

public class ChessRogueInstance : BaseRogueInstance
{
    public ChessRogueInstance(PlayerInstance player, RogueDLCAreaExcel areaExcel, RogueNousAeonExcel aeonExcel,
        RogueSubModeEnum rogueSubMode, int branchId) : base(player, rogueSubMode, aeonExcel.RogueBuffType)
    {
        CurLineup = player.LineupManager!.GetCurLineup()!;
        Player = player;
        AeonId = aeonExcel.AeonID;
        AreaExcel = areaExcel;
        AeonExcel = aeonExcel;
        DiceInstance = new ChessRogueDiceInstance(this, player.ChessRogueManager!.GetDice(branchId));
        Layers = areaExcel.LayerIDList;
        CurLayer = Layers.First();
        EventManager = new RogueEventManager(player, this);
        RogueType = rogueSubMode == RogueSubModeEnum.ChessRogueNous ? 160 : 130;

        foreach (var difficulty in areaExcel.DifficultyID)
            if (GameData.RogueDLCDifficultyData.TryGetValue(difficulty, out var diff))
                DifficultyExcel.Add(diff);

        var action = new RogueActionInstance
        {
            QueuePosition = CurActionQueuePosition
        };
        action.SetBonus();

        RogueActions.Add(CurActionQueuePosition, action);

        // generate cells
        GenerateLayer();
    }

    public RogueDLCAreaExcel AreaExcel { get; set; }
    public RogueNousAeonExcel AeonExcel { get; set; }
    public List<RogueNousDifficultyLevelExcel> DifficultyLevel { get; set; } = [];
    public List<RogueDLCBossDecayExcel> BossBuff { get; set; } = [];
    public int BossAeonId { get; set; }
    public List<RogueDLCDifficultyExcel> DifficultyExcel { get; set; } = [];
    public ChessRogueDiceInstance DiceInstance { get; set; }
    public static Dictionary<ModifierEffectTypeEnum, ModifierEffectHandler> ModifierEffectHandlers { get; set; } = [];

    public Dictionary<int, ChessRogueCellInstance> RogueCells { get; set; } = [];
    public ChessRogueCellInstance? CurCell { get; set; }
    public List<int> CanMoveCellIdList { get; set; } = [];
    public List<ChessRogueCellInstance> HistoryCell { get; set; } = [];
    public int StartCell { get; set; }

    public List<int> Layers { get; set; }
    public int CurLayer { get; set; }
    public RogueDLCChessBoardExcel? CurBoardExcel { get; set; }
    public ChessRogueLevelStatus CurLevelStatus { get; set; } = ChessRogueLevelStatus.ChessRogueLevelProcessing;

    public bool FirstEnterBattle { get; set; } = true;
    public int LayerMap { get; set; }

    public int ActionPoint { get; set; } = 15;
    public int CurModifierId { get; set; } = 1;

    public List<int> DisableAeonIds { get; set; } = [];

    public override void OnBattleStart(BattleInstance battle)
    {
        base.OnBattleStart(battle);

        if (DifficultyExcel.Count > 0) battle.CustomLevel = DifficultyExcel.RandomElement().LevelList.RandomElement();

        if (ActionPoint < 0)
        {
            GameData.ActionPointOverdrawData.TryGetValue(Math.Max(ActionPoint, -50), out var overdrawData);
            if (overdrawData != null)
                battle.Buffs.Add(new MazeBuff(overdrawData.MazeBuff, 1, -1)
                {
                    WaveFlag = -1
                });
        }

        CalculateDifficulty(battle);

        if (CurCell!.BlockType == RogueDLCBlockTypeEnum.MonsterNousBoss ||
            CurCell!.BlockType == RogueDLCBlockTypeEnum.MonsterSwarmBoss)
        {
            var buffList = new List<int>();
            foreach (var buff in BossBuff)
                if (buff.EffectType == BossDecayEffectTypeEnum.AddMazeBuffList)
                    buffList.SafeAddRange(buff.EffectParamList); // add buff
                else
                    foreach (var param in buff.EffectParamList) // remove buff
                        buffList.Remove(param);

            foreach (var buffId in buffList)
                battle.Buffs.Add(new MazeBuff(buffId, 1, -1)
                {
                    WaveFlag = -1
                });
        }


        if (DiceInstance.Modifier == null) return;
        var modifier = DiceInstance.Modifier;
        modifier.BeforeBattle(this, battle);
    }

    public void CalculateDifficulty(BattleInstance battle)
    {
        if (DifficultyExcel.Count > 0)
        {
            List<int> difficultyIds = [];
            List<MazeBuff> buffs = [];

            foreach (var difficulty in DifficultyLevel) difficultyIds.Add(difficulty.DifficultyID);

            if (difficultyIds.Contains(103))
                buffs.Add(new MazeBuff(610121, 1, -1)
                {
                    WaveFlag = -1
                });

            if (difficultyIds.Contains(105))
                buffs.Add(new MazeBuff(610121, 1, -1)
                {
                    WaveFlag = -1
                });

            var attributeBuffId = 0;

            if (difficultyIds.Contains(106))
                attributeBuffId = 650204;
            else if (difficultyIds.Contains(104))
                attributeBuffId = 650203;
            else if (difficultyIds.Contains(102))
                attributeBuffId = 650202;
            else if (difficultyIds.Contains(101)) attributeBuffId = 650201;

            buffs.Add(new MazeBuff(attributeBuffId, 1, -1)
            {
                WaveFlag = -1,
                DynamicValues = { { "_RogueLayer", Layers.IndexOf(CurLayer) + 1 } }
            });

            foreach (var buff in buffs) battle.Buffs.Add(buff);
        }
    }

    public override async ValueTask OnBattleEnd(BattleInstance battle, PVEBattleResultCsReq req)
    {
        foreach (var miracle in RogueMiracles.Values) miracle.OnEndBattle(battle);

        if (req.EndStatus != BattleEndStatus.BattleEndWin)
        {
            // quit
            CurLevelStatus = ChessRogueLevelStatus.ChessRogueLevelFailed;
            await Player.SendPacket(new PacketChessRogueUpdateLevelBaseInfoScNotify(CurLevelStatus));
            return;
        }

        await RollBuff(battle.Stages.Count);

        if (DiceInstance.Modifier != null)
        {
            var modifier = DiceInstance.Modifier;
            await modifier.AfterBattle(this, battle);
        }

        switch (CurCell!.BlockType)
        {
            case RogueDLCBlockTypeEnum.MonsterBoss:
                await Player.SendPacket(new PacketChessRogueLayerAccountInfoNotify(this));
                break;
            case RogueDLCBlockTypeEnum.MonsterNousBoss:
            case RogueDLCBlockTypeEnum.MonsterSwarmBoss:
                CurLevelStatus = ChessRogueLevelStatus.ChessRogueLevelFinish;
                await Player.SendPacket(new PacketChessRogueLayerAccountInfoNotify(this));
                await Player.SendPacket(new PacketChessRogueUpdateLevelBaseInfoScNotify(CurLevelStatus));
                break;
        }
    }

    #region Modifier

    public async ValueTask ApplyModifier(int selectCellId)
    {
        if (DiceInstance.Modifier == null) return;

        var modifier = DiceInstance.Modifier;
        if (selectCellId == 0)
        {
            modifier.IsConfirmed = true;
            await Player.SendPacket(new PacketRogueModifierStageStartNotify(modifier.SourceType));
            // gain money
            await GainMoney(10, 2);

            await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
            return;
        }

        await modifier.SelectModifierCell(this, selectCellId);

        await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
    }

    #endregion

    #region Buff Management

    public override void HandleMazeBuffModifier(AdventureModifierConfig config, MazeBuff buff)
    {
        var task = config.OnBeforeBattle;

        foreach (var info in task)
        {
            if (!info.Type.Replace("RPG.GameCore.", "").StartsWith("SetDynamicValueBy")) continue;
            var key = info.Type.Replace("RPG.GameCore.SetDynamicValueBy", "");
            var value = key switch
            {
                "ItemNum" => CurMoney,
                "RogueMiracleNum" => RogueMiracles.Count,
                "RogueBuffNumWithType" => RogueBuffs.Count,
                "RogueModifierCount" => DiceInstance.Modifier == null ? 0 : 1,
                "RogueLayer" => Layers.IndexOf(CurLayer) + 1,
                _ => 0
            };

            key = key switch
            {
                "ItemNum" => "ItemNumber",
                "RogueMiracleNum" => "RogueMiracleNumber",
                "RogueBuffNumWithType" => "RogueBuffNumberWithType",
                "RogueModifierCount" => "RogueModifierCount",
                "RogueLayer" => "_RogueLayer",
                _ => key
            };

            buff.DynamicValues.Add(key, value);
        }
    }

    public override async ValueTask RollBuff(int amount)
    {
        if (CurCell!.BlockType == RogueDLCBlockTypeEnum.MonsterBoss)
        {
            await RollBuff(amount, 100003, 2); // boss room
            await RollMiracle(1);
        }
        else
        {
            await RollBuff(amount, 100005);
        }
    }

    public async ValueTask AddAeonBuff() // TODO: support for aeon cross
    {
        if (AeonBuffPending) return;
        if (CurAeonBuffCount + CurAeonEnhanceCount >= 4)
            // max aeon buff count
            return;
        var curAeonBuffCount = 0; // current path buff count
        var hintId = AeonId * 100 + 1;
        var enhanceData = GameData.RogueAeonEnhanceData[AeonId].Select(x => x as BaseRogueBuffExcel).ToList();
        var buffData = GameData.RogueAeonBuffData[AeonId];
        foreach (var buff in RogueBuffs)
            if (buff.BuffExcel.RogueBuffType == AeonExcel.RogueBuffType)
            {
                if (buff.BuffExcel is RogueBuffExcel { IsAeonBuff: false })
                {
                    curAeonBuffCount++;
                }
                else
                {
                    hintId++; // next hint
                    enhanceData.Remove(buff.BuffExcel);
                }
            }

        var needAeonBuffCount = (CurAeonBuffCount + CurAeonEnhanceCount) switch
        {
            0 => 3,
            1 => 6,
            2 => 10,
            3 => 14,
            _ => 100
        };

        if (curAeonBuffCount >= needAeonBuffCount)
        {
            RogueBuffSelectMenu menu = new(this)
            {
                QueueAppend = 2,
                HintId = hintId,
                RollMaxCount = 0,
                RollFreeCount = 0,
                IsAeonBuff = true
            };
            if (CurAeonBuffCount < 1)
            {
                CurAeonBuffCount++;
                // add aeon buff
                menu.RollBuff([buffData], 1);
            }
            else
            {
                CurAeonEnhanceCount++;
                // add enhance buff
                menu.RollBuff(enhanceData, enhanceData.Count);
            }

            var action = menu.GetActionInstance();
            RogueActions.Add(action.QueuePosition, action);

            AeonBuffPending = true;
            await UpdateMenu();
        }
    }

    public override async ValueTask UpdateMenu(int position = 0)
    {
        await base.UpdateMenu(position);


        await AddAeonBuff(); // check if aeon buff can be added
    }

    #endregion

    #region Map Management

    public async ValueTask EnterNextLayer()
    {
        HistoryCell.Clear();
        CurLayer = Layers[Layers.IndexOf(CurLayer) + 1];
        CurLevelStatus = ChessRogueLevelStatus.ChessRogueLevelProcessing;

        GenerateLayer();
        await EnterCell(StartCell);

        await Player.SendPacket(new PacketChessRogueEnterNextLayerScRsp(this));
    }

    public void GenerateLayer()
    {
        var level = Layers.IndexOf(CurLayer) + 1;
        FirstEnterBattle = true;

        LayerMap = GameConstants.AllowedChessRogueEntranceId.RandomElement();

        CurBoardExcel = RogueSubMode == RogueSubModeEnum.ChessRogue
            ? GameData.RogueSwarmChessBoardData[level].RandomElement()
            : GameData.RogueNousChessBoardData[level].RandomElement();

        RogueCells.Clear();
        CurCell = null;

        StartCell = CurBoardExcel.MapInfo!.StartGridItemID;

        foreach (var item in CurBoardExcel.MapInfo!.RogueChestGridItemMap)
        {
            var cell = new ChessRogueCellInstance(this, item.Value)
            {
                PosY = item.Value.PosY,
                PosX = item.Value.PosX,
                CellId = item.Key
            };
            RogueCells.Add(item.Key, cell);

            //if (cell.GetCellId() == CurBoardExcel.MapInfo!.EndGridItemID)  // last cell
            //{
            cell.Init();
            //}
        }
    }

    public async ValueTask EnterCell(int id, int monsterId = 0)
    {
        if (RogueCells.TryGetValue(id, out var cell))
        {
            if (monsterId > 0)
            {
                cell.SelectMonsterId = monsterId;
                if (BossAeonId == 0) BossAeonId = cell.GetBossAeonId(monsterId);

                var excel = cell.GetBossDecayExcel(monsterId);
                if (excel != null) BossBuff.Add(excel);
            }

            CurCell = cell;
            cell.CellStatus = ChessRogueBoardCellStatus.Finish;

            await Player.EnterSceneByEntranceId(cell.GetEntryId(), cell.RoomConfig!.AnchorGroup,
                cell.RoomConfig!.AnchorId,
                false);

            HistoryCell.Add(cell);

            DiceInstance.CurSurfaceId = 0;
            DiceInstance.DiceStatus = ChessRogueDiceStatus.ChessRogueDiceIdle;
            await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
        }
    }

    public async ValueTask SelectCell(int cellId)
    {
        var cell = RogueCells[cellId];
        if (cell.CellStatus == ChessRogueBoardCellStatus.Finish) return;

        cell.CellStatus = ChessRogueBoardCellStatus.Selected;

        await Player.SendPacket(new PacketChessRogueCellUpdateNotify(cell, CurBoardExcel?.ChessBoardID ?? 0));
        await CostActionPoint(1);

        if (DiceInstance.Modifier != null)
        {
            var modifier = DiceInstance.Modifier;
            await modifier.SelectCell(this, cellId);
        }

        await Player.SendPacket(new PacketChessRogueSelectCellScRsp(cellId));
    }

    #endregion

    #region Dice Management

    public async ValueTask RollDice()
    {
        DiceInstance.RollDice();

        await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
        await Player.SendPacket(new PacketChessRogueRollDiceScRsp(DiceInstance));
    }

    public async ValueTask ReRollDice()
    {
        DiceInstance.RerollTimes--;
        DiceInstance.RollDice();

        await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
        await Player.SendPacket(new PacketChessRogueReRollDiceScRsp(DiceInstance));
    }

    public async ValueTask CheatDice(int surfaceId)
    {
        DiceInstance.CheatTimes--;
        DiceInstance.CurSurfaceId = surfaceId;

        await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
        await Player.SendPacket(new PacketChessRogueCheatRollScRsp(DiceInstance, surfaceId));
    }

    public async ValueTask ConfirmRoll()
    {
        await DiceInstance.ConfirmDice();

        await Player.SendPacket(new PacketChessRogueUpdateDiceInfoScNotify(DiceInstance));
        await Player.SendPacket(new PacketChessRogueConfirmRollScRsp(DiceInstance));

        //var cellIds = new List<int>();

        //foreach (var cell in RogueCells)
        //{
        //    if (cell.Key == CurCell!.GetCellId() || cell.Value.CellStatus == ChessRogueBoardCellStatus.Finish)
        //    {
        //        continue;
        //    }
        //    cellIds.Add(cell.Key);
        //}

        //Player.SendPacket(new PacketChessRogueUpdateAllowedSelectCellScNotify(CurLayerData![-1][0], cellIds));
    }

    #endregion

    #region Action Management

    public async ValueTask CostActionPoint(int cost)
    {
        ActionPoint -= cost;

        await Player.SendPacket(new PacketChessRogueUpdateActionPointScNotify(ActionPoint));
    }

    public async ValueTask QuitRogue()
    {
        CurLevelStatus = ChessRogueLevelStatus.ChessRogueLevelFinish;
        await Player.SendPacket(new PacketChessRogueUpdateLevelBaseInfoScNotify(CurLevelStatus));

        Player.ChessRogueManager!.RogueInstance = null;
        await Player.EnterScene(801120102, 0, false);
        Player.LineupManager!.SetExtraLineup(ExtraLineupType.LineupNone, []);

        await Player.SendPacket(new PacketChessRogueQuitScRsp(this));
    }

    public async ValueTask LeaveRogue()
    {
        CurLevelStatus = ChessRogueLevelStatus.ChessRogueLevelFinish;
        await Player.SendPacket(new PacketChessRogueUpdateLevelBaseInfoScNotify(CurLevelStatus));

        Player.ChessRogueManager!.RogueInstance = null;
        await Player.EnterScene(801120102, 0, false);
        Player.LineupManager!.SetExtraLineup(ExtraLineupType.LineupNone, []);

        await Player.SendPacket(new PacketChessRogueLeaveScRsp(this));
    }

    #endregion

    #region Serialization

    public ChessRogueInfo ToStageProto()
    {
        var playerInfo = new ChessRogueInfo
        {
            Lineup = Player.LineupManager!.GetCurLineup()!.ToProto(),
            Scene = Player.SceneInstance!.ToProto()
        };

        return playerInfo;
    }

    public ChessRogueGameInfo ToRogueGameInfo()
    {
        var proto = new ChessRogueGameInfo
        {
            GameMiracleInfo = ToMiracleInfo(),
            RogueBuffInfo = ToBuffInfo(),
            RogueAeonInfo = ToAeonInfo(),
            RogueSubMode = (uint)RogueSubMode,
            RogueDiceInfo = DiceInstance.ToProto(),
            RogueLineupInfo = ToLineupInfo(),
            RogueDifficultyInfo = ToDifficultyInfo(),
            VirtualItemInfo = ToVirtualItemInfo(),
            LevelInfo = ToLevelInfo(),
            PendingAction = RogueActions.Count > 0
                ? RogueActions.First().Value.ToProto()
                : new RogueCommonPendingAction()
        };

        proto.RogueCurrentGameInfo.AddRange(ToGameInfo());

        return proto;
    }

    public ChessRogueMiracleInfo ToMiracleInfo()
    {
        var proto = new ChessRogueMiracleInfo
        {
            ChessRogueMiracleInfo_ = new ChessRogueMiracle()
        };

        proto.ChessRogueMiracleInfo_.MiracleList.AddRange(RogueMiracles.Select(x => x.Value.ToGameMiracleProto())
            .ToList());

        return proto;
    }

    public ChessRogueBuffInfo ToBuffInfo()
    {
        var proto = new ChessRogueBuffInfo
        {
            ChessRogueBuffInfo_ = new ChessRogueBuff()
        };

        proto.ChessRogueBuffInfo_.BuffList.AddRange(RogueBuffs.Select(x => x.ToCommonProto()).ToList());

        return proto;
    }

    public ChessRogueAeonInfo ToAeonInfo()
    {
        var proto = new ChessRogueAeonInfo
        {
            GameAeonId = (uint)AeonId
        };

        return proto;
    }

    public ChessRogueGameAeonInfo ToGameAeonInfo()
    {
        var proto = new ChessRogueGameAeonInfo
        {
            GameAeonId = (uint)AeonId
        };

        return proto;
    }

    public ChessRogueLineupInfo ToLineupInfo()
    {
        var proto = new ChessRogueLineupInfo
        {
            ReviveInfo = new RogueAvatarReviveCost
            {
                RogueReviveCost = new ItemCostData
                {
                    ItemList =
                    {
                        new ItemCost
                        {
                            PileItem = new PileItem
                            {
                                ItemId = 31,
                                ItemNum = (uint)CurReviveCost
                            }
                        }
                    }
                }
            }
        };

        foreach (var avatar in CurLineup!.BaseAvatars!)
            proto.ChessAvatarList.Add(new ChessRogueLineupAvatarInfo
            {
                AvatarId = (uint)avatar.BaseAvatarId
            });

        return proto;
    }

    public List<RogueGameInfo> ToGameInfo()
    {
        var proto = new List<RogueGameInfo>
        {
            new()
            {
                RogueDifficultyInfo = ToDifficultyLevelInfo()
            },
            new()
            {
                RogueAeonInfo = ToGameAeonInfo()
            },
            new(),
            new()
            {
                GameMiracleInfo = ToMiracleInfo()
            },
            new()
            {
                RogueBuffInfo = ToBuffInfo()
            }
        };

        return proto;
    }

    public ChessRogueCurrentDifficultyInfo ToDifficultyInfo()
    {
        var proto = new ChessRogueCurrentDifficultyInfo();

        foreach (var level in DifficultyLevel) proto.DifficultyIdList.Add((uint)level.DifficultyID);

        return proto;
    }

    public RogueDifficultyLevelInfo ToDifficultyLevelInfo()
    {
        var proto = new RogueDifficultyLevelInfo();

        foreach (var level in DifficultyLevel) proto.DifficultyIdList.Add((uint)level.DifficultyID);

        return proto;
    }

    public RogueVirtualItem ToVirtualItemInfo()
    {
        return new RogueVirtualItem
        {
            RogueMoney = (uint)CurMoney
        };
    }

    public ChessRogueLevelInfo ToLevelInfo()
    {
        List<uint> canSelected = [];
        foreach (var cell in RogueCells.Where(cell => cell.Value.CellStatus == ChessRogueBoardCellStatus.Idle))
        {
            if (cell.Value.PosY == CurCell!.PosY - 1 || cell.Value.PosY == CurCell!.PosY + 1)
                if (cell.Value.PosX == CurCell!.PosX || cell.Value.PosX == CurCell!.PosX + 1)
                    canSelected.Add((uint)cell.Value.GetCellId());
            if (cell.Value.PosY != CurCell!.PosY) continue;
            if (cell.Value.PosX == CurCell!.PosX + 2)
                canSelected.Add((uint)cell.Value.GetCellId());
        }

        canSelected.AddRange(CanMoveCellIdList.Select(i => (uint)i));

        CanMoveCellIdList.Clear(); // clear

        var proto = new ChessRogueLevelInfo
        {
            LevelStatus = (uint)CurLevelStatus,
            ActionPoint = ActionPoint,
            Id = (uint)AreaExcel.AreaID,
            LayerId = (uint)CurLayer,
            AreaInfo = new ChessRogueAreaInfo
            {
                LayerStatus = ChessRogueBoardCellStatus.Processing,
                CurId = (uint)CurCell!.GetCellId(),
                CurBoardId = (uint)(CurBoardExcel?.ChessBoardID ?? 0),
                Cell = new CellInfo
                {
                    CellList = { RogueCells.Select(x => x.Value.ToProto()).ToList() }
                },
                AllowSelectCellIdList = { canSelected },
                HistoryCell = { HistoryCell.Select(x => x.ToHistoryProto()).ToList() }
            }
        };

        return proto;
    }

    public ChessRogueFinishInfo ToFinishInfo()
    {
        var info = new ChessRogueFinishInfo
        {
            //EndAreaId = (uint)AreaExcel.AreaID,
            //LastLayerId = (uint)CurLayer,
            //RogueLineup = CurLineup!.ToProto(),
            //DifficultyLevel =
            //    uint.Parse(AreaExcel.AreaID.ToString().Substring(AreaExcel.AreaID.ToString().Length - 1, 1)),
            RogueSubMode = (uint)RogueSubMode,
            RogueBuffInfo = new ChessRogueBuff
            {
                BuffList = { RogueBuffs.Select(x => x.ToCommonProto()) }
            },
            GameMiracleInfo = new ChessRogueMiracle
            {
                MiracleList = { RogueMiracles.Select(x => x.Value.ToGameMiracleProto()) }
            }
        };

        return info;
    }

    #endregion
}