551 lines
No EOL
27 KiB
C#
551 lines
No EOL
27 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using PhoenixLib.Logging;
|
|
using WingsEmu.DTOs.Quests;
|
|
using WingsEmu.Game._enum;
|
|
using WingsEmu.Game.Algorithm;
|
|
using WingsEmu.Game.Characters;
|
|
using WingsEmu.Game.Configurations;
|
|
using WingsEmu.Game.Entities;
|
|
using WingsEmu.Game.Helpers.Damages;
|
|
using WingsEmu.Game.Networking;
|
|
using WingsEmu.Game.Quests;
|
|
using WingsEmu.Packets.Enums;
|
|
using WingsEmu.Packets.Enums.Character;
|
|
|
|
namespace WingsAPI.Game.Extensions.Quests
|
|
{
|
|
public static class QuestExtensions
|
|
{
|
|
private static readonly string _b64SqstIndex = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{}";
|
|
|
|
|
|
private static readonly QuestType[] DialogQuestTypes =
|
|
{
|
|
QuestType.DIALOG,
|
|
QuestType.DIALOG_2,
|
|
QuestType.DELIVER_ITEM_TO_NPC,
|
|
QuestType.GIVE_ITEM_TO_NPC,
|
|
QuestType.GIVE_ITEM_TO_NPC_2,
|
|
QuestType.GIVE_NPC_GOLD,
|
|
QuestType.DIALOG_WHILE_WEARING,
|
|
QuestType.DIALOG_WHILE_HAVING_ITEM
|
|
};
|
|
|
|
public static string GenerateTargetQuest(this IClientSession session, short x, short y, short mapId, int questId) => $"target {x} {y} {mapId} {questId}";
|
|
public static string GenerateTargetOffQuest(this IClientSession session, short x, short y, short mapId, int questId) => $"targetoff {x} {y} {mapId} {questId}";
|
|
|
|
public static string GenerateQnpcPacket(this IClientSession session, short level, short npcVnum, short mapId) =>
|
|
$"qnpc {npcVnum}|{mapId}|{level} -1|-1|-1 -1|-1|-1 -1|-1|-1 -1|-1|-1 -1|-1|-1 -1|-1|-1 -1|-1|-1 -1|-1|-1 -1|-1|-1";
|
|
|
|
public static string GenerateQrPacket(this IClientSession session, CharacterQuest quest, IReadOnlyCollection<CharacterQuestGeneratedReward> rndRewards, QuestsRatesConfiguration questRates)
|
|
{
|
|
List<QuestPrizeDto> prizes = quest.Quest.Prizes;
|
|
if (prizes == null)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
var qrPacket = new StringBuilder("qr ");
|
|
foreach (QuestPrizeDto prize in prizes)
|
|
{
|
|
switch (prize.RewardType)
|
|
{
|
|
case (byte)QuestRewardType.Reput:
|
|
case (byte)QuestRewardType.Exp:
|
|
case (byte)QuestRewardType.SecondExp:
|
|
case (byte)QuestRewardType.ThirdExp:
|
|
case (byte)QuestRewardType.JobExp:
|
|
case (byte)QuestRewardType.SecondJobExp:
|
|
qrPacket.Append($"{prize.RewardType.ToString()} 0 {prize.Data0.ToString()} ");
|
|
break;
|
|
case (byte)QuestRewardType.Gold:
|
|
qrPacket.Append($"{prize.RewardType.ToString()} 0 {prize.Data0 * questRates.GoldRate} ");
|
|
break;
|
|
case (byte)QuestRewardType.AllRewards:
|
|
qrPacket.Append(prize.Data0 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data0.ToString()} 1 ");
|
|
qrPacket.Append(prize.Data1 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data1.ToString()} 1 ");
|
|
qrPacket.Append(prize.Data2 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data2.ToString()} 1 ");
|
|
qrPacket.Append(prize.Data3 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data3.ToString()} 1 ");
|
|
break;
|
|
case (byte)QuestRewardType.SecondGold:
|
|
qrPacket.Append($"{prize.RewardType.ToString()} 0 {prize.Data0 * questRates.BaseGold * questRates.GoldRate} ");
|
|
break;
|
|
case (byte)QuestRewardType.ThirdGold:
|
|
qrPacket.Append($"{prize.RewardType.ToString()} 0 {prize.Data0 * quest.ObjectiveAmount.Sum(s => s.Value.RequiredAmount) * session.PlayerEntity.Level * questRates.GoldRate} ");
|
|
break;
|
|
case (byte)QuestRewardType.Unknow:
|
|
case (byte)QuestRewardType.ItemsDependingOnClass:
|
|
switch (session.PlayerEntity.Class)
|
|
{
|
|
case ClassType.Swordman:
|
|
qrPacket.Append(prize.Data0 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data0.ToString()} {prize.Data4.ToString()} ");
|
|
break;
|
|
case ClassType.Archer:
|
|
qrPacket.Append(prize.Data1 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data1.ToString()} {prize.Data4.ToString()} ");
|
|
break;
|
|
case ClassType.Magician:
|
|
qrPacket.Append(prize.Data2 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data2.ToString()} {prize.Data4.ToString()} ");
|
|
break;
|
|
default:
|
|
qrPacket.Append(prize.Data3 == -1 ? "" : $"{prize.RewardType.ToString()} {prize.Data3.ToString()} {prize.Data4.ToString()} ");
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (CharacterQuestGeneratedReward rndReward in rndRewards)
|
|
{
|
|
qrPacket.Append($"{(byte)QuestRewardType.RandomReward} {rndReward.ItemVnum} {rndReward.Amount} ");
|
|
}
|
|
|
|
for (int i = prizes.Count(s => s.RewardType != (byte)QuestRewardType.RandomReward) + rndRewards.Count; i < 4; i++)
|
|
{
|
|
qrPacket.Append("0 0 0 ");
|
|
}
|
|
|
|
qrPacket.Append(quest.QuestId);
|
|
return qrPacket.ToString();
|
|
}
|
|
|
|
public static string GenerateQuestList(this IClientSession session, IQuestManager questManager, int? questToShowInfo = null)
|
|
{
|
|
IEnumerable<CharacterQuest> quests = session.PlayerEntity.GetCurrentQuests();
|
|
string header = "qstlist";
|
|
if (quests == null)
|
|
{
|
|
return header;
|
|
}
|
|
|
|
var packet = new StringBuilder();
|
|
foreach (CharacterQuest quest in quests)
|
|
{
|
|
packet.Append($" {session.GenerateQuestData(quest, questManager, qstlistCall: true, questToShowInfo: questToShowInfo)}");
|
|
}
|
|
|
|
int soundFlowerEmptySlot = session.GetEmptyQuestSlot(QuestSlotType.GENERAL, true);
|
|
for (int i = 0; i < session.PlayerEntity.GetPendingSoundFlowerQuests(); i++)
|
|
{
|
|
packet.Append($" {soundFlowerEmptySlot + i}.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0");
|
|
}
|
|
|
|
return $"{header}{packet}";
|
|
}
|
|
|
|
public static string GenerateQuestProgress(this IClientSession session, IQuestManager questManager, int questVnum)
|
|
{
|
|
CharacterQuest currentQuest = session.PlayerEntity.GetCurrentQuest(questVnum);
|
|
return currentQuest == null ? string.Empty : $"qsti {session.GenerateQuestData(currentQuest, questManager, session.PlayerEntity.IsQuestCompleted(currentQuest))}";
|
|
}
|
|
|
|
private static string GenerateQuestData(this IClientSession session, CharacterQuest characterQuest, IQuestManager questManager, bool isFinished = false, bool qstlistCall = false,
|
|
int? questToShowInfo = null)
|
|
{
|
|
int questSlot = session.GetQuestSlot(characterQuest);
|
|
if (questSlot == -1)
|
|
{
|
|
Log.Debug($"The slot for quest {characterQuest.QuestId} was not found.");
|
|
return "";
|
|
}
|
|
|
|
IReadOnlyList<QuestObjectiveDto> objectives;
|
|
bool statusAdded;
|
|
var data = new StringBuilder($"{questSlot}.{characterQuest.QuestId}");
|
|
|
|
switch (characterQuest.SlotType)
|
|
{
|
|
case QuestSlotType.MAIN:
|
|
data.Append($".{characterQuest.QuestId}");
|
|
break;
|
|
case QuestSlotType.GENERAL:
|
|
IReadOnlyCollection<int> questlines = questManager.GetQuestlines(characterQuest.QuestId);
|
|
data.Append($".{(questlines.Any() ? questlines.First() : characterQuest.QuestId)}");
|
|
break;
|
|
case QuestSlotType.SECONDARY:
|
|
data.Append(".0");
|
|
break;
|
|
}
|
|
|
|
data.Append($".{(byte)characterQuest.Quest.QuestType}");
|
|
|
|
switch (characterQuest.Quest.QuestType)
|
|
{
|
|
case QuestType.WIN_RAID_AND_TALK_TO_NPC:
|
|
case QuestType.DELIVER_ITEM_TO_NPC:
|
|
case QuestType.KILL_MONSTER_BY_VNUM:
|
|
case QuestType.GIVE_ITEM_TO_NPC:
|
|
case QuestType.GIVE_ITEM_TO_NPC_2:
|
|
case QuestType.DIALOG_WHILE_WEARING:
|
|
case QuestType.DIALOG_WHILE_HAVING_ITEM:
|
|
case QuestType.USE_ITEM_ON_TARGET:
|
|
case QuestType.CAPTURE_WITHOUT_KEEPING:
|
|
case QuestType.CAPTURE_AND_KEEP:
|
|
case QuestType.DROP_IN_TIMESPACE:
|
|
case QuestType.COMPLETE_TIMESPACE:
|
|
case QuestType.CRAFT_WITHOUT_KEEPING:
|
|
case QuestType.DROP_CHANCE:
|
|
case QuestType.DROP_CHANCE_2:
|
|
case QuestType.COLLECT:
|
|
case QuestType.DROP_HARDCODED:
|
|
case QuestType.KILL_X_MOBS_SOUND_FLOWER:
|
|
statusAdded = false;
|
|
objectives = characterQuest.Quest.Objectives;
|
|
foreach (QuestObjectiveDto objective in objectives)
|
|
{
|
|
CharacterQuestObjectiveDto questObjectiveDto = characterQuest.ObjectiveAmount[objective.ObjectiveIndex];
|
|
data.Append($".{questObjectiveDto.CurrentAmount}.{questObjectiveDto.RequiredAmount}");
|
|
|
|
if (!statusAdded)
|
|
{
|
|
data.Append($".{(isFinished ? 1 : 0)}");
|
|
statusAdded = true;
|
|
}
|
|
}
|
|
|
|
for (int i = objectives.Count; i < 5; i++)
|
|
{
|
|
data.Append(".0.0");
|
|
}
|
|
|
|
break;
|
|
|
|
case QuestType.DIALOG:
|
|
case QuestType.DIALOG_2:
|
|
case QuestType.NOTHING:
|
|
case QuestType.GO_TO_MAP:
|
|
case QuestType.GIVE_NPC_GOLD:
|
|
data.Append($".0.0.{(isFinished ? 1 : 0)}.0.0.0.0.0.0.0.0");
|
|
break;
|
|
case QuestType.KILL_PLAYER_IN_REGION:
|
|
case QuestType.DIE_X_TIMES:
|
|
case QuestType.EARN_REPUTATION:
|
|
case QuestType.COMPLETE_TIMESPACE_WITH_ATLEAST_X_POINTS:
|
|
break;
|
|
}
|
|
|
|
if (qstlistCall)
|
|
{
|
|
data.Append(characterQuest.QuestId != questToShowInfo ? ".0" : $".{(isFinished ? 0 : 1)}");
|
|
}
|
|
else
|
|
{
|
|
data.Append(".0");
|
|
}
|
|
|
|
return data.ToString();
|
|
}
|
|
|
|
public static bool IsQuestCompleted(this IPlayerEntity player, CharacterQuest characterQuest)
|
|
{
|
|
if (characterQuest == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
IEnumerable<QuestObjectiveDto> objectives = characterQuest.Quest.Objectives;
|
|
switch (characterQuest.Quest.QuestType)
|
|
{
|
|
case QuestType.DELIVER_ITEM_TO_NPC:
|
|
case QuestType.GIVE_ITEM_TO_NPC:
|
|
case QuestType.GIVE_ITEM_TO_NPC_2:
|
|
case QuestType.KILL_MONSTER_BY_VNUM:
|
|
case QuestType.CAPTURE_WITHOUT_KEEPING:
|
|
case QuestType.CAPTURE_AND_KEEP:
|
|
case QuestType.CRAFT_WITHOUT_KEEPING:
|
|
case QuestType.DROP_CHANCE:
|
|
case QuestType.DROP_CHANCE_2:
|
|
case QuestType.DROP_IN_TIMESPACE:
|
|
case QuestType.COLLECT:
|
|
case QuestType.DROP_HARDCODED:
|
|
case QuestType.COMPLETE_TIMESPACE_WITH_ATLEAST_X_POINTS:
|
|
case QuestType.GIVE_NPC_GOLD:
|
|
case QuestType.COMPLETE_TIMESPACE:
|
|
case QuestType.KILL_X_MOBS_SOUND_FLOWER:
|
|
return !objectives.Any(s => characterQuest.ObjectiveAmount[s.ObjectiveIndex].CurrentAmount < characterQuest.ObjectiveAmount[s.ObjectiveIndex].RequiredAmount);
|
|
case QuestType.GO_TO_MAP:
|
|
return player.Position.IsInAoeZone(new Position(characterQuest.Quest.TargetMapX, characterQuest.Quest.TargetMapY), 2);
|
|
case QuestType.USE_ITEM_ON_TARGET:
|
|
return !objectives.Any(s => characterQuest.ObjectiveAmount[s.ObjectiveIndex].CurrentAmount <= 0);
|
|
case QuestType.DIALOG_WHILE_HAVING_ITEM:
|
|
return !objectives.Any(s => player.CountItemWithVnum(s.Data1) < characterQuest.ObjectiveAmount[s.ObjectiveIndex].RequiredAmount);
|
|
case QuestType.DIALOG:
|
|
case QuestType.DIALOG_2:
|
|
case QuestType.DIALOG_WHILE_WEARING:
|
|
if ((characterQuest.QuestId == (int)QuestsVnums.TALK_WEARING_KOVOLT_MASK_1 || characterQuest.QuestId == (int)QuestsVnums.TALK_WEARING_KOVOLT_MASK_2)
|
|
&& player.Class != ClassType.Adventurer) // Kovolt Mask
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return !objectives.Any(s => characterQuest.ObjectiveAmount[s.ObjectiveIndex].CurrentAmount <= 0);
|
|
case QuestType.NOTHING:
|
|
return true;
|
|
|
|
// Not used for now
|
|
case QuestType.WIN_RAID_AND_TALK_TO_NPC:
|
|
case QuestType.DIE_X_TIMES:
|
|
case QuestType.EARN_REPUTATION:
|
|
case QuestType.KILL_PLAYER_IN_REGION:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static string GenerateSqstPacket(this IClientSession session, IQuestManager questManager, int sqstIndex)
|
|
{
|
|
IReadOnlyCollection<QuestNpcDto> blueAlertQuests = questManager.GetNpcBlueAlertQuests().ToList();
|
|
IReadOnlyCollection<CharacterQuest> completedBlueAlertQuests = session.PlayerEntity.GetCompletedQuests().Where(s => blueAlertQuests.Any(t => t.QuestId == s.QuestId)).ToList();
|
|
var sb = new StringBuilder();
|
|
sb.Append($"sqst {sqstIndex} ");
|
|
for (byte packetIndex = 0; packetIndex < 250; packetIndex++)
|
|
{
|
|
byte[] binary = { 0, 0, 0, 0, 0, 0 };
|
|
for (byte bitIndex = 0; bitIndex < 6; bitIndex++)
|
|
{
|
|
int questId = packetIndex * 6 + bitIndex + 1500 * sqstIndex;
|
|
if (completedBlueAlertQuests.Any(s => s.QuestId == questId))
|
|
{
|
|
if (bitIndex == 0)
|
|
{
|
|
binary[bitIndex] = 1;
|
|
}
|
|
else
|
|
{
|
|
binary[^bitIndex] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
string binaryString = string.Join("", binary);
|
|
sb.Append(_b64SqstIndex.ElementAt(Convert.ToInt16(binaryString, 2)));
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static void SendSqstPackets(this IClientSession session, IQuestManager questManager)
|
|
{
|
|
var packets = new List<string>();
|
|
int sqstPacketsAmount = (int)Math.Ceiling(questManager.GetNpcBlueAlertQuests().Max(s => s.QuestId ?? 0) / 1500.0);
|
|
for (int i = 0; i < sqstPacketsAmount; i++)
|
|
{
|
|
packets.Add(session.GenerateSqstPacket(questManager, i));
|
|
}
|
|
|
|
session.SendPackets(packets);
|
|
}
|
|
|
|
public static void SendSqstPacket(this IClientSession session, IQuestManager questManager, int sqstIndex) => session.SendPacket(session.GenerateSqstPacket(questManager, sqstIndex));
|
|
|
|
public static void UpdateQuestSqstPacket(this IClientSession session, IQuestManager questManager, int questVnum)
|
|
{
|
|
int sqstIndex = (int)Math.Floor(questVnum / 1500.0);
|
|
session.SendPacket(session.GenerateSqstPacket(questManager, sqstIndex));
|
|
}
|
|
|
|
public static void SendQuestsTargets(this IClientSession session)
|
|
{
|
|
IReadOnlyCollection<CharacterQuest> characterQuests = session.PlayerEntity.GetCurrentQuests().Where(s => s.Quest.TargetMapId != 0).ToList();
|
|
if (!characterQuests.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (CharacterQuest characterQuest in characterQuests)
|
|
{
|
|
QuestDto quest = characterQuest.Quest;
|
|
session.SendTargetQuest(quest.TargetMapX, quest.TargetMapY, quest.TargetMapId, quest.Id);
|
|
}
|
|
}
|
|
|
|
public static void DeleteQuestTarget(this IClientSession session, CharacterQuest characterQuest)
|
|
{
|
|
if (characterQuest.Quest.TargetMapId == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
session.SendTargetOffQuest(characterQuest.Quest.TargetMapX, characterQuest.Quest.TargetMapY, characterQuest.Quest.TargetMapId, characterQuest.QuestId);
|
|
}
|
|
|
|
public static void SendTargetQuest(this IClientSession session, short x, short y, short mapId, int questId) => session.SendPacket(session.GenerateTargetQuest(x, y, mapId, questId));
|
|
public static void SendTargetOffQuest(this IClientSession session, short x, short y, short mapId, int questId) => session.SendPacket(session.GenerateTargetOffQuest(x, y, mapId, questId));
|
|
|
|
public static void RefreshQuestList(this IClientSession session, IQuestManager questManager, int? questToShowInfo) =>
|
|
session.SendPacket(session.GenerateQuestList(questManager, questToShowInfo));
|
|
|
|
public static void RefreshQuestProgress(this IClientSession session, IQuestManager questManager, int questId) => session.SendPacket(session.GenerateQuestProgress(questManager, questId));
|
|
|
|
public static void SendQrPacket(this IClientSession session, CharacterQuest quest, IReadOnlyCollection<CharacterQuestGeneratedReward> rndRewards, QuestsRatesConfiguration questRates) =>
|
|
session.SendPacket(session.GenerateQrPacket(quest, rndRewards, questRates));
|
|
|
|
public static void SendQnpcPacket(this IClientSession session, short level, short npcVnum, short mapId) => session.SendPacket(session.GenerateQnpcPacket(level, npcVnum, mapId));
|
|
public static CharacterQuest GetQuestById(this IPlayerEntity character, int questId) => character.GetCurrentQuest(questId);
|
|
public static CharacterQuest GetQuestByActionSlot(this IClientSession session, int action, int slot) => action == 1 ? session.PlayerEntity.GetCurrentQuest(slot) : session.GetQuestBySlot(slot);
|
|
|
|
public static CharacterQuest GetQuestBySlot(this IClientSession session, int slot)
|
|
{
|
|
CharacterQuest quest;
|
|
|
|
if (slot < 0 || slot > 10)
|
|
{
|
|
Log.Debug($"[ERROR] PACKET QT: Invalid slot index: {slot.ToString()}");
|
|
return null;
|
|
}
|
|
|
|
if (slot < 5)
|
|
{
|
|
quest = session.PlayerEntity.GetCurrentQuests().Where(s => s.SlotType == QuestSlotType.GENERAL).ElementAtOrDefault(slot);
|
|
}
|
|
else if (slot == 5)
|
|
{
|
|
quest = session.PlayerEntity.GetCurrentQuests().Where(s => s.SlotType == QuestSlotType.MAIN).ElementAtOrDefault(0);
|
|
}
|
|
else
|
|
{
|
|
slot -= 6;
|
|
quest = session.PlayerEntity.GetCurrentQuests().Where(s => s.SlotType == QuestSlotType.SECONDARY).ElementAtOrDefault(slot);
|
|
}
|
|
|
|
return quest;
|
|
}
|
|
|
|
public static long GetLevelXpPercentage(this ICharacterAlgorithm characterAlgorithm, short percentage, short level) =>
|
|
Convert.ToInt64(characterAlgorithm.GetLevelXp(level) * (percentage / 100.0));
|
|
|
|
public static long GetSpecialistJobXpPercentage(this ICharacterAlgorithm characterAlgorithm, short percentage, short level, bool isFunSpecialist) =>
|
|
Convert.ToInt64(characterAlgorithm.GetSpecialistJobXp(level, isFunSpecialist) * (percentage / 100.0));
|
|
|
|
public static long GetHeroLevelXpPercentage(this ICharacterAlgorithm characterAlgorithm, short percentage, short level) =>
|
|
Convert.ToInt64(characterAlgorithm.GetHeroLevelXp(level) * (percentage / 100.0));
|
|
|
|
public static long GetJobXpPercentage(this ICharacterAlgorithm characterAlgorithm, short percentage, short level) => Convert.ToInt64(characterAlgorithm.GetJobXp(level) * (percentage / 100.0));
|
|
|
|
public static int GetEmptyQuestSlot(this IClientSession session, QuestSlotType slotType, bool isSoundFlower = false)
|
|
{
|
|
IReadOnlyCollection<CharacterQuest> quests = session.PlayerEntity.GetCurrentQuests().Where(s => s.SlotType == slotType).ToList();
|
|
int currentGeneralQuestsCount = quests.Count + session.PlayerEntity.GetPendingSoundFlowerQuests();
|
|
int slot = slotType switch
|
|
{
|
|
QuestSlotType.GENERAL => currentGeneralQuestsCount > (isSoundFlower ? 5 : 4) ? -1 : quests.Count,
|
|
QuestSlotType.MAIN => !quests.Any() ? 5 : -1,
|
|
QuestSlotType.SECONDARY => quests.Count > 4 ? -1 : quests.Count + 6,
|
|
_ => -1
|
|
};
|
|
return slot;
|
|
}
|
|
|
|
public static int GetQuestSlot(this IClientSession session, CharacterQuest characterQuest)
|
|
{
|
|
var quests = session.PlayerEntity.GetCurrentQuests().Where(s => s.SlotType == characterQuest.SlotType).ToList();
|
|
return characterQuest.SlotType switch
|
|
{
|
|
QuestSlotType.GENERAL => quests.IndexOf(characterQuest),
|
|
QuestSlotType.MAIN => 5,
|
|
QuestSlotType.SECONDARY => quests.IndexOf(characterQuest) + 6,
|
|
_ => -1
|
|
};
|
|
}
|
|
|
|
public static bool HasAlreadyQuestOrQuestline(this IClientSession session, QuestDto quest, IQuestManager questManager, INpcRunTypeQuestsConfiguration npcRunTypeQuestsConfiguration)
|
|
{
|
|
return session.PlayerEntity.HasQuestWithId(quest.Id)
|
|
|| session.PlayerEntity.GetCurrentQuests().SelectMany(s => questManager.GetQuestlines(s.QuestId)).Intersect(questManager.GetQuestlines(quest.Id)).Any()
|
|
|| session.HasRunningPeriodicQuest(quest, npcRunTypeQuestsConfiguration);
|
|
}
|
|
|
|
public static async Task<bool> HasCompletedPeriodicQuest(this IClientSession session, QuestDto quest, IQuestManager questManager, INpcRunTypeQuestsConfiguration npcRunTypeQuestsConfiguration,
|
|
IPeriodicQuestsConfiguration periodicQuestsConfiguration)
|
|
{
|
|
IEnumerable<CharacterQuestDto> completedQuests = session.PlayerEntity.GetCompletedPeriodicQuests();
|
|
PeriodicQuestSet periodicQuestSet = periodicQuestsConfiguration.GetPeriodicQuestSetByQuestId(quest.Id);
|
|
if (periodicQuestSet == null)
|
|
{
|
|
return completedQuests.Any(s => npcRunTypeQuestsConfiguration.HaveTheSameNpcRunType(s.QuestId, quest.Id));
|
|
}
|
|
|
|
bool couldAddKey = periodicQuestSet.PerNoswingsAccount is true
|
|
? await questManager.TryTakeDailyQuest(session.Account.MasterAccountId, periodicQuestSet.Id)
|
|
: await questManager.TryTakeDailyQuest(session.PlayerEntity.Id, periodicQuestSet.Id);
|
|
return completedQuests.Any(s => npcRunTypeQuestsConfiguration.HaveTheSameNpcRunType(s.QuestId, quest.Id)) || !couldAddKey;
|
|
}
|
|
|
|
public static bool HasRunningPeriodicQuest(this IClientSession session, QuestDto quest, INpcRunTypeQuestsConfiguration npcRunTypeQuestsConfiguration)
|
|
{
|
|
IEnumerable<CharacterQuestDto> activeQuests = session.PlayerEntity.GetCurrentQuests();
|
|
return activeQuests.Any(s => npcRunTypeQuestsConfiguration.HaveTheSameNpcRunType(s.QuestId, quest.Id));
|
|
}
|
|
|
|
public static PeriodicQuestSet GetPeriodicQuestSet(this QuestDto quest, IQuestManager questManager, IPeriodicQuestsConfiguration periodicQuestsConfiguration)
|
|
{
|
|
PeriodicQuestSet periodicQuestSet = periodicQuestsConfiguration.GetDailyQuests().FirstOrDefault(s => s.QuestVnums.Any(t => questManager.GetQuestById(t).NextQuestId == quest.Id));
|
|
return periodicQuestSet;
|
|
}
|
|
|
|
public static bool ShowNpcDialog(this IClientSession session, INpcEntity npcEntity, IQuestManager questManager)
|
|
{
|
|
IEnumerable<CharacterQuest> characterQuests = session.PlayerEntity.GetCurrentQuestsByTypes(DialogQuestTypes).ToArray();
|
|
|
|
if (!characterQuests.Any())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
foreach (CharacterQuest characterQuest in characterQuests)
|
|
{
|
|
if (questManager.IsNpcBlueAlertQuest(characterQuest.QuestId) && (characterQuest.Quest.QuestType == QuestType.DIALOG || characterQuest.Quest.QuestType == QuestType.DIALOG_2))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (QuestObjectiveDto objective in characterQuest.Quest.Objectives)
|
|
{
|
|
if (npcEntity.NpcVNum != (characterQuest.Quest.QuestType is QuestType.DELIVER_ITEM_TO_NPC ? objective.Data1 : objective.Data0))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CharacterQuestObjectiveDto questObjectiveDto = characterQuest.ObjectiveAmount[objective.ObjectiveIndex];
|
|
switch (characterQuest.Quest.QuestType)
|
|
{
|
|
case QuestType.DELIVER_ITEM_TO_NPC:
|
|
case QuestType.GIVE_ITEM_TO_NPC_2:
|
|
case QuestType.GIVE_ITEM_TO_NPC:
|
|
int amountLeft = questObjectiveDto.RequiredAmount - questObjectiveDto.CurrentAmount;
|
|
if (amountLeft == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int amountInPossession = session.PlayerEntity.CountItemWithVnum(characterQuest.Quest.QuestType is QuestType.DELIVER_ITEM_TO_NPC ? objective.Data0 : objective.Data1);
|
|
if (amountInPossession == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
case QuestType.GIVE_NPC_GOLD:
|
|
int totalGoldToGive = questObjectiveDto.RequiredAmount;
|
|
if (session.PlayerEntity.Gold < totalGoldToGive)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
case QuestType.DIALOG:
|
|
case QuestType.DIALOG_2:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
} |