mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-03 04:36:03 +08:00
419 lines
17 KiB
C#
419 lines
17 KiB
C#
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<BaseGridFightComponent> 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<BaseGridFightSyncData> syncs = [];
|
|
|
|
var basicComp = GetComponent<GridFightBasicComponent>();
|
|
var levelComp = GetComponent<GridFightLevelComponent>();
|
|
var traitComp = GetComponent<GridFightTraitComponent>();
|
|
var itemsComponent = GetComponent<GridFightItemsComponent>();
|
|
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<GridFightRoleDamageSttInfo> 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<GridFightTraitDamageSttInfo> 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<GridFightShopComponent>().RefreshShop(true, false);
|
|
|
|
_ = CreatePendingAction<GridFightPortalBuffPendingAction>(sendPacket:false);
|
|
_ = CreatePendingAction<GridFightRoundBeginPendingAction>(sendPacket: false);
|
|
_ = CreatePendingAction<GridFightReturnPreparationPendingAction>(sendPacket: false);
|
|
}
|
|
|
|
public T GetComponent<T>() 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<GridFightRoleComponent>();
|
|
var levelComp = GetComponent<GridFightLevelComponent>();
|
|
var augmentComp = GetComponent<GridFightAugmentComponent>();
|
|
var itemsComp = GetComponent<GridFightItemsComponent>();
|
|
var traitComp = GetComponent<GridFightTraitComponent>();
|
|
var basicComp = GetComponent<GridFightBasicComponent>();
|
|
|
|
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<GridFightGameInfo> ToGameInfos()
|
|
{
|
|
return (from c in Components select c.ToProto()).ToList();
|
|
}
|
|
|
|
public GridFightGameData ToGameDataInfo()
|
|
{
|
|
return new GridFightGameData();
|
|
}
|
|
|
|
#region Pending Action
|
|
|
|
public SortedDictionary<uint, BaseGridFightPendingAction> 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<List<BaseGridFightSyncData>> CreatePendingAction<T>(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<GridFightBasicComponent>();
|
|
|
|
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<GridFightBasicComponent>();
|
|
var levelComp = GetComponent<GridFightLevelComponent>();
|
|
var roleComp = GetComponent<GridFightRoleComponent>();
|
|
var itemsComp = GetComponent<GridFightItemsComponent>();
|
|
|
|
var curAction = GetCurAction();
|
|
|
|
// end
|
|
var isFinish = true;
|
|
GridFightSrc src;
|
|
|
|
List<BaseGridFightSyncData> 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<GridFightAugmentComponent>()
|
|
.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,
|
|
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<List<BaseGridFightSyncData>> CheckCurNodeFinish(GridFightSrc src)
|
|
{
|
|
var levelComp = GetComponent<GridFightLevelComponent>();
|
|
var curSection = levelComp.CurrentSection;
|
|
|
|
if (curSection.Encounters.Count != 0) return [];
|
|
|
|
if (PendingActions.Count != 0) return [];
|
|
|
|
// next
|
|
return await levelComp.EnterNextSection(src:GridFightSrc.KGridFightSrcNone);
|
|
}
|
|
|
|
#endregion
|
|
} |