using EggLink.DanhengServer.Data; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.GridFight.Component; using EggLink.DanhengServer.GameServer.Game.GridFight.PendingAction; using EggLink.DanhengServer.GameServer.Game.GridFight.Sync; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Server.Packet.Send.GridFight; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Util; namespace EggLink.DanhengServer.GameServer.Game.GridFight; public class GridFightInstance(PlayerInstance player, uint season, uint divisionId, bool isOverLock, uint uniqueId) { public uint Season { get; } = season; public uint DivisionId { get; } = divisionId; public bool IsOverLock { get; } = isOverLock; public uint UniqueId { get; } = uniqueId; public List Components { get; } = []; public PlayerInstance Player { get; } = player; public BattleInstance? StartBattle() { var battle = Player.BattleManager!.StartGridFightBattle(this); return battle; } public async ValueTask EndBattle(BattleInstance battle, PVEBattleResultCsReq req) { if (battle.BattleEndStatus == BattleEndStatus.BattleEndQuit) return; List syncs = []; var basicComp = GetComponent(); var levelComp = GetComponent(); var traitComp = GetComponent(); var itemsComponent = GetComponent(); var prevData = basicComp.Data.Clone(); var curEncounter = levelComp.CurrentSection.Encounters[(int)(levelComp.CurrentSection.BranchId - 1)]; var expNum = 2u; var baseCoin = levelComp.CurrentSection.Excel.BasicGoldRewardNum; var interestCoin = Math.Min(basicComp.Data.CurGold / 10, basicComp.Data.MaxInterest); var progress = req.Stt.GridFightBattleStt.FinishProgress; if (progress == 100) { basicComp.Data.ComboNum++; } else { basicComp.Data.ComboNum = 0; // cost hp await basicComp.UpdateLineupHp(-5, false); } var end = levelComp.IsLastSection(); var comboCoin = basicComp.Data.ComboNum switch { >= 5 => 3u, 2 or 3 or 4 => 2u, 0 => 0u, _ => 1u }; await basicComp.UpdateGoldNum((int)(baseCoin + interestCoin + comboCoin), false); await basicComp.AddLevelExp(expNum, false); List sttList = []; foreach (var roleBattleStt in req.Stt.GridFightBattleStt.RoleBattleStt) { var res = await levelComp.AddRoleDamageStt(roleBattleStt.RoleBasicId, roleBattleStt.Damage, false); if (res.Item2 != null) sttList.Add(res.Item2); } List traitSttList = []; foreach (var traitBattleStt in req.Stt.GridFightBattleStt.TraitBattleStt) { var res = await levelComp.AddTraitDamageStt(traitBattleStt.TraitId, traitBattleStt.Damage, false); if (res.Item2 != null) traitSttList.Add(res.Item2); } var curData = basicComp.Data.Clone(); // if any drop var drops = await curEncounter.TakeMonsterDrop(itemsComponent); await Player.SendPacket(new PacketGridFightEndBattleStageNotify(this, expNum, prevData, curData, sttList, traitSttList, battle.BattleEndStatus == BattleEndStatus.BattleEndWin, baseCoin, interestCoin, comboCoin, drops.Item2, progress)); syncs.AddRange(drops.Item1); syncs.Add(new GridFightGoldSyncData(GridFightSrc.KGridFightSrcBattleEnd, basicComp.Data, 0, levelComp.CurrentSection.ChapterId, levelComp.CurrentSection.SectionId)); syncs.Add(new GridFightPlayerLevelSyncData(GridFightSrc.KGridFightSrcBattleEnd, basicComp.Data)); syncs.Add(new GridFightLineupHpSyncData(GridFightSrc.KGridFightSrcBattleEnd, basicComp.Data)); syncs.Add(new GridFightComboNumSyncData(GridFightSrc.KGridFightSrcBattleEnd, basicComp.Data)); syncs.Add(new GridFightRoleDamageSttSyncData(GridFightSrc.KGridFightSrcBattleEnd, levelComp)); syncs.AddRange(await levelComp.EnterNextSection(false)); // encounter drop syncs.AddRange(await curEncounter.TakeEncounterDrop(itemsComponent)); await Player.SendPacket(new PacketGridFightSyncUpdateResultScNotify(syncs)); // trait await traitComp.HandleBattleEnd(req, progress == 100); if (end) { // settle await Player.SendPacket(new PacketGridFightSettleNotify(this)); Player.GridFightManager!.GridFightInstance = null; } } public void InitializeComponents() { Components.Add(new GridFightBasicComponent(this)); Components.Add(new GridFightShopComponent(this)); Components.Add(new GridFightLevelComponent(this)); Components.Add(new GridFightRoleComponent(this)); Components.Add(new GridFightAugmentComponent(this)); Components.Add(new GridFightTraitComponent(this)); Components.Add(new GridFightItemsComponent(this)); Components.Add(new GridFightOrbComponent(this)); _ = GetComponent().RefreshShop(true, false); _ = CreatePendingAction(sendPacket:false); _ = CreatePendingAction(sendPacket: false); _ = CreatePendingAction(sendPacket: false); } public T GetComponent() where T : BaseGridFightComponent { return (T)Components.First(c => c is T); } public uint GetDivisionDifficulty() { return GameData.GridFightDivisionStageData.TryGetValue(DivisionId, out var excel) ? excel.EnemyDifficultyLevel : 0; } public GridFightCurrentInfo ToProto() { return new GridFightCurrentInfo { DivisionId = DivisionId, Season = Season, IsOverlock = IsOverLock, UniqueId = UniqueId, PendingAction = GetCurAction().ToProto(), GridFightGameData = ToGameDataInfo(), RogueCurrentGameInfo = { ToGameInfos() } }; } public GridFightFinishInfo ToFinishInfo() { var roleComp = GetComponent(); var levelComp = GetComponent(); var augmentComp = GetComponent(); var itemsComp = GetComponent(); var traitComp = GetComponent(); var basicComp = GetComponent(); return new GridFightFinishInfo { Reason = GridFightSettleReason.KGridFightSettleReasonFinish, SettleRoleUniqueIdList = { roleComp.Data.Roles.Where(x => x.Pos <= GridFightRoleComponent.PrepareAreaPos).Select(x => x.UniqueId) }, GridFightEquipmentList = { itemsComp.Data.EquipmentItems.Select(x => x.ToProto()) }, GridFightAugmentInfo = { augmentComp.Data.Augments.Select(x => x.ToProto()) }, SettlePortalBuffList = { levelComp.PortalBuffs.Select(x => x.ToProto()) }, TraitDamageSttList = { levelComp.TraitDamageSttInfos.Select(x => x.ToProto(traitComp)) }, RoleDamageSttList = { levelComp.RoleDamageSttInfos.Select(x => x.ToProto()) }, GridFightTraitInfo = { traitComp.Data.Traits.Select(x => x.ToProto(roleComp)) }, GridGameRoleList = { roleComp.Data.Roles.Select(x => x.ToProto()) }, RogueTournCurAreaInfo = new GridFightFinishAreaInfo { ChapterId = levelComp.CurrentSection.ChapterId, GameDivisionId = DivisionId, GridFightCurLineupHp = basicComp.Data.CurHp, GridFightMaxLineupHp = 100, RouteId = levelComp.CurrentSection.Excel.ID, SectionId = levelComp.CurrentSection.SectionId } }; } public List ToGameInfos() { return (from c in Components select c.ToProto()).ToList(); } public GridFightGameData ToGameDataInfo() { return new GridFightGameData(); } #region Pending Action public SortedDictionary PendingActions { get; set; } = new(); private uint _curQueuePos = 1; public BaseGridFightPendingAction GetCurAction() { if (PendingActions.Count > 0) { return PendingActions.First().Value; } return new GridFightEmptyPendingAction(this); } public uint AddPendingAction(BaseGridFightPendingAction action) { var pos = _curQueuePos++; action.QueuePosition = pos; PendingActions[pos] = action; return pos; } public async ValueTask> CreatePendingAction(GridFightSrc src = GridFightSrc.KGridFightSrcEnterNode, bool sendPacket = true, params object[] initializeParam) where T: BaseGridFightPendingAction { object[] paramList = [this, ..initializeParam]; var action = (T)Activator.CreateInstance(typeof(T), paramList)!; var basicComp = GetComponent(); AddPendingAction(action); basicComp.Data.LockReason = (uint)GridFightLockReason.KGridFightLockReasonPendingAction; basicComp.Data.LockType = (uint)GridFightLockType.KGridFightLockTypeAll; var res = new GridFightPendingActionSyncData(src, action); if (sendPacket) await Player.SendPacket(new PacketGridFightSyncUpdateResultScNotify(res)); return [res, new GridFightLockInfoSyncData(src, basicComp.Data.Clone())]; } public async ValueTask HandleResultRequest(GridFightHandlePendingActionCsReq req) { var basicComp = GetComponent(); var levelComp = GetComponent(); var roleComp = GetComponent(); var itemsComp = GetComponent(); var curAction = GetCurAction(); // end var isFinish = true; GridFightSrc src; List syncs = []; switch (req.GridFightActionTypeCase) { case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.PortalBuffAction: { src = GridFightSrc.KGridFightSrcSelectPortalBuff; syncs.AddRange(await levelComp.AddPortalBuff(req.PortalBuffAction.SelectPortalBuffId, false, src)); // initial supply await basicComp.UpdateGoldNum(5, false, GridFightSrc.KGridFightSrcInitialSupplySelect); syncs.Add(new GridFightGoldSyncData(GridFightSrc.KGridFightSrcInitialSupplySelect, basicComp.Data)); var rolePool = GameData.GridFightRoleBasicInfoData.Values.Where(x => x.Rarity == 1).ToList(); for (var i = 0; i < 2; i++) { syncs.AddRange(await roleComp.AddAvatar(rolePool.RandomElement().ID, 1, false, true, GridFightSrc.KGridFightSrcInitialSupplySelect)); } syncs.AddRange(await itemsComp.UpdateConsumable(350102, 1, GridFightSrc.KGridFightSrcInitialSupplySelect, false)); break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.PortalBuffRerollAction: { if (curAction is GridFightPortalBuffPendingAction portalBuffAction) { isFinish = false; await portalBuffAction.RerollBuff(); } break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.AugmentAction: { src = GridFightSrc.KGridFightSrcSelectAugment; syncs.AddRange(await GetComponent() .AddAugment(req.AugmentAction.AugmentId, false, src)); break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.RerollAugmentAction: { if (curAction is GridFightAugmentPendingAction augmentAction) { isFinish = false; await augmentAction.RerollAugment(req.RerollAugmentAction.AugmentId); } break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.EliteBranchAction: { var target = req.EliteBranchAction.EliteBranchId; levelComp.CurrentSection.BranchId = target; // sync syncs.Add(new GridFightLevelSyncData(GridFightSrc.KGridFightSrcNone, levelComp)); break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.SupplyAction: { src = GridFightSrc.KGridFightSrcSelectSupply; PendingActions.Remove(curAction.QueuePosition); if (curAction is GridFightSupplyPendingAction supplyAction) { foreach (var supply in req.SupplyAction.SelectSupplyIndexes) { var role = supplyAction.RoleList[(int)supply - 1]; syncs.AddRange(await roleComp.AddAvatar(role.RoleId, 1, false, true, GridFightSrc.KGridFightSrcSelectSupply, 0, 0, null, req.SupplyAction.SelectSupplyIndexes.ToArray())); // add equipment var res = await itemsComp.AddEquipment(role.EquipmentId, GridFightSrc.KGridFightSrcSelectSupply, false, 0, req.SupplyAction.SelectSupplyIndexes.ToArray()); syncs.AddRange(res.Item2); } } syncs.AddRange(await CheckCurNodeFinish(src)); break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.RecommendEquipmentAction: { var target = req.RecommendEquipmentAction.SelectEquipmentId; var res = await itemsComp.AddEquipment(target, GridFightSrc.KGridFightSrcNone, false); syncs.AddRange(res.Item2); break; } case GridFightHandlePendingActionCsReq.GridFightActionTypeOneofCase.TraitAction: { if (curAction is GridFightTraitPendingAction traitAction) { traitAction.Effect.CoreRoleUniqueId = req.TraitAction.UniqueId; // sync syncs.Add(new GridFightTraitSyncData(GridFightSrc.KGridFightSrcTraitEffectUpdate, traitAction.Effect, 0, traitAction.Effect.TraitId, traitAction.Effect.EffectId)); } break; } } if (isFinish) { PendingActions.Remove(curAction.QueuePosition); syncs.Add(new GridFightFinishPendingActionSyncData(GridFightSrc.KGridFightSrcNone, curAction.QueuePosition)); // unlock basicComp.Data.LockReason = (uint)GridFightLockReason.KGridFightLockReasonUnknown; basicComp.Data.LockType = (uint)GridFightLockType.KGridFightLockTypeNone; syncs.Add(new GridFightLockInfoSyncData(GridFightSrc.KGridFightSrcNone, basicComp.Data.Clone())); } if (PendingActions.Count > 0) { basicComp.Data.LockReason = (uint)GridFightLockReason.KGridFightLockReasonPendingAction; basicComp.Data.LockType = (uint)GridFightLockType.KGridFightLockTypeAll; syncs.Add(new GridFightPendingActionSyncData(GridFightSrc.KGridFightSrcNone, GetCurAction(), 1)); syncs.Add(new GridFightLockInfoSyncData(GridFightSrc.KGridFightSrcNone, basicComp.Data.Clone(), 1)); } await Player.SendPacket(new PacketGridFightSyncUpdateResultScNotify(syncs)); } public async ValueTask> CheckCurNodeFinish(GridFightSrc src) { var levelComp = GetComponent(); var curSection = levelComp.CurrentSection; if (curSection.Encounters.Count != 0) return []; if (PendingActions.Count != 0) return []; // next return await levelComp.EnterNextSection(src:GridFightSrc.KGridFightSrcNone); } #endregion }