Files
2025-11-01 11:57:29 +08:00

332 lines
13 KiB
C#

using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Avatar;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Enums.Avatar;
using EggLink.DanhengServer.GameServer.Game.Battle.Custom;
using EggLink.DanhengServer.GameServer.Game.Lineup;
using EggLink.DanhengServer.GameServer.Game.Player;
using EggLink.DanhengServer.GameServer.Game.Scene;
using EggLink.DanhengServer.GameServer.Game.Scene.Entity;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.BattleCollege;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
using LineupInfo = EggLink.DanhengServer.Database.Lineup.LineupInfo;
namespace EggLink.DanhengServer.GameServer.Game.Battle;
public class BattleInstance(PlayerInstance player, LineupInfo lineup, List<StageConfigExcel> stages)
: BasePlayerManager(player)
{
public BattleInstance(PlayerInstance player, LineupInfo lineup, List<EntityMonster> monsters) : this(player, lineup,
new List<StageConfigExcel>())
{
if (player.ActivityManager!.TrialActivityInstance != null &&
player.ActivityManager!.TrialActivityInstance.Data.CurTrialStageId != 0)
{
var instance = player.ActivityManager!.TrialActivityInstance;
GameData.StageConfigData.TryGetValue(instance.Data.CurTrialStageId, out var stage);
if (stage != null) Stages.Add(stage);
StageId = Stages[0].StageID;
}
else
{
foreach (var id in monsters.Select(monster => monster.GetStageId()))
{
GameData.PlaneEventData.TryGetValue(id * 10 + player.Data.WorldLevel, out var planeEvent);
if (planeEvent == null) continue;
GameData.StageConfigData.TryGetValue(planeEvent.StageID, out var stage);
if (stage != null) Stages.Add(stage);
}
EntityMonsters = monsters;
StageId = Stages[0].StageID;
}
}
public int BattleId { get; set; } = ++player.NextBattleId;
public int StaminaCost { get; set; }
public int WorldLevel { get; set; }
public int CocoonWave { get; set; }
public int MappingInfoId { get; set; }
public int RoundLimit { get; set; }
public int StageId { get; set; } = stages.Count > 0 ? stages[0].StageID : 0; // Set to 0 when hit monster
public int EventId { get; set; }
public int CustomLevel { get; set; }
public BattleEndStatus BattleEndStatus { get; set; }
public List<ItemData> MonsterDropItems { get; set; } = [];
public List<StageConfigExcel> Stages { get; set; } = stages;
public LineupInfo Lineup { get; set; } = lineup;
public List<EntityMonster> EntityMonsters { get; set; } = [];
public List<AvatarSceneInfo> AvatarInfo { get; set; } = [];
public List<MazeBuff> Buffs { get; set; } = [];
public BattleRogueMagicInfo? MagicInfo { get; set; }
public Dictionary<int, BattleEventInstance> BattleEvents { get; set; } = [];
public Dictionary<int, BattleTargetList> BattleTargets { get; set; } = [];
public BattleCollegeConfigExcel? CollegeConfigExcel { get; set; }
public PVEBattleResultCsReq? BattleResult { get; set; }
public BattleGridFightOptions? GridFightOptions { get; set; }
public bool IsTournRogue { get; set; }
public delegate ValueTask OnBattleEndDelegate(BattleInstance battle, PVEBattleResultCsReq req);
public event OnBattleEndDelegate? OnBattleEnd;
public async ValueTask TriggerOnBattleEnd()
{
if (OnBattleEnd != null)
await OnBattleEnd(this, BattleResult!);
}
public ItemList GetDropItemList()
{
if (BattleEndStatus != BattleEndStatus.BattleEndWin) return new ItemList();
var list = new ItemList();
foreach (var item in MonsterDropItems) list.ItemList_.Add(item.ToProto());
var t = System.Threading.Tasks.Task.Run(async () =>
{
foreach (var item in await Player.InventoryManager!.HandleMappingInfo(MappingInfoId, WorldLevel))
list.ItemList_.Add(item.ToProto());
});
t.Wait();
if (CollegeConfigExcel == null ||
Player.BattleCollegeData?.FinishedCollegeIdList.Contains(CollegeConfigExcel.ID) != false)
return list; // if college excel is not null and college is not finished
// finish it
Player.BattleCollegeData.FinishedCollegeIdList.Add(CollegeConfigExcel.ID);
var t2 = System.Threading.Tasks.Task.Run(async () =>
{
await Player.SendPacket(new PacketBattleCollegeDataChangeScNotify(Player));
foreach (var item in await Player.InventoryManager!.HandleReward(CollegeConfigExcel.RewardID))
list.ItemList_.Add(item.ToProto());
});
t2.Wait();
return list;
}
public void AddBattleTarget(int key, int targetId, int progress, int totalProgress = 0)
{
if (!BattleTargets.TryGetValue(key, out var value))
{
value = new BattleTargetList();
BattleTargets.Add(key, value);
}
var battleTarget = new BattleTarget
{
Id = (uint)targetId,
Progress = (uint)progress,
TotalProgress = (uint)totalProgress
};
value.BattleTargetList_.Add(battleTarget);
}
public List<AvatarLineupData> GetBattleAvatars()
{
var excel = GameData.StageConfigData[StageId];
List<int> list = [.. excel.TrialAvatarList];
// if college excel is not null
if (CollegeConfigExcel is { TrialAvatarList.Count: > 0 }) list = [.. CollegeConfigExcel.TrialAvatarList];
if (list.Count > 0)
{
List<int> tempList = [.. list];
if (Player.Data.CurrentGender == Gender.Man)
foreach (var avatar in tempList.Where(avatar =>
GameData.SpecialAvatarData.TryGetValue(avatar * 10 + 0, out var specialAvatarExcel) &&
specialAvatarExcel.AvatarID is 8002 or 8004 or 8006))
list.Remove(avatar);
else
foreach (var avatar in tempList.Where(avatar =>
GameData.SpecialAvatarData.TryGetValue(avatar * 10 + 0, out var specialAvatarExcel) &&
specialAvatarExcel.AvatarID is 8001 or 8003 or 8005))
list.Remove(avatar);
}
if (list.Count > 0) // if list is not empty
{
List<AvatarLineupData> avatars = [];
foreach (var avatar in list)
{
var specialAvatar = Player.AvatarManager!.GetTrialAvatar(avatar);
if (specialAvatar != null)
{
specialAvatar.CheckLevel(Player.Data.WorldLevel);
avatars.Add(new AvatarLineupData(specialAvatar, AvatarType.AvatarTrialType));
}
else
{
var avatarInfo = Player.AvatarManager!.GetFormalAvatar(avatar);
if (avatarInfo != null) avatars.Add(new AvatarLineupData(avatarInfo, AvatarType.AvatarFormalType));
}
}
return avatars;
}
else
{
List<AvatarLineupData> avatars = [];
foreach (var avatar in Lineup.BaseAvatars!) // if list is empty, use scene lineup
{
BaseAvatarInfo? avatarInstance = null;
var avatarType = AvatarType.AvatarFormalType;
if (avatar.AssistUid != 0)
{
var player = DatabaseHelper.Instance!.GetInstance<AvatarData>(avatar.AssistUid);
if (player != null)
{
avatarInstance = player.FormalAvatars.Find(item => item.BaseAvatarId == avatar.BaseAvatarId);
avatarType = AvatarType.AvatarAssistType;
}
}
else if (avatar.SpecialAvatarId != 0)
{
var specialAvatar = Player.AvatarManager!.GetTrialAvatar(avatar.SpecialAvatarId);
if (specialAvatar != null)
{
specialAvatar.CheckLevel(Player.Data.WorldLevel);
avatarInstance = specialAvatar;
avatarType = AvatarType.AvatarTrialType;
}
}
else
{
avatarInstance = Player.AvatarManager!.GetFormalAvatar(avatar.BaseAvatarId);
}
if (avatarInstance == null) continue;
avatars.Add(new AvatarLineupData(avatarInstance, avatarType));
}
return avatars;
}
}
public SceneBattleInfo ToProto()
{
var proto = new SceneBattleInfo
{
BattleId = (uint)BattleId,
WorldLevel = (uint)WorldLevel,
RoundsLimit = (uint)RoundLimit,
StageId = (uint)StageId,
LogicRandomSeed = (uint)Random.Shared.Next()
};
if (GridFightOptions != null)
{
GridFightOptions.HandleProto(proto, this); // grid fight will handle the proto itself
}
else
{
if (MagicInfo != null) proto.BattleRogueMagicInfo = MagicInfo;
foreach (var protoWave in Stages.Select(wave => wave.ToProto()))
{
if (CustomLevel > 0)
foreach (var item in protoWave)
item.MonsterParam.Level = (uint)CustomLevel;
proto.MonsterWaveList.AddRange(protoWave);
}
if (Player.BattleManager!.NextBattleMonsterIds.Count > 0)
{
var ids = Player.BattleManager!.NextBattleMonsterIds;
// split every 5
for (var i = 0; i < (ids.Count - 1) / 5 + 1; i++)
{
var count = Math.Min(5, ids.Count - i * 5);
var waveIds = ids.GetRange(i * 5, count);
proto.MonsterWaveList.Add(new SceneMonsterWave
{
BattleStageId = (uint)(Stages.FirstOrDefault()?.StageID ?? 0),
BattleWaveId = (uint)(proto.MonsterWaveList.Count + 1),
MonsterParam = new SceneMonsterWaveParam(),
MonsterList =
{
waveIds.Select(x => new SceneMonster
{
MonsterId = (uint)x
})
}
});
}
}
var avatars = GetBattleAvatars();
foreach (var avatar in avatars)
proto.BattleAvatarList.Add(avatar.AvatarInfo.ToBattleProto(
new PlayerDataCollection(Player.Data, Player.InventoryManager!.Data, Lineup), avatar.AvatarType));
System.Threading.Tasks.Task.Run(async () =>
{
foreach (var monster in EntityMonsters) await monster.ApplyBuff(this);
foreach (var avatar in AvatarInfo)
if (avatars.Select(x => x.AvatarInfo).FirstOrDefault(x =>
x.BaseAvatarId == avatar.AvatarInfo.BaseAvatarId) !=
null) // if avatar is in lineup
await avatar.ApplyBuff(this);
}).Wait();
foreach (var buff in Buffs.Clone())
if (Enum.IsDefined(typeof(DamageTypeEnum), buff.BuffID))
Buffs.RemoveAll(x => x.BuffID == buff.BuffID && x.DynamicValues.Count == 0);
}
foreach (var eventInstance in BattleEvents.Values) proto.BattleEvent.Add(eventInstance.ToProto());
for (var i = 1; i <= 5; i++)
{
var battleTargetEntry = new BattleTargetList();
if (BattleTargets.TryGetValue(i, out var battleTargetList))
battleTargetEntry.BattleTargetList_.AddRange(battleTargetList.BattleTargetList_);
proto.BattleTargetInfo.Add((uint)i, battleTargetEntry);
}
// global buff
foreach (var buff in GameData.AvatarGlobalBuffConfigData.Values)
if (Player.AvatarManager!.GetFormalAvatar(buff.AvatarID) != null)
// add buff
Buffs.Add(new MazeBuff(buff.MazeBuffID, 1, -1)
{
WaveFlag = -1
});
foreach (var buff in Buffs)
{
if (buff.WaveFlag != null) continue;
var buffs = Buffs.FindAll(x => x.BuffID == buff.BuffID);
if (buffs.Count < 2) continue;
var count = 0;
foreach (var mazeBuff in buffs)
{
mazeBuff.WaveFlag = (int)Math.Pow(2, count);
count++;
}
}
if (IsTournRogue)
proto.AJGPJGLPMIO = new();
proto.BuffList.AddRange(Buffs.Select(buff => buff.ToProto(this)));
return proto;
}
}