diff --git a/Libraries/Core/Helpers/QuestHelper.cs b/Libraries/Core/Helpers/QuestHelper.cs
index 92563513..edce4612 100644
--- a/Libraries/Core/Helpers/QuestHelper.cs
+++ b/Libraries/Core/Helpers/QuestHelper.cs
@@ -129,14 +129,23 @@ public class QuestHelper(
public bool TraderLoyaltyLevelRequirementCheck(QuestCondition questProperties, PmcData profile)
{
var requiredLoyaltyLevel = questProperties.Value as float?;
- if (!profile.TradersInfo.TryGetValue(questProperties.Target as string, out var trader))
+ if (!profile.TradersInfo.TryGetValue(
+ questProperties.Target.IsItem
+ ? questProperties.Target.Item
+ : questProperties.Target.List.FirstOrDefault(),
+ out var trader
+ ))
{
_logger.Error(
_localisationService.GetText("quest-unable_to_find_trader_in_profile", questProperties.Target)
);
}
- return CompareAvailableForValues(trader.LoyaltyLevel.Value, requiredLoyaltyLevel.Value, questProperties.CompareMethod);
+ return CompareAvailableForValues(
+ trader.LoyaltyLevel.Value,
+ requiredLoyaltyLevel.Value,
+ questProperties.CompareMethod
+ );
}
///
@@ -148,7 +157,12 @@ public class QuestHelper(
public bool TraderStandingRequirementCheck(QuestCondition questProperties, PmcData profile)
{
var requiredLoyaltyLevel = int.Parse(questProperties.Value.ToString());
- if (!profile.TradersInfo.TryGetValue(questProperties.Target.ToString(), out var trader))
+ if (!profile.TradersInfo.TryGetValue(
+ questProperties.Target.IsItem
+ ? questProperties.Target.Item
+ : questProperties.Target.List.FirstOrDefault(),
+ out var trader
+ ))
{
_logger.Error(
_localisationService.GetText("quest-unable_to_find_trader_in_profile", questProperties.Target)
@@ -219,19 +233,22 @@ public class QuestHelper(
var isHalloweenEventActive = _seasonalEventService.HalloweenEventEnabled();
// Not christmas + quest is for christmas
- if (!isChristmasEventActive && _seasonalEventService.IsQuestRelatedToEvent(questId, SeasonalEventType.Christmas))
+ if (!isChristmasEventActive &&
+ _seasonalEventService.IsQuestRelatedToEvent(questId, SeasonalEventType.Christmas))
{
return false;
}
// Not halloween + quest is for halloween
- if (!isHalloweenEventActive && _seasonalEventService.IsQuestRelatedToEvent(questId, SeasonalEventType.Halloween))
+ if (!isHalloweenEventActive &&
+ _seasonalEventService.IsQuestRelatedToEvent(questId, SeasonalEventType.Halloween))
{
return false;
}
// Should non-season event quests be shown to player
- if (!(_questConfig.ShowNonSeasonalEventQuests ?? false) && _seasonalEventService.IsQuestRelatedToEvent(questId, SeasonalEventType.None))
+ if (!(_questConfig.ShowNonSeasonalEventQuests ?? false) &&
+ _seasonalEventService.IsQuestRelatedToEvent(questId, SeasonalEventType.None))
{
return false;
}
@@ -316,11 +333,9 @@ public class QuestHelper(
(q) =>
{
var acceptedQuestCondition = q.Conditions.AvailableForStart.FirstOrDefault(
- (c) =>
- {
- return (c.ConditionType == "Quest" && ((List)c.Target).Contains(failedQuestId) && c.Status[0] == QuestStatusEnum.Fail
- );
- }
+ c => c.ConditionType == "Quest" &&
+ (c.Target.IsList ? c.Target.List : [c.Target.Item]).Contains(failedQuestId) &&
+ c.Status[0] == QuestStatusEnum.Fail
);
if (acceptedQuestCondition is null)
@@ -638,7 +653,8 @@ public class QuestHelper(
}
var condition = questInDb.Conditions.AvailableForFinish.FirstOrDefault(
- (c) => c.ConditionType == "FindItem" && (((List)c?.Target)?.Contains(itemTpl) ?? false)
+ c => c.ConditionType == "FindItem" &&
+ ((c.Target.IsList ? c.Target.List : [c.Target.Item])?.Contains(itemTpl) ?? false)
);
if (condition is not null)
{
@@ -728,7 +744,13 @@ public class QuestHelper(
return false;
}
- return quest.Conditions.Fail.Any((condition) => (((List)condition.Target)?.Contains(completedQuestId)) ?? false);
+ return quest.Conditions.Fail.Any(
+ condition =>
+ (condition.Target.IsList ? condition.Target.List : [condition.Target.Item])?.Contains(
+ completedQuestId
+ ) ??
+ false
+ );
}
)
.ToList();
@@ -758,7 +780,8 @@ public class QuestHelper(
var preCompleteProfileQuests = _cloner.Clone(pmcData.Quests);
var completedQuestId = body.QuestId;
- var clientQuestsClone = _cloner.Clone(GetClientQuests(sessionID)); // Must be gathered prior to applyQuestReward() & failQuests()
+ var clientQuestsClone =
+ _cloner.Clone(GetClientQuests(sessionID)); // Must be gathered prior to applyQuestReward() & failQuests()
var newQuestState = QuestStatusEnum.Success;
UpdateQuestState(pmcData, newQuestState, completedQuestId);
@@ -868,7 +891,9 @@ public class QuestHelper(
// Player can use trader mods then remove them, leaving quests behind
if (!profile.TradersInfo.TryGetValue(quest.TraderId, out var trader))
{
- _logger.Debug($"Unable to show quest: {quest.QuestName} as its for a trader: {quest.TraderId} that no longer exists.");
+ _logger.Debug(
+ $"Unable to show quest: {quest.QuestName} as its for a trader: {quest.TraderId} that no longer exists."
+ );
continue;
}
@@ -890,8 +915,13 @@ public class QuestHelper(
foreach (var conditionToFulfil in questRequirements)
{
// If the previous quest isn't in the user profile, it hasn't been completed or started
- var questIdsToFulfil = conditionToFulfil.Target as string[] ?? [];
- var prerequisiteQuest = profile.Quests.FirstOrDefault(profileQuest => questIdsToFulfil.Contains(profileQuest.QId));
+ var questIdsToFulfil = (conditionToFulfil.Target.IsList
+ ? conditionToFulfil.Target.List
+ : (conditionToFulfil.Target.Item == null
+ ? null
+ : [conditionToFulfil.Target.Item])) ?? [];
+ var prerequisiteQuest =
+ profile.Quests.FirstOrDefault(profileQuest => questIdsToFulfil.Contains(profileQuest.QId));
if (prerequisiteQuest is null)
{
@@ -911,7 +941,10 @@ public class QuestHelper(
if (conditionToFulfil.AvailableAfter > 0)
{
// Compare current time to unlock time for previous quest
- prerequisiteQuest.StatusTimers.TryGetValue(prerequisiteQuest.Status.Value, out var previousQuestCompleteTime);
+ prerequisiteQuest.StatusTimers.TryGetValue(
+ prerequisiteQuest.Status.Value,
+ out var previousQuestCompleteTime
+ );
var unlockTime = previousQuestCompleteTime + conditionToFulfil.AvailableAfter;
if (unlockTime > _timeUtil.GetTimeStamp())
{
diff --git a/Libraries/Core/Models/Eft/Common/Tables/Quest.cs b/Libraries/Core/Models/Eft/Common/Tables/Quest.cs
index 7be9f5f8..f6e6c837 100644
--- a/Libraries/Core/Models/Eft/Common/Tables/Quest.cs
+++ b/Libraries/Core/Models/Eft/Common/Tables/Quest.cs
@@ -1,5 +1,7 @@
using System.Text.Json.Serialization;
using Core.Models.Enums;
+using Core.Utils.Json;
+using Core.Utils.Json.Converters;
using SptCommon.Extensions;
namespace Core.Models.Eft.Common.Tables;
@@ -205,7 +207,8 @@ public record QuestCondition
/// Can be: string[] or string
///
[JsonPropertyName("target")]
- public object? Target { get; set; } // TODO: string[] | string
+ [JsonConverter(typeof(ListOrTConverterFactory))]
+ public ListOrT? Target { get; set; } // TODO: string[] | string
[JsonPropertyName("value")]
public object? Value { get; set; } // TODO: string | number
diff --git a/Libraries/Core/Services/LocationLifecycleService.cs b/Libraries/Core/Services/LocationLifecycleService.cs
index cc8384a2..470b1fc3 100644
--- a/Libraries/Core/Services/LocationLifecycleService.cs
+++ b/Libraries/Core/Services/LocationLifecycleService.cs
@@ -818,7 +818,10 @@ public class LocationLifecycleService
// Find a quest that has a FindItem condition that has the list items tpl as a target
var matchingQuests = questDb.Where(quest => {
var matchingCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(
- questCondition => questCondition.ConditionType == "FindItem" && (questCondition.Target as List).Contains(lostItem.Template)
+ questCondition => questCondition.ConditionType == "FindItem" &&
+ (questCondition.Target.IsList
+ ? questCondition.Target.List
+ : [questCondition.Target.Item]).Contains(lostItem.Template)
);
if (matchingCondition is null) {
// Quest doesnt have a matching condition
diff --git a/Libraries/Core/Utils/Json/Converters/ListOrTConverter.cs b/Libraries/Core/Utils/Json/Converters/ListOrTConverter.cs
index fca1deea..8cd96f1a 100644
--- a/Libraries/Core/Utils/Json/Converters/ListOrTConverter.cs
+++ b/Libraries/Core/Utils/Json/Converters/ListOrTConverter.cs
@@ -22,6 +22,14 @@ public class ListOrTConverter : JsonConverter?>
{
switch (reader.TokenType)
{
+ case JsonTokenType.String:
+ case JsonTokenType.Number:
+ using (var jsonDocument = JsonDocument.ParseValue(ref reader))
+ {
+ var jsonText = jsonDocument.RootElement.GetRawText();
+ var value = JsonSerializer.Deserialize(jsonText, options);
+ return new ListOrT(null, value);
+ }
case JsonTokenType.StartArray:
using (var jsonDocument = JsonDocument.ParseValue(ref reader))
{