using System; using System.Collections.Generic; using System.Linq; using System.Text; using WingsAPI.Data.TimeSpace; using WingsEmu.Game.Configurations; using WingsEmu.Game.Entities; using WingsEmu.Game.Maps; using WingsEmu.Game.Networking; using WingsEmu.Game.Portals; using WingsEmu.Game.TimeSpaces.Enums; using WingsEmu.Packets.Enums; namespace WingsEmu.Game.TimeSpaces; public static class TimeSpacePacketExtensions { public static string GenerateRsfn(this IMapInstance mapInstance, bool isStartMap = false, bool isVisit = true) { int number = isStartMap ? 0 : mapInstance.GetAliveMonsters(x => x.SummonerType == null || x.SummonerType != VisualType.Player).Any() && isVisit ? 3 : isVisit ? 0 : 1; return $"rsfn {mapInstance.MapIndexX} {mapInstance.MapIndexY} {number}"; } public static string GenerateMinfo(this IClientSession session) { TimeSpaceInstance timeSpace = session.PlayerEntity.TimeSpaceComponent.TimeSpace.Instance; TimeSpaceObjective timeSpaceObjective = session.PlayerEntity.TimeSpaceComponent.TimeSpace.Instance.TimeSpaceObjective; return "minfo " + $"{(timeSpaceObjective.KillAllMonsters ? 1 : 0)} " + $"{(timeSpaceObjective.GoToExit ? 1 : 0)} " + $"{timeSpaceObjective.KillMonsterVnum ?? -1}.{timeSpaceObjective.KilledMonsterAmount}/{timeSpaceObjective.KillMonsterAmount ?? 0} " + $"{timeSpaceObjective.CollectItemVnum ?? -1}.{timeSpaceObjective.CollectedItemAmount}/{timeSpaceObjective.CollectItemAmount ?? 0} " + $"{(timeSpaceObjective.Conversation == null ? "-1/0" : $"{timeSpaceObjective.ConversationsHad}/{timeSpaceObjective.Conversation}")} " + $"{timeSpaceObjective.InteractObjectsVnum ?? -1}.{timeSpaceObjective.InteractedObjectsAmount}/{timeSpaceObjective.InteractObjectsAmount ?? 0} " + $"{(timeSpaceObjective.ProtectNPC ? 1 : 0)} " + $"{timeSpace.MaxLives} 0"; } public static string GenerateMissionTargetMessage(this IClientSession session, string message) => $"sinfo {message}"; public static void SendMissionTargetMessage(this IClientSession session, string message) => session.SendPacket(session.GenerateMissionTargetMessage(message)); public static void SendMinfo(this IClientSession session) { if (!session.PlayerEntity.TimeSpaceComponent.IsInTimeSpaceParty) { return; } if (session.PlayerEntity.TimeSpaceComponent.TimeSpace.Instance == null) { return; } session.SendPacket(session.GenerateMinfo()); } public static void RefreshTimespaceScoreUi(this IClientSession session) { session.SendPacket(session.GenerateRnscPacket()); } public static string GenerateRnscPacket(this IClientSession session) => $"rnsc {session.PlayerEntity.TimeSpaceComponent.TimeSpace.Instance.Score}"; public static string GenerateRsfpPacket(this IClientSession session) { if (session.CurrentMapInstance.MapInstanceType == MapInstanceType.RaidInstance) { return "rsfp 0 0"; } if (session.CurrentMapInstance.MapInstanceType != MapInstanceType.TimeSpaceInstance) { return "rsfp 0 -1"; } return $"rsfp {session.CurrentMapInstance.MapIndexX} {session.CurrentMapInstance.MapIndexY}"; } public static string GenerateRsfmPacket(this IClientSession session, TimeSpaceAction action, byte spiralFloor) { TimeSpaceParty timeSpaceParty = session.PlayerEntity.TimeSpaceComponent.TimeSpace; byte maxIndexX = timeSpaceParty.Instance.TimeSpaceSubInstances.Values.Max(s => s.MapInstance.MapIndexX); byte maxIndexY = timeSpaceParty.Instance.TimeSpaceSubInstances.Values.Max(s => s.MapInstance.MapIndexY); return $"rsfm {timeSpaceParty.TimeSpaceId} {(byte)action} {maxIndexX + 1} {maxIndexY + 1} {spiralFloor}"; } public static string GenerateRepayPacket(this IClientSession session, IReadOnlyList rewards, TimeSpaceRewardItem selectedDrawItem) { string repayPacket = "repay"; foreach (TimeSpaceRewardItem bonusReward in rewards.Where(x => x.Type == TimeSpaceRewardType.BONUS)) { repayPacket += $" {bonusReward.ItemVnum}.{bonusReward.Rarity}.{bonusReward.Amount}"; } for (int i = 0; i < 3 - rewards.Count(x => x.Type == TimeSpaceRewardType.BONUS); i++) { repayPacket += " -1.0.0"; } foreach (TimeSpaceRewardItem specialReward in rewards.Where(x => x.Type == TimeSpaceRewardType.SPECIAL)) { repayPacket += $" {specialReward.ItemVnum}.{specialReward.Rarity}.{specialReward.Amount}"; } for (int i = 0; i < 2 - rewards.Count(x => x.Type == TimeSpaceRewardType.SPECIAL); i++) { repayPacket += " -1.0.0"; } repayPacket += selectedDrawItem == null ? " -1.0.0" : $" {selectedDrawItem.ItemVnum}.{selectedDrawItem.Rarity}.{selectedDrawItem.Amount}"; return repayPacket; } public static string GenerateScorePacket(this IClientSession session, TimeSpaceFinishType timeSpaceFinishType, bool isMonsterPerfect = false, bool isNpcPerfect = false, bool isMapsPerfect = false) { TimeSpaceParty timeSpace = session.PlayerEntity.TimeSpaceComponent.TimeSpace; TimeSpaceInstance timeSpaceInstance = session.PlayerEntity.TimeSpaceComponent.TimeSpace.Instance; return "score " + $"{(int)timeSpaceFinishType} " + $"{timeSpaceInstance.Score} " + "500 " + "500 " + "500 " + $"{(timeSpace.IsEasyMode ? 0 : timeSpace.TimeSpaceInformation.Rewards.Draw?.Count ?? 0)} " + $"{timeSpaceInstance.KilledMonsters} " + $"{timeSpaceInstance.EnteredRooms} " + $"{timeSpaceInstance.SavedNpcs} " + $"{(isMonsterPerfect ? 1 : 0)}{(isMapsPerfect ? 1 : 0)}{(isNpcPerfect ? 1 : 0)} " + "1 " + "15"; } public static IEnumerable GenerateTimeSpacePortals(this IClientSession session) { var packets = new List(); foreach (ITimeSpacePortalEntity timeSpace in session.CurrentMapInstance.TimeSpacePortals) { byte type = (byte)(session.PlayerEntity.Level < timeSpace.MinLevel ? 0 : 1); if (session.PlayerEntity.CompletedTimeSpaces.Contains(timeSpace.TimeSpaceId)) { type = 4; } if (timeSpace.IsHero) { type = 8; } packets.Add($"wp {timeSpace.Position.X} {timeSpace.Position.Y} {timeSpace.TimeSpaceId} {type} {timeSpace.MinLevel} {timeSpace.MaxLevel}"); } return packets; } public static void SendTimeSpacePortals(this IClientSession session) { session.SendPackets(session.GenerateTimeSpacePortals()); } public static string GenerateTimeSpaceInfo(this IClientSession session, ITimeSpacePortalEntity portalEntity, TimeSpaceRecordDto timeSpaceRecordDto) { byte type = (byte)(session.PlayerEntity.Level < portalEntity.MinLevel ? 0 : 1); bool completed = session.PlayerEntity.CompletedTimeSpaces.Contains(portalEntity.TimeSpaceId); if (completed) { type = 4; } string name = session.GetLanguage(portalEntity.Name); string desc = session.GetLanguage(portalEntity.Description); var drawRewards = new StringBuilder(); var specialRewards = new StringBuilder(); var bonusRewards = new StringBuilder(); byte drawCount = 5; byte specialCount = 2; byte bonusCount = 3; foreach ((short vnum, short amount) in portalEntity.DrawRewards) { drawCount--; drawRewards.Append($"{vnum}.{amount} "); } for (int i = 0; i < drawCount; i++) { drawRewards.Append("-1.0 "); } foreach ((short vnum, short amount) in portalEntity.SpecialRewards) { specialCount--; specialRewards.Append($"{vnum}.{amount} "); } for (int i = 0; i < specialCount; i++) { specialRewards.Append("-1.0 "); } foreach ((short vnum, short amount) in portalEntity.BonusRewards) { bonusCount--; bonusRewards.Append($"{vnum}.{amount} "); } for (int i = 0; i < bonusCount; i++) { bonusRewards.Append("-1.0 "); } return $"rbr {portalEntity.TimeSpaceId}.{(portalEntity.IsHero ? 1 : portalEntity.IsHidden || portalEntity.IsSpecial ? 2 : 0)}.0 {type} {(completed ? 15 : 0)} " + $"{portalEntity.MinLevel}.{portalEntity.MaxLevel} {portalEntity.SeedsOfPowerRequired} " + $"{drawRewards}{specialRewards}{bonusRewards}{(timeSpaceRecordDto == null ? "-1.0" : $"{timeSpaceRecordDto.Record}.{timeSpaceRecordDto.CharacterName}")} 0 " + $"{(portalEntity.IsHidden || portalEntity.IsSpecial ? 0 : 1)} {name}\n{desc}"; } public static string GenerateTimeSpaceInfo(this IClientSession session, INpcEntity npcEntity, List<(short, short)> drawRewards, List<(short, short)> specialRewards, List<(short, short)> bonusRewards, TimeSpaceRecordDto timeSpaceRecordDto) { byte type = (byte)(session.PlayerEntity.Level < npcEntity.TimeSpaceInfo.MinLevel ? 0 : 1); bool completed = session.PlayerEntity.CompletedTimeSpaces.Contains(npcEntity.TimeSpaceInfo.TsId); if (completed) { type = 4; } string name = session.GetLanguage(npcEntity.TimeSpaceInfo.Name); string desc = session.GetLanguage(npcEntity.TimeSpaceInfo.Description); var drawRewardsPacket = new StringBuilder(); var specialRewardsPacket = new StringBuilder(); var bonusRewardsPacket = new StringBuilder(); byte drawCount = 5; byte specialCount = 2; byte bonusCount = 3; foreach ((short vnum, short amount) in drawRewards) { drawCount--; drawRewardsPacket.Append($"{vnum}.{amount} "); } for (int i = 0; i < drawCount; i++) { drawRewardsPacket.Append("-1.0 "); } foreach ((short vnum, short amount) in specialRewards) { specialCount--; specialRewardsPacket.Append($"{vnum}.{amount} "); } for (int i = 0; i < specialCount; i++) { specialRewardsPacket.Append("-1.0 "); } foreach ((short vnum, short amount) in bonusRewards) { bonusCount--; bonusRewardsPacket.Append($"{vnum}.{amount} "); } for (int i = 0; i < bonusCount; i++) { bonusRewardsPacket.Append("-1.0 "); } return $"rbr {npcEntity.TimeSpaceInfo.TsId}.{(npcEntity.TimeSpaceInfo.IsHero ? 1 : npcEntity.TimeSpaceInfo.IsHidden || npcEntity.TimeSpaceInfo.IsSpecial ? 2 : 0)}.0 {type} {(completed ? 15 : 0)} " + $"{npcEntity.TimeSpaceInfo.MinLevel}.{npcEntity.TimeSpaceInfo.MaxLevel} {npcEntity.TimeSpaceInfo.SeedsOfPowerRequired} " + $"{drawRewardsPacket}{specialRewardsPacket}{bonusRewardsPacket}{(timeSpaceRecordDto == null ? "-1.0" : $"{timeSpaceRecordDto.Record}.{timeSpaceRecordDto.CharacterName}")} 0 " + $"{(npcEntity.TimeSpaceInfo.IsHidden || npcEntity.TimeSpaceInfo.IsSpecial ? 0 : 1)} {name}\n{desc}"; } public static void SendTimeSpaceInfo(this IClientSession session, ITimeSpacePortalEntity portal, TimeSpaceRecordDto timeSpaceRecordDto) { session.SendPacket(session.GenerateTimeSpaceInfo(portal, timeSpaceRecordDto)); } public static void SendTimeSpaceInfo(this IClientSession session, INpcEntity npc, TimeSpaceRecordDto timeSpaceRecordDto) { TimeSpaceFileConfiguration timeSpaceFileConfiguration = npc.TimeSpaceInfo; List<(short, short)> drawRewards = new(); List<(short, short)> specialRewards = new(); List<(short, short)> bonusRewards = new(); if (timeSpaceFileConfiguration.Rewards?.Draw != null) { foreach (TimeSpaceItemConfiguration draw in timeSpaceFileConfiguration.Rewards.Draw) { drawRewards.Add((draw.ItemVnum, draw.Amount)); } } if (timeSpaceFileConfiguration.Rewards?.Special != null) { foreach (TimeSpaceItemConfiguration special in timeSpaceFileConfiguration.Rewards.Special) { specialRewards.Add((special.ItemVnum, special.Amount)); } } if (timeSpaceFileConfiguration.Rewards?.Bonus != null) { foreach (TimeSpaceItemConfiguration bonus in timeSpaceFileConfiguration.Rewards.Bonus) { bonusRewards.Add((bonus.ItemVnum, bonus.Amount)); } } session.SendPacket(session.GenerateTimeSpaceInfo(npc, drawRewards, specialRewards, bonusRewards, timeSpaceRecordDto)); } public static string GenerateRsfiPacket(this IClientSession session, ISubActConfiguration subActConfiguration, ITimeSpaceConfiguration timeSpaceConfiguration) { long lastCompletedTimeSpace = 0; foreach (long tsId in session.PlayerEntity.CompletedTimeSpaces.Reverse()) { TimeSpaceFileConfiguration getTimeSpace = timeSpaceConfiguration.GetTimeSpaceConfiguration(tsId); if (getTimeSpace == null) { continue; } if (getTimeSpace.IsSpecial || getTimeSpace.IsHidden) { continue; } lastCompletedTimeSpace = tsId; break; } SubActsConfiguration getAct = subActConfiguration.GetConfigurationByTimeSpaceId(lastCompletedTimeSpace); if (getAct == null) { return "rsfi 0 0 0 0 0 0"; } int timeSpaceCount = getAct.TimeSpaces.Length; int timeSpacesDone = getAct.TimeSpaces.Count(timeSpace => session.PlayerEntity.CompletedTimeSpaces.Contains(timeSpace)); // Get next act if (timeSpaceCount == timeSpacesDone) { SubActsConfiguration newAct = null; int counter = subActConfiguration.GetConfigurations().Count(); int count = 0; while (counter > 0) { counter--; count++; newAct = subActConfiguration.GetConfigurationById(getAct.Id + count); if (newAct != null) { break; } } if (newAct != null) { getAct = newAct; timeSpaceCount = getAct.TimeSpaces.Length; timeSpacesDone = getAct.TimeSpaces.Count(timeSpace => session.PlayerEntity.CompletedTimeSpaces.Contains(timeSpace)); } } return $"rsfi {getAct.Act} {getAct.SubAct} {timeSpacesDone} {timeSpaceCount} {timeSpacesDone} {timeSpaceCount}"; } public static void SendRsfiPacket(this IClientSession session, ISubActConfiguration subActConfiguration, ITimeSpaceConfiguration timeSpaceConfiguration) { session.SendPacket(session.GenerateRsfiPacket(subActConfiguration, timeSpaceConfiguration)); } public static string GenerateTimerFreeze(this IClientSession session) => "guri 8 8"; public static void SendTimerFreeze(this IClientSession session) => session.SendPacket(session.GenerateTimerFreeze()); public static string GenerateNpcReqPacket(this IClientSession session, int dialog) => $"npc_req 1 {session.PlayerEntity.Id} {dialog}"; public static void SendNpcReqPacket(this IClientSession session, int dialog) => session.SendPacket(session.GenerateNpcReqPacket(dialog)); public static int CalculateGoldReward(this TimeSpaceParty timeSpaceParty) => (int)(timeSpaceParty.TimeSpaceInformation.MinLevel * (2 + Math.Floor(timeSpaceParty.TimeSpaceInformation.MinLevel / 10.0) / 5) * timeSpaceParty.Instance.KilledMonsters); public static long CalculateExperience(this TimeSpaceParty timeSpaceParty) => timeSpaceParty.TimeSpaceInformation.MinLevel * 3 * timeSpaceParty.Instance.KilledMonsters; public static int GetTimeSpacePenalty(this IClientSession session) { TimeSpaceParty timeSpaceParty = session.PlayerEntity.TimeSpaceComponent.TimeSpace; int difference = session.PlayerEntity.Level - timeSpaceParty.TimeSpaceInformation.MinLevel; switch (difference) { case <= 0: return 0; case > 50: difference = 50; break; } return difference * 2; } public static int GetTimeSpaceScorePenalty(this TimeSpaceParty timeSpace) { int difference = timeSpace.HigherLevel - timeSpace.TimeSpaceInformation.MinLevel; switch (difference) { case <= 0: return 0; case > 50: difference = 50; break; } return difference * 2; } public static bool CanJoinToTimeSpace(this IClientSession session, long timeSpaceId, ISubActConfiguration subActConfiguration, ITimeSpaceConfiguration timeSpaceConfig) { TimeSpaceFileConfiguration timeSpaceConfigInfo = timeSpaceConfig.GetTimeSpaceConfiguration(timeSpaceId); SubActsConfiguration timeSpaceConfiguration = subActConfiguration.GetConfigurationByTimeSpaceId(timeSpaceId); if (timeSpaceConfiguration == null || timeSpaceConfigInfo == null) { return false; } bool canJoin = true; SubActsConfiguration getPreviousTimeSpaceConfiguration = null; int count = 0; int counter = subActConfiguration.GetConfigurations().Count(); while (counter > count) { count++; getPreviousTimeSpaceConfiguration = subActConfiguration.GetConfigurationById(timeSpaceConfiguration.Id - count); if (getPreviousTimeSpaceConfiguration?.TimeSpaces == null || !getPreviousTimeSpaceConfiguration.TimeSpaces.Any()) { continue; } break; } if (getPreviousTimeSpaceConfiguration?.TimeSpaces != null) { long getLastTimeSpace = getPreviousTimeSpaceConfiguration.TimeSpaces.LastOrDefault(); if (!session.PlayerEntity.CompletedTimeSpaces.Contains(getLastTimeSpace)) { canJoin = false; } } if (!timeSpaceConfigInfo.IsHero) { return canJoin; } foreach (long timeSpace in timeSpaceConfiguration.TimeSpaces) { if (timeSpace >= timeSpaceId) { continue; } if (session.PlayerEntity.CompletedTimeSpaces.Contains(timeSpace)) { continue; } canJoin = false; break; } return canJoin; } public static void SendScorePacket(this IClientSession session, TimeSpaceFinishType timeSpaceFinishType, bool isMonsterPerfect = false, bool isNpcPerfect = false, bool isMapsPerfect = false) => session.SendPacket(session.GenerateScorePacket(timeSpaceFinishType, isMonsterPerfect, isNpcPerfect, isMapsPerfect)); public static void SendRsfpPacket(this IClientSession session) => session.SendPacket(session.GenerateRsfpPacket()); public static void SendRsfmPacket(this IClientSession session, TimeSpaceAction action, byte spiralFloor = 0) { TimeSpaceParty timeSpaceParty = session.PlayerEntity.TimeSpaceComponent.TimeSpace; if (timeSpaceParty?.Instance == null) { return; } session.SendPacket(session.GenerateRsfmPacket(action, spiralFloor)); } public static void SendRepayPacket(this IClientSession session, IReadOnlyList rewards, TimeSpaceRewardItem selectedDrawItem) => session.SendPacket(session.GenerateRepayPacket(rewards, selectedDrawItem)); public static bool IsInSpecialOrHiddenTimeSpace(this IClientSession session) { TimeSpaceParty timeSpace = session.PlayerEntity.TimeSpaceComponent.TimeSpace; if (timeSpace == null) { return false; } return timeSpace.TimeSpaceInformation.IsSpecial || timeSpace.TimeSpaceInformation.IsHidden; } }