From c8e0d84f6a5425a8c8c5a9523bada405d5cae1a4 Mon Sep 17 00:00:00 2001 From: StopWuyu Date: Sat, 23 Aug 2025 14:24:52 +0800 Subject: [PATCH] feat: friend development & battle record --- Common/Data/Excel/ChallengeConfigExcel.cs | 1 + Common/Database/Friend/FriendRecordData.cs | 273 ++++++++++++++++++ Common/Database/Player/PlayerData.cs | 4 +- Common/Util/GameConstants.cs | 2 + GameServer/Game/Avatar/AvatarManager.cs | 12 + GameServer/Game/Challenge/ChallengeManager.cs | 199 ++++++++++++- .../Instances/ChallengeBossInstance.cs | 14 +- .../Instances/ChallengeMemoryInstance.cs | 13 +- .../Instances/ChallengePeakInstance.cs | 10 +- .../Instances/ChallengeStoryInstance.cs | 13 +- .../ChallengePeak/ChallengePeakManager.cs | 6 +- GameServer/Game/Inventory/InventoryManager.cs | 15 +- GameServer/Game/Lineup/LineupManager.cs | 22 ++ GameServer/Game/Player/PlayerInstance.cs | 12 + ...HandlerGetChallengeGroupStatisticsCsReq.cs | 18 ++ ...HandlerGetFriendBattleRecordDetailCsReq.cs | 32 ++ .../HandlerGetFriendDevelopmentInfoCsReq.cs | 27 ++ .../PacketGetChallengeGroupStatisticsScRsp.cs | 37 +++ .../PacketGetFriendBattleRecordDetailScRsp.cs | 105 +++++++ .../PacketGetFriendDevelopmentInfoScRsp.cs | 39 +++ 20 files changed, 841 insertions(+), 13 deletions(-) create mode 100644 Common/Database/Friend/FriendRecordData.cs create mode 100644 GameServer/Server/Packet/Recv/Challenge/HandlerGetChallengeGroupStatisticsCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Friend/HandlerGetFriendBattleRecordDetailCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Friend/HandlerGetFriendDevelopmentInfoCsReq.cs create mode 100644 GameServer/Server/Packet/Send/Challenge/PacketGetChallengeGroupStatisticsScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Friend/PacketGetFriendBattleRecordDetailScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Friend/PacketGetFriendDevelopmentInfoScRsp.cs diff --git a/Common/Data/Excel/ChallengeConfigExcel.cs b/Common/Data/Excel/ChallengeConfigExcel.cs index a3caef95..a51dd67b 100644 --- a/Common/Data/Excel/ChallengeConfigExcel.cs +++ b/Common/Data/Excel/ChallengeConfigExcel.cs @@ -19,6 +19,7 @@ public class ChallengeConfigExcel : ExcelResource public int StageNum { get; set; } public int ChallengeCountDown { get; set; } public int MazeBuffID { get; set; } + public uint Floor { get; set; } public List? ChallengeTargetID { get; set; } = []; diff --git a/Common/Database/Friend/FriendRecordData.cs b/Common/Database/Friend/FriendRecordData.cs new file mode 100644 index 00000000..3f28a3da --- /dev/null +++ b/Common/Database/Friend/FriendRecordData.cs @@ -0,0 +1,273 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using SqlSugar; + +namespace EggLink.DanhengServer.Database.Friend; + +[SugarTable("friend_record_data")] +public class FriendRecordData : BaseDatabaseDataHelper +{ + [SugarColumn(IsJson = true)] public List DevelopmentInfos { get; set; } = []; // max 20 entries + [SugarColumn(IsJson = true)] public Dictionary ChallengeGroupStatistics { get; set; } = []; // cur group statistics + public uint NextRecordId { get; set; } + + public void AddAndRemoveOld(FriendDevelopmentInfoPb info) + { + // get same type + var same = DevelopmentInfos.Where(x => x.DevelopmentType == info.DevelopmentType); + + // if param equal remove + foreach (var infoPb in same.ToArray()) + { + // ReSharper disable once UsageOfDefaultStructEquality + if (infoPb.Params.SequenceEqual(info.Params)) + { + // remove + DevelopmentInfos.Remove(infoPb); + } + } + + DevelopmentInfos.Add(info); + } +} + +public class FriendDevelopmentInfoPb +{ + public DevelopmentType DevelopmentType { get; set; } + public long Time { get; set; } = Extensions.GetUnixSec(); + public Dictionary Params { get; set; } = []; + + public FriendDevelopmentInfo ToProto() + { + var proto = new FriendDevelopmentInfo + { + Time = Time, + DevelopmentType = DevelopmentType + }; + + switch (DevelopmentType) + { + case DevelopmentType.DevelopmentNone: + case DevelopmentType.DevelopmentActivityStart: + case DevelopmentType.DevelopmentActivityEnd: + case DevelopmentType.DevelopmentRogueMagic: + break; + case DevelopmentType.DevelopmentRogueCosmos: + case DevelopmentType.DevelopmentRogueChessNous: + case DevelopmentType.DevelopmentRogueChess: + proto.RogueDevelopmentInfo = new FriendRogueDevelopmentInfo + { + AreaId = Params.GetValueOrDefault("AreaId", 0u) + }; + break; + case DevelopmentType.DevelopmentMemoryChallenge: + case DevelopmentType.DevelopmentStoryChallenge: + case DevelopmentType.DevelopmentBossChallenge: + proto.ChallengeDevelopmentInfo = new FriendChallengeDevelopmentInfo + { + ChallengeId = Params.GetValueOrDefault("ChallengeId", 0u) + }; + break; + case DevelopmentType.DevelopmentUnlockAvatar: + proto.AvatarId = Params.GetValueOrDefault("AvatarId", 0u); + break; + case DevelopmentType.DevelopmentUnlockEquipment: + proto.EquipmentTid = Params.GetValueOrDefault("EquipmentTid", 0u); + break; + case DevelopmentType.DevelopmentRogueTourn: + case DevelopmentType.DevelopmentRogueTournWeek: + case DevelopmentType.DevelopmentRogueTournDivision: + proto.RogueTournDevelopmentInfo = new FriendRogueTournDevelopmentInfo + { + AreaId = Params.GetValueOrDefault("AreaId", 0u), + FinishTournDifficulty = Params.GetValueOrDefault("FinishTournDifficulty", 0u) + }; + break; + case DevelopmentType.DevelopmentChallengePeak: + proto.ChallengePeakDevelopmentInfo = new FriendChallengePeakDevelopmentInfo + { + PeakLevelId = Params.GetValueOrDefault("PeakLevelId", 0u) + }; + break; + } + + return proto; + } +} + +public class ChallengeGroupStatisticsPb +{ + public uint GroupId { get; set; } + public Dictionary? MemoryGroupStatistics { get; set; } + public Dictionary? StoryGroupStatistics { get; set; } + public Dictionary? BossGroupStatistics { get; set; } + + public ChallengeGroupStatistics ToProto() + { + var proto = new ChallengeGroupStatistics + { + GroupId = GroupId + }; + + if (MemoryGroupStatistics != null) + { + foreach (var memoryGroupStatistic in MemoryGroupStatistics.Values) + { + proto.GroupTotalStars += memoryGroupStatistic.Stars; + } + + var maxFloor = MemoryGroupStatistics.Values.MaxBy(x => x.Level); + if (maxFloor != null) + { + proto.MemoryGroup = maxFloor.ToProto(); + } + } + + if (StoryGroupStatistics != null) + { + foreach (var storyGroupStatistic in StoryGroupStatistics.Values) + { + proto.GroupTotalStars += storyGroupStatistic.Stars; + } + + var maxFloor = StoryGroupStatistics.Values.MaxBy(x => x.Level); + if (maxFloor != null) + { + proto.StoryGroup = maxFloor.ToProto(); + } + } + + if (BossGroupStatistics != null) + { + foreach (var bossGroupStatistic in BossGroupStatistics.Values) + { + proto.GroupTotalStars += bossGroupStatistic.Stars; + } + + var maxFloor = BossGroupStatistics.Values.MaxBy(x => x.Level); + if (maxFloor != null) + { + proto.BossGroup = maxFloor.ToProto(); + } + } + + return proto; + } +} + +public class MemoryGroupStatisticsPb +{ + public uint RecordId { get; set; } + public uint Level { get; set; } + public uint RoundCount { get; set; } + public uint Stars { get; set; } + public List> Lineups { get; set; } = []; + + public MemoryGroupStatistics ToProto() + { + return new MemoryGroupStatistics + { + RecordId = RecordId, + SttInfo = new MemoryStatisticsInfo + { + CurLevelStars = Stars, + Level = Level, + RoundCount = RoundCount, + LineupList = + { + Lineups.Select(x => new ChallengeLineupList + { + AvatarList = { x.Select(avatar => avatar.ToProto()) } + }) + } + } + }; + } +} + +public class StoryGroupStatisticsPb +{ + public uint RecordId { get; set; } + public uint Level { get; set; } + public uint Score { get; set; } + public uint BuffOne { get; set; } + public uint BuffTwo { get; set; } + public uint Stars { get; set; } + public List> Lineups { get; set; } = []; + + public StoryGroupStatistics ToProto() + { + return new StoryGroupStatistics + { + RecordId = RecordId, + SttInfo = new StoryStatisticsInfo + { + CurLevelStars = Stars, + Level = Level, + LineupList = + { + Lineups.Select(x => new ChallengeLineupList + { + AvatarList = { x.Select(avatar => avatar.ToProto()) } + }) + }, + BuffOne = BuffOne, + BuffTwo = BuffTwo, + ScoreId = Score + } + }; + } +} + +public class BossGroupStatisticsPb +{ + public uint RecordId { get; set; } + public uint Level { get; set; } + public uint Score { get; set; } + public uint BuffOne { get; set; } + public uint BuffTwo { get; set; } + public uint Stars { get; set; } + public List> Lineups { get; set; } = []; + + public BossGroupStatistics ToProto() + { + return new BossGroupStatistics + { + RecordId = RecordId, + SttInfo = new BossStatisticsInfo + { + CurLevelStars = Stars, + Level = Level, + LineupList = + { + Lineups.Select(x => new ChallengeLineupList + { + AvatarList = { x.Select(avatar => avatar.ToProto()) } + }) + }, + BuffOne = BuffOne, + BuffTwo = BuffTwo, + ScoreId = Score + } + }; + } +} + +public class ChallengeAvatarInfoPb +{ + public uint Level { get; set; } + public uint Index { get; set; } + public uint Id { get; set; } + public AvatarType AvatarType { get; set; } = AvatarType.AvatarFormalType; + + public ChallengeAvatarInfo ToProto() + { + return new ChallengeAvatarInfo + { + Level = Level, + AvatarType = AvatarType, + Id = Id, + Index = Index + }; + } +} \ No newline at end of file diff --git a/Common/Database/Player/PlayerData.cs b/Common/Database/Player/PlayerData.cs index 001d8f9e..f90bf828 100644 --- a/Common/Database/Player/PlayerData.cs +++ b/Common/Database/Player/PlayerData.cs @@ -188,14 +188,14 @@ public class PlayerData : BaseDatabaseDataHelper var pos = 0; foreach (var avatar in avatarInfo.AssistAvatars.Select(assist => - avatarInfo.FormalAvatars.Find(x => x.AvatarId == assist))) + avatarInfo.FormalAvatars.Find(x => x.BaseAvatarId == assist))) if (avatar != null) info.AssistAvatarList.Add(avatar.ToDetailProto(pos++, new PlayerDataCollection(this, inventoryInfo, new LineupInfo()))); pos = 0; foreach (var avatar in avatarInfo.DisplayAvatars.Select(display => - avatarInfo.FormalAvatars.Find(x => x.AvatarId == display))) + avatarInfo.FormalAvatars.Find(x => x.BaseAvatarId == display))) if (avatar != null) info.DisplayAvatarList.Add(avatar.ToDetailProto(pos++, new PlayerDataCollection(this, inventoryInfo, new LineupInfo()))); diff --git a/Common/Util/GameConstants.cs b/Common/Util/GameConstants.cs index 04daeb8f..319de23e 100644 --- a/Common/Util/GameConstants.cs +++ b/Common/Util/GameConstants.cs @@ -26,6 +26,8 @@ public static class GameConstants public const uint CHALLENGE_PEAK_GOLD_FRAME_ID = 226003; public const uint CHALLENGE_PEAK_ULTRA_FRAME_ID = 226004; + public const uint CHALLENGE_PEAK_CUR_GROUP_ID = 1; + public static readonly List UpgradeWorldLevel = [20, 30, 40, 50, 60, 65]; public static readonly List AllowedChessRogueEntranceId = [8020701, 8020901, 8020401, 8020201]; } \ No newline at end of file diff --git a/GameServer/Game/Avatar/AvatarManager.cs b/GameServer/Game/Avatar/AvatarManager.cs index ce2062a0..115c3859 100644 --- a/GameServer/Game/Avatar/AvatarManager.cs +++ b/GameServer/Game/Avatar/AvatarManager.cs @@ -2,7 +2,9 @@ using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Database.Avatar; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Database.Inventory; +using EggLink.DanhengServer.Enums.Item; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar; using EggLink.DanhengServer.GameServer.Server.Packet.Send.PlayerSync; @@ -47,6 +49,16 @@ public class AvatarManager(PlayerInstance player) : BasePlayerManager(player) AvatarData.FormalAvatars.Add(avatar); + if (avatarExcel.Rarity == RarityEnum.CombatPowerAvatarRarityType5 && avatarExcel.AvatarID <= 3000) + { + // add development + Player.FriendRecordData!.AddAndRemoveOld(new FriendDevelopmentInfoPb + { + DevelopmentType = DevelopmentType.DevelopmentUnlockAvatar, + Params = { { "AvatarId", (uint)avatarExcel.AvatarID } } + }); + } + if (sync) await Player.SendPacket(new PacketPlayerSyncScNotify(avatar)); diff --git a/GameServer/Game/Challenge/ChallengeManager.cs b/GameServer/Game/Challenge/ChallengeManager.cs index acc0381a..04d195ff 100644 --- a/GameServer/Game/Challenge/ChallengeManager.cs +++ b/GameServer/Game/Challenge/ChallengeManager.cs @@ -1,6 +1,7 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Database.Challenge; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Database.Inventory; using EggLink.DanhengServer.GameServer.Game.Challenge.Definitions; using EggLink.DanhengServer.GameServer.Game.Challenge.Instances; @@ -9,6 +10,7 @@ using EggLink.DanhengServer.GameServer.Server.Packet.Send.Challenge; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Proto.ServerSide; using Google.Protobuf; +using System; using static EggLink.DanhengServer.GameServer.Plugin.Event.PluginEvent; namespace EggLink.DanhengServer.GameServer.Game.Challenge; @@ -131,7 +133,7 @@ public class ChallengeManager(PlayerInstance player) : BasePlayerManager(player) ChallengeInstance = instance; // Set first lineup before we enter scenes - await Player.LineupManager!.SetCurLineup(instance.GetCurrentExtraLineupType() + 10); + await Player.LineupManager!.SetExtraLineup((ExtraLineupType)instance.GetCurrentExtraLineupType()); // Enter scene try @@ -299,6 +301,201 @@ public class ChallengeManager(PlayerInstance player) : BasePlayerManager(player) } } + public void SaveBattleRecord(BaseLegacyChallengeInstance inst) + { + switch (inst) + { + case ChallengeMemoryInstance memory: + { + Player.FriendRecordData!.ChallengeGroupStatistics.TryAdd((uint)memory.Config.GroupID, + new ChallengeGroupStatisticsPb + { + GroupId = (uint)memory.Config.GroupID + }); + var stats = Player.FriendRecordData.ChallengeGroupStatistics[(uint)memory.Config.GroupID]; + + stats.MemoryGroupStatistics ??= []; + + var starCount = 0u; + for (var i = 0; i < 3; i++) starCount += (memory.Data.Memory.Stars & (1 << i)) != 0 ? 1u : 0u; + + if (stats.MemoryGroupStatistics.GetValueOrDefault((uint)memory.Config.ID)?.Stars > + starCount) return; // dont save if we have more stars already + + + var pb = new MemoryGroupStatisticsPb + { + RoundCount = (uint)(memory.Config.ChallengeCountDown - memory.Data.Memory.RoundsLeft), + Stars = starCount, + RecordId = Player.FriendRecordData!.NextRecordId++, + Level = memory.Config.Floor + }; + + List lineupTypes = + [ + ExtraLineupType.LineupChallenge + ]; + + if (memory.Config.StageNum >= 2) + lineupTypes.Add(ExtraLineupType.LineupChallenge2); + + foreach (var type in lineupTypes) + { + var lineup = Player.LineupManager!.GetExtraLineup(type); + if (lineup == null) continue; + + var index = 0u; + var lineupPb = new List(); + + foreach (var avatar in lineup.BaseAvatars ?? []) + { + var formalAvatar = Player.AvatarManager!.GetFormalAvatar(avatar.BaseAvatarId); + if (formalAvatar == null) continue; + + lineupPb.Add(new ChallengeAvatarInfoPb + { + Index = index++, + Id = (uint)formalAvatar.BaseAvatarId, + AvatarType = AvatarType.AvatarFormalType, + Level = (uint)formalAvatar.Level + }); + } + + pb.Lineups.Add(lineupPb); + } + + stats.MemoryGroupStatistics[(uint)memory.Config.ID] = pb; + break; + } + case ChallengeStoryInstance story: + { + Player.FriendRecordData!.ChallengeGroupStatistics.TryAdd((uint)story.Config.GroupID, + new ChallengeGroupStatisticsPb + { + GroupId = (uint)story.Config.GroupID + }); + var stats = Player.FriendRecordData.ChallengeGroupStatistics[(uint)story.Config.GroupID]; + + stats.StoryGroupStatistics ??= []; + + var starCount = 0u; + for (var i = 0; i < 3; i++) starCount += (story.Data.Story.Stars & (1 << i)) != 0 ? 1u : 0u; + + if (stats.StoryGroupStatistics.GetValueOrDefault((uint)story.Config.ID)?.Stars > + starCount) return; // dont save if we have more stars already + + var pb = new StoryGroupStatisticsPb + { + Stars = starCount, + RecordId = Player.FriendRecordData!.NextRecordId++, + Level = story.Config.Floor, + BuffOne = story.Data.Story.Buffs.Count > 0 ? story.Data.Story.Buffs[0] : 0, + BuffTwo = story.Data.Story.Buffs.Count > 1 ? story.Data.Story.Buffs[1] : 0, + Score = (uint)story.GetTotalScore() + }; + + List lineupTypes = + [ + ExtraLineupType.LineupChallenge + ]; + + if (story.Config.StageNum >= 2) + lineupTypes.Add(ExtraLineupType.LineupChallenge2); + + foreach (var type in lineupTypes) + { + var lineup = Player.LineupManager!.GetExtraLineup(type); + if (lineup == null) continue; + + var index = 0u; + var lineupPb = new List(); + + foreach (var avatar in lineup.BaseAvatars ?? []) + { + var formalAvatar = Player.AvatarManager!.GetFormalAvatar(avatar.BaseAvatarId); + if (formalAvatar == null) continue; + + lineupPb.Add(new ChallengeAvatarInfoPb + { + Index = index++, + Id = (uint)formalAvatar.BaseAvatarId, + AvatarType = AvatarType.AvatarFormalType, + Level = (uint)formalAvatar.Level + }); + } + + pb.Lineups.Add(lineupPb); + } + + stats.StoryGroupStatistics[(uint)story.Config.ID] = pb; + break; + } + case ChallengeBossInstance boss: + { + Player.FriendRecordData!.ChallengeGroupStatistics.TryAdd((uint)boss.Config.GroupID, + new ChallengeGroupStatisticsPb + { + GroupId = (uint)boss.Config.GroupID + }); + var stats = Player.FriendRecordData.ChallengeGroupStatistics[(uint)boss.Config.GroupID]; + + stats.BossGroupStatistics ??= []; + + var starCount = 0u; + for (var i = 0; i < 3; i++) starCount += (boss.Data.Boss.Stars & (1 << i)) != 0 ? 1u : 0u; + + if (stats.BossGroupStatistics.GetValueOrDefault((uint)boss.Config.ID)?.Stars > + starCount) return; // dont save if we have more stars already + + var pb = new BossGroupStatisticsPb + { + Stars = starCount, + RecordId = Player.FriendRecordData!.NextRecordId++, + Level = boss.Config.Floor, + BuffOne = boss.Data.Boss.Buffs.Count > 0 ? boss.Data.Boss.Buffs[0] : 0, + BuffTwo = boss.Data.Boss.Buffs.Count > 1 ? boss.Data.Boss.Buffs[1] : 0, + Score = (uint)boss.GetTotalScore() + }; + + List lineupTypes = + [ + ExtraLineupType.LineupChallenge + ]; + + if (boss.Config.StageNum >= 2) + lineupTypes.Add(ExtraLineupType.LineupChallenge2); + + foreach (var type in lineupTypes) + { + var lineup = Player.LineupManager!.GetExtraLineup(type); + if (lineup == null) continue; + + var index = 0u; + var lineupPb = new List(); + + foreach (var avatar in lineup.BaseAvatars ?? []) + { + var formalAvatar = Player.AvatarManager!.GetFormalAvatar(avatar.BaseAvatarId); + if (formalAvatar == null) continue; + + lineupPb.Add(new ChallengeAvatarInfoPb + { + Index = index++, + Id = (uint)formalAvatar.BaseAvatarId, + AvatarType = AvatarType.AvatarFormalType, + Level = (uint)formalAvatar.Level + }); + } + + pb.Lineups.Add(lineupPb); + } + + stats.BossGroupStatistics[(uint)boss.Config.ID] = pb; + break; + } + } + } + #endregion } diff --git a/GameServer/Game/Challenge/Instances/ChallengeBossInstance.cs b/GameServer/Game/Challenge/Instances/ChallengeBossInstance.cs index c1122991..782cf71c 100644 --- a/GameServer/Game/Challenge/Instances/ChallengeBossInstance.cs +++ b/GameServer/Game/Challenge/Instances/ChallengeBossInstance.cs @@ -1,5 +1,6 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Enums.Item; using EggLink.DanhengServer.Enums.Mission; using EggLink.DanhengServer.GameServer.Game.Battle; @@ -11,6 +12,7 @@ using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Proto.ServerSide; using EggLink.DanhengServer.Util; +using SqlSugar; namespace EggLink.DanhengServer.GameServer.Game.Challenge.Instances; @@ -282,6 +284,16 @@ public class ChallengeBossInstance(PlayerInstance player, ChallengeDataPb data) // Call MissionManager await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.ChallengeFinish, this); + + // save + Player.ChallengeManager.SaveBattleRecord(this); + + // add development + Player.FriendRecordData!.AddAndRemoveOld(new FriendDevelopmentInfoPb + { + DevelopmentType = DevelopmentType.DevelopmentBossChallenge, + Params = {{ "ChallengeId", (uint)Config.ID } } + }); } else { @@ -301,7 +313,7 @@ public class ChallengeBossInstance(PlayerInstance player, ChallengeDataPb data) // Change player line up SetCurrentExtraLineup(ExtraLineupType.LineupChallenge2); - await Player.LineupManager!.SetCurLineup(GetCurrentExtraLineupType() + 10); + await Player.LineupManager!.SetExtraLineup((ExtraLineupType)GetCurrentExtraLineupType()); await Player.SendPacket(new PacketChallengeLineupNotify((ExtraLineupType)GetCurrentExtraLineupType())); Data.Boss.SavedMp = (uint)Player.LineupManager.GetCurLineup()!.Mp; diff --git a/GameServer/Game/Challenge/Instances/ChallengeMemoryInstance.cs b/GameServer/Game/Challenge/Instances/ChallengeMemoryInstance.cs index a7a3b4f6..84a1e388 100644 --- a/GameServer/Game/Challenge/Instances/ChallengeMemoryInstance.cs +++ b/GameServer/Game/Challenge/Instances/ChallengeMemoryInstance.cs @@ -1,5 +1,6 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Enums.Mission; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.Challenge.Definitions; @@ -171,6 +172,16 @@ public class ChallengeMemoryInstance(PlayerInstance player, ChallengeDataPb data // Call MissionManager await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.ChallengeFinish, this); + + // save + Player.ChallengeManager.SaveBattleRecord(this); + + // add development + Player.FriendRecordData!.AddAndRemoveOld(new FriendDevelopmentInfoPb + { + DevelopmentType = DevelopmentType.DevelopmentMemoryChallenge, + Params = { { "ChallengeId", (uint)Config.ID } } + }); } else { @@ -184,7 +195,7 @@ public class ChallengeMemoryInstance(PlayerInstance player, ChallengeDataPb data // Change player line up SetCurrentExtraLineup(ExtraLineupType.LineupChallenge2); - await Player.LineupManager!.SetCurLineup((int)(Data.Memory.CurrentExtraLineup + 10)); + await Player.LineupManager!.SetExtraLineup((ExtraLineupType)GetCurrentExtraLineupType()); await Player.SendPacket(new PacketChallengeLineupNotify((ExtraLineupType)Data.Memory.CurrentExtraLineup)); Data.Memory.SavedMp = (uint)Player.LineupManager.GetCurLineup()!.Mp; diff --git a/GameServer/Game/Challenge/Instances/ChallengePeakInstance.cs b/GameServer/Game/Challenge/Instances/ChallengePeakInstance.cs index 232651cd..96a54145 100644 --- a/GameServer/Game/Challenge/Instances/ChallengePeakInstance.cs +++ b/GameServer/Game/Challenge/Instances/ChallengePeakInstance.cs @@ -1,5 +1,6 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Enums.Mission; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.Challenge.Definitions; @@ -102,9 +103,16 @@ public class ChallengePeakInstance(PlayerInstance player, ChallengeDataPb data) await Player.SendPacket(new PacketChallengePeakSettleScNotify(this, res.Item2)); // Call MissionManager - await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.ChallengeFinish, this); + await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.ChallengePeakBattleFinish, this); await Player.ChallengePeakManager!.SaveHistory(this, res.Item2); + + // add development + Player.FriendRecordData!.AddAndRemoveOld(new FriendDevelopmentInfoPb + { + DevelopmentType = DevelopmentType.DevelopmentChallengePeak, + Params = { { "PeakLevelId", (uint)Config.ID } } + }); } // Set saved technique points (This will be restored if the player resets the challenge) diff --git a/GameServer/Game/Challenge/Instances/ChallengeStoryInstance.cs b/GameServer/Game/Challenge/Instances/ChallengeStoryInstance.cs index 1f76fa82..98403381 100644 --- a/GameServer/Game/Challenge/Instances/ChallengeStoryInstance.cs +++ b/GameServer/Game/Challenge/Instances/ChallengeStoryInstance.cs @@ -1,5 +1,6 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Enums.Mission; using EggLink.DanhengServer.GameServer.Game.Battle; using EggLink.DanhengServer.GameServer.Game.Challenge.Definitions; @@ -211,6 +212,16 @@ public class ChallengeStoryInstance(PlayerInstance player, ChallengeDataPb data) // Call MissionManager await Player.MissionManager!.HandleFinishType(MissionFinishTypeEnum.ChallengeFinish, this); + + // save + Player.ChallengeManager.SaveBattleRecord(this); + + // add development + Player.FriendRecordData!.AddAndRemoveOld(new FriendDevelopmentInfoPb + { + DevelopmentType = DevelopmentType.DevelopmentStoryChallenge, + Params = { { "ChallengeId", (uint)Config.ID } } + }); } else { @@ -224,7 +235,7 @@ public class ChallengeStoryInstance(PlayerInstance player, ChallengeDataPb data) // Change player line up SetCurrentExtraLineup(ExtraLineupType.LineupChallenge2); - await Player.LineupManager!.SetCurLineup((int)(Data.Story.CurrentExtraLineup + 10)); + await Player.LineupManager!.SetExtraLineup((ExtraLineupType)GetCurrentExtraLineupType()); await Player.SendPacket(new PacketChallengeLineupNotify((ExtraLineupType)Data.Story.CurrentExtraLineup)); Data.Story.SavedMp = (uint)Player.LineupManager.GetCurLineup()!.Mp; diff --git a/GameServer/Game/ChallengePeak/ChallengePeakManager.cs b/GameServer/Game/ChallengePeak/ChallengePeakManager.cs index fc6bfa99..31d92b29 100644 --- a/GameServer/Game/ChallengePeak/ChallengePeakManager.cs +++ b/GameServer/Game/ChallengePeak/ChallengePeakManager.cs @@ -1,10 +1,8 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Database.Challenge; using EggLink.DanhengServer.Database.Lineup; -using EggLink.DanhengServer.GameServer.Game.Challenge.Definitions; using EggLink.DanhengServer.GameServer.Game.Challenge.Instances; using EggLink.DanhengServer.GameServer.Game.Player; -using EggLink.DanhengServer.GameServer.Server.Packet.Send.Challenge; using EggLink.DanhengServer.GameServer.Server.Packet.Send.ChallengePeak; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Proto.ServerSide; @@ -85,7 +83,7 @@ public class ChallengePeakManager(PlayerInstance player) : BasePlayerManager(pla HashSet targetIds = []; HashSet avatarIds = []; - if (Player.ChallengeManager!.ChallengeData.PeakBossLevelDatas.TryGetValue(bossLevelId << 2 & 0, out var bossPbData)) // easy (is hard = 0) + if (Player.ChallengeManager!.ChallengeData.PeakBossLevelDatas.TryGetValue(bossLevelId << 2 | 0, out var bossPbData)) // easy (is hard = 0) { bossProto.PeakEasyBoss.PeakLevelAvatarIdList.AddRange(bossPbData.BaseAvatarList); bossProto.PeakEasyBoss.BossDisplayAvatarIdList.AddRange(bossPbData.BaseAvatarList); @@ -316,7 +314,7 @@ public class ChallengePeakManager(PlayerInstance player) : BasePlayerManager(pla Player.ChallengeManager!.ChallengeInstance = instance; // Set first lineup before we enter scenes - await Player.LineupManager!.SetCurLineup((int)instance.Data.Peak.CurrentExtraLineup + 10); + await Player.LineupManager!.SetExtraLineup((ExtraLineupType)instance.Data.Peak.CurrentExtraLineup); // Enter scene try diff --git a/GameServer/Game/Inventory/InventoryManager.cs b/GameServer/Game/Inventory/InventoryManager.cs index 45f545b8..250aafb4 100644 --- a/GameServer/Game/Inventory/InventoryManager.cs +++ b/GameServer/Game/Inventory/InventoryManager.cs @@ -1,6 +1,6 @@ -using System.Collections.Frozen; -using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Database.Inventory; using EggLink.DanhengServer.Enums.Item; using EggLink.DanhengServer.Enums.Mission; @@ -14,6 +14,7 @@ using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Util; using Google.Protobuf.Collections; +using System.Collections.Frozen; namespace EggLink.DanhengServer.GameServer.Game.Inventory; @@ -64,6 +65,16 @@ public class InventoryManager(PlayerInstance player) : BasePlayerManager(player) } itemData = await PutItem(itemId, 1, rank, level: level, uniqueId: ++Data.NextUniqueId); + + if (itemConfig.Rarity == ItemRarityEnum.SuperRare) + { + // add development + Player.FriendRecordData!.AddAndRemoveOld(new FriendDevelopmentInfoPb + { + DevelopmentType = DevelopmentType.DevelopmentUnlockEquipment, + Params = { { "EquipmentTid", (uint)itemConfig.ID } } + }); + } break; case ItemMainTypeEnum.Usable: switch (itemConfig.ItemSubType) diff --git a/GameServer/Game/Lineup/LineupManager.cs b/GameServer/Game/Lineup/LineupManager.cs index dd36a03b..17372069 100644 --- a/GameServer/Game/Lineup/LineupManager.cs +++ b/GameServer/Game/Lineup/LineupManager.cs @@ -189,6 +189,28 @@ public class LineupManager : BasePlayerManager LineupData.CurExtraLineup = index; } + public async ValueTask SetExtraLineup(ExtraLineupType type, bool notify = true) + { + if (type == ExtraLineupType.LineupNone) + { + // reset lineup + LineupData.CurExtraLineup = -1; + if (notify) await Player.SendPacket(new PacketSyncLineupNotify(GetCurLineup()!)); + return; + } + + var index = (int)type + 10; + + // get cur extra lineup + var lineup = GetExtraLineup(type); + if (lineup == null || lineup.BaseAvatars?.Count == 0) return; + + LineupData.CurExtraLineup = index; + + // sync + if (notify) await Player.SendPacket(new PacketSyncLineupNotify(GetCurLineup()!)); + } + public async ValueTask AddAvatar(int lineupIndex, int avatarId, bool sendPacket = true) { if (lineupIndex < 0) return; diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs index c857b509..f80c95a1 100644 --- a/GameServer/Game/Player/PlayerInstance.cs +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -1,6 +1,7 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Database.Avatar; +using EggLink.DanhengServer.Database.Friend; using EggLink.DanhengServer.Database.Player; using EggLink.DanhengServer.Database.Scene; using EggLink.DanhengServer.Database.Tutorial; @@ -105,6 +106,7 @@ public partial class PlayerInstance(PlayerData data) public PlayerData Data { get; set; } = data; public PlayerUnlockData? PlayerUnlockData { get; private set; } + public FriendRecordData? FriendRecordData { get; private set; } public SceneData? SceneData { get; private set; } public HeartDialData? HeartDialData { get; private set; } public TutorialData? TutorialData { get; private set; } @@ -198,6 +200,7 @@ public partial class PlayerInstance(PlayerData data) TutorialGuideData = InitializeDatabase(); ServerPrefsData = InitializeDatabase(); BattleCollegeData = InitializeDatabase(); + FriendRecordData = InitializeDatabase(); Components.Add(new SwitchHandComponent(this)); @@ -234,6 +237,15 @@ public partial class PlayerInstance(PlayerData data) if (ConfigManager.Config.ServerOption.EnableMission) await MissionManager!.AcceptMainMissionByCondition(); + foreach (var friendDevelopmentInfoPb in FriendRecordData.DevelopmentInfos.ToArray()) + { + if (Extensions.GetUnixSec() - friendDevelopmentInfoPb.Time >= + TimeSpan.TicksPerDay * 7 / TimeSpan.TicksPerSecond) + { + FriendRecordData.DevelopmentInfos.Remove(friendDevelopmentInfoPb); + } + } + await QuestManager!.AcceptQuestByCondition(); } diff --git a/GameServer/Server/Packet/Recv/Challenge/HandlerGetChallengeGroupStatisticsCsReq.cs b/GameServer/Server/Packet/Recv/Challenge/HandlerGetChallengeGroupStatisticsCsReq.cs new file mode 100644 index 00000000..833c6334 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Challenge/HandlerGetChallengeGroupStatisticsCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Challenge; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Challenge; + +[Opcode(CmdIds.GetChallengeGroupStatisticsCsReq)] +public class HandlerGetChallengeGroupStatisticsCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = GetChallengeGroupStatisticsCsReq.Parser.ParseFrom(data); + + await connection.SendPacket(new PacketGetChallengeGroupStatisticsScRsp(req.GroupId, + connection.Player!.FriendRecordData!.ChallengeGroupStatistics.Values.FirstOrDefault(x => + x.GroupId == req.GroupId))); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Friend/HandlerGetFriendBattleRecordDetailCsReq.cs b/GameServer/Server/Packet/Recv/Friend/HandlerGetFriendBattleRecordDetailCsReq.cs new file mode 100644 index 00000000..a7a8700e --- /dev/null +++ b/GameServer/Server/Packet/Recv/Friend/HandlerGetFriendBattleRecordDetailCsReq.cs @@ -0,0 +1,32 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Avatar; +using EggLink.DanhengServer.Database.Challenge; +using EggLink.DanhengServer.Database.Friend; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Friend; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Friend; + +[Opcode(CmdIds.GetFriendBattleRecordDetailCsReq)] +public class HandlerGetFriendBattleRecordDetailCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = GetFriendBattleRecordDetailCsReq.Parser.ParseFrom(data); + var uid = req.Uid; + + // get data from db + var recordData = DatabaseHelper.Instance!.GetInstance((int)uid); + var challengeData = DatabaseHelper.Instance!.GetInstance((int)uid); + var avatarData = DatabaseHelper.Instance!.GetInstance((int)uid); + + if (recordData == null || challengeData == null || avatarData == null) + { + await connection.SendPacket(new PacketGetFriendBattleRecordDetailScRsp(Retcode.RetFriendPlayerNotFound)); + return; + } + + await connection.SendPacket(new PacketGetFriendBattleRecordDetailScRsp(recordData, challengeData, avatarData)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Friend/HandlerGetFriendDevelopmentInfoCsReq.cs b/GameServer/Server/Packet/Recv/Friend/HandlerGetFriendDevelopmentInfoCsReq.cs new file mode 100644 index 00000000..b95f6992 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Friend/HandlerGetFriendDevelopmentInfoCsReq.cs @@ -0,0 +1,27 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Friend; +using EggLink.DanhengServer.GameServer.Server.Packet.Send.Friend; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Friend; + +[Opcode(CmdIds.GetFriendDevelopmentInfoCsReq)] +public class HandlerGetFriendDevelopmentInfoCsReq : Handler +{ + public override async Task OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = GetFriendDevelopmentInfoCsReq.Parser.ParseFrom(data); + var uid = req.Uid; + + // get data + var recordData = DatabaseHelper.Instance!.GetInstance((int)uid); + if (recordData == null) + { + await connection.SendPacket(new PacketGetFriendDevelopmentInfoScRsp(Retcode.RetFriendPlayerNotFound)); + return; + } + + await connection.SendPacket(new PacketGetFriendDevelopmentInfoScRsp(recordData)); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Challenge/PacketGetChallengeGroupStatisticsScRsp.cs b/GameServer/Server/Packet/Send/Challenge/PacketGetChallengeGroupStatisticsScRsp.cs new file mode 100644 index 00000000..1c999e37 --- /dev/null +++ b/GameServer/Server/Packet/Send/Challenge/PacketGetChallengeGroupStatisticsScRsp.cs @@ -0,0 +1,37 @@ +using EggLink.DanhengServer.Database.Friend; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using System.Linq; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Challenge; + +public class PacketGetChallengeGroupStatisticsScRsp : BasePacket +{ + public PacketGetChallengeGroupStatisticsScRsp(uint groupId, ChallengeGroupStatisticsPb? data) : base(CmdIds.GetChallengeGroupStatisticsScRsp) + { + var proto = new GetChallengeGroupStatisticsScRsp + { + GroupId = groupId + }; + + var maxMemory = data?.MemoryGroupStatistics?.Values.MaxBy(x => x.Level); + if (maxMemory != null) + { + proto.MemoryGroup = maxMemory.ToProto(); + } + + var maxStory = data?.StoryGroupStatistics?.Values.MaxBy(x => x.Level); + if (maxStory != null) + { + proto.StoryGroup = maxStory.ToProto(); + } + + var maxBoss = data?.BossGroupStatistics?.Values.MaxBy(x => x.Level); + if (maxBoss != null) + { + proto.BossGroup = maxBoss.ToProto(); + } + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Friend/PacketGetFriendBattleRecordDetailScRsp.cs b/GameServer/Server/Packet/Send/Friend/PacketGetFriendBattleRecordDetailScRsp.cs new file mode 100644 index 00000000..85725c16 --- /dev/null +++ b/GameServer/Server/Packet/Send/Friend/PacketGetFriendBattleRecordDetailScRsp.cs @@ -0,0 +1,105 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Database.Avatar; +using EggLink.DanhengServer.Database.Challenge; +using EggLink.DanhengServer.Database.Friend; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Friend; + +public class PacketGetFriendBattleRecordDetailScRsp : BasePacket +{ + public PacketGetFriendBattleRecordDetailScRsp(FriendRecordData recordData, ChallengeData challengeData, AvatarData avatarData) : base( + CmdIds.GetFriendBattleRecordDetailScRsp) + { + var proto = new GetFriendBattleRecordDetailScRsp + { + Uid = (uint)recordData.Uid, + ChallengeRecord = { recordData.ChallengeGroupStatistics.Values.Select(x => x.ToProto()) }, + RogueRecord = new RogueStatistics() + }; + + var data = GameData.ChallengePeakGroupConfigData.GetValueOrDefault((int)GameConstants + .CHALLENGE_PEAK_CUR_GROUP_ID); + + if (data != null) + { + var peakRec = new ChallengePeakGroupStatistics + { + GroupId = GameConstants.CHALLENGE_PEAK_CUR_GROUP_ID, + BossLevelStt = new BossLevelStatistics() + }; + + foreach (var preId in data.PreLevelIDList) + { + var stt = new PreLevelStatistics + { + PeakLevelId = (uint)preId, + }; + + var rec = challengeData.PeakLevelDatas.GetValueOrDefault(preId); + if (rec != null) + { + var index = 0u; + stt.Lineup = new ChallengeLineupList + { + AvatarList = + { + rec.BaseAvatarList.Select(x => new ChallengeAvatarInfo + { + Index = index++, + Id = x, + AvatarType = AvatarType.AvatarFormalType, + Level = (uint)(avatarData.FormalAvatars.Find(avatar => avatar.BaseAvatarId == x)?.Level ?? 1) + }) + } + }; + + stt.PeakRoundsCount = rec.RoundCnt; + } + + peakRec.PreLevelSttList.Add(stt); + } + + var bossRec = challengeData.PeakBossLevelDatas.GetValueOrDefault(data.BossLevelID << 2 | 1); + bossRec ??= challengeData.PeakBossLevelDatas.GetValueOrDefault(data.BossLevelID << 2 | 0); + if (bossRec != null) + { + peakRec.BossLevelStt = new BossLevelStatistics + { + PeakLevelId = (uint)bossRec.LevelId, + BuffId = bossRec.BuffId, + Lineup = new ChallengeLineupList + { + AvatarList = + { + bossRec.BaseAvatarList.Select((x, index) => new ChallengeAvatarInfo + { + Index = (uint)index, + Id = x, + AvatarType = AvatarType.AvatarFormalType, + Level = (uint)(avatarData.FormalAvatars.Find(avatar => avatar.BaseAvatarId == x)?.Level ?? 1) + }) + } + }, + LeastRoundsCount = bossRec.RoundCnt + }; + } + + proto.PeakRecord.Add(peakRec); + } + + SetData(proto); + } + + public PacketGetFriendBattleRecordDetailScRsp(Retcode code) : base(CmdIds.GetFriendBattleRecordDetailScRsp) + { + var proto = new GetFriendBattleRecordDetailScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Send/Friend/PacketGetFriendDevelopmentInfoScRsp.cs b/GameServer/Server/Packet/Send/Friend/PacketGetFriendDevelopmentInfoScRsp.cs new file mode 100644 index 00000000..0f2160ac --- /dev/null +++ b/GameServer/Server/Packet/Send/Friend/PacketGetFriendDevelopmentInfoScRsp.cs @@ -0,0 +1,39 @@ +using EggLink.DanhengServer.Database.Friend; +using EggLink.DanhengServer.Kcp; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Friend; + +public class PacketGetFriendDevelopmentInfoScRsp : BasePacket +{ + public PacketGetFriendDevelopmentInfoScRsp(Retcode code) : base(CmdIds.GetFriendDevelopmentInfoScRsp) + { + var proto = new GetFriendDevelopmentInfoScRsp + { + Retcode = (uint)code + }; + + SetData(proto); + } + + public PacketGetFriendDevelopmentInfoScRsp(FriendRecordData data) : base(CmdIds.GetFriendDevelopmentInfoScRsp) + { + foreach (var friendDevelopmentInfoPb in data.DevelopmentInfos.ToArray()) + { + if (Extensions.GetUnixSec() - friendDevelopmentInfoPb.Time >= + TimeSpan.TicksPerDay * 7 / TimeSpan.TicksPerSecond) + { + data.DevelopmentInfos.Remove(friendDevelopmentInfoPb); + } + } + + var proto = new GetFriendDevelopmentInfoScRsp + { + DevelopmentList = { data.DevelopmentInfos.Select(x => x.ToProto()) }, + Uid = (uint)data.Uid + }; + + SetData(proto); + } +} \ No newline at end of file