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.Mission.FinishAction; using EggLink.DanhengServer.GameServer.Game.Mission.FinishType; 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; using System.Reflection; 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; var types = Assembly.GetExecutingAssembly().GetTypes(); foreach (var type in types) { var attr = type.GetCustomAttribute(); if (attr == null) continue; var handler = (ModifierEffectHandler)Activator.CreateInstance(type, null)!; ModifierEffectHandlers.Add(attr.EffectType, handler); } 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 DifficultyLevel { get; set; } = []; public List BossBuff { get; set; } = []; public int BossAeonId { get; set; } public List DifficultyExcel { get; set; } = []; public ChessRogueDiceInstance DiceInstance { get; set; } public Dictionary ModifierEffectHandlers { get; set; } = []; public Dictionary RogueCells { get; set; } = []; public ChessRogueCellInstance? CurCell { get; set; } public List CanMoveCellIdList { get; set; } = []; public List HistoryCell { get; set; } = []; public int StartCell { get; set; } public List 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 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(); 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 difficultyIds = []; List 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 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() { await base.UpdateMenu(); 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.EnterMissionScene(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(); //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 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 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 ChessRogueCurrentInfo ToCurrentProto() { var proto = new ChessRogueCurrentInfo { RogueSubMode = (uint)RogueSubMode }; proto.RogueCurrentGameInfo.AddRange(ToGameInfo()); return proto; } 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 ToGameInfo() { var proto = new List { 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 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 }