Repeatable quest generation MongoID conversion (#439)
* Update repeatable quest generation for mongoid, add new server localizations, switch `Traders` to MongoId * Give error default value
This commit is contained in:
@@ -677,6 +677,10 @@
|
||||
"repeatable-elimination-config-not-found": "Unable to find the elimination config",
|
||||
"repeatable-elimination-any-not-found": "We're not targeting a specific location and `any` was not found in locations",
|
||||
"repeatable-elimination-specific-weapon-null": "Specific allowed weapon categories are null",
|
||||
"repeatable-quest_helper_template_not_found": "No repeatable quest template found for type: %s",
|
||||
"repeatable-quest_helper_template_name_not_found": "Could not resolve template name for: %s",
|
||||
"repeatable-quest_helper_no_status": "No quest status found for type: %s",
|
||||
"repeatable-quest_helper_no_loc_id": "No location in LocationIdMap found for key: %s",
|
||||
"reward-type_not_handled": "Reward type: {{rewardType}} not handled for quest/achievement: {{questId}}",
|
||||
"reward-unable_to_find_matching_hideout_production": "Unable to find matching hideout craft unlock for quest/achievement: {{questId}}, matches found: {{matchCount}}",
|
||||
"route_onupdate_no_response": "onUpdate: %s route doesn't report success or fail",
|
||||
|
||||
@@ -2,6 +2,7 @@ using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Generators.RepeatableQuestGeneration;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Eft.ItemEvent;
|
||||
@@ -24,29 +25,28 @@ namespace SPTarkov.Server.Core.Controllers;
|
||||
|
||||
[Injectable]
|
||||
public class RepeatableQuestController(
|
||||
ISptLogger<RepeatableQuestChangeRequest> _logger,
|
||||
EliminationQuestGenerator _eliminationQuestGenerator,
|
||||
CompletionQuestGenerator _completionQuestGenerator,
|
||||
ExplorationQuestGenerator _explorationQuestGenerator,
|
||||
PickupQuestGenerator _pickupQuestGenerator,
|
||||
TimeUtil _timeUtil,
|
||||
MathUtil _mathUtil,
|
||||
RandomUtil _randomUtil,
|
||||
HttpResponseUtil _httpResponseUtil,
|
||||
ProfileHelper _profileHelper,
|
||||
ProfileFixerService _profileFixerService,
|
||||
ServerLocalisationService _serverLocalisationService,
|
||||
EventOutputHolder _eventOutputHolder,
|
||||
PaymentService _paymentService,
|
||||
RepeatableQuestHelper _repeatableQuestHelper,
|
||||
QuestHelper _questHelper,
|
||||
DatabaseService _databaseService,
|
||||
ConfigServer _configServer,
|
||||
ICloner _cloner
|
||||
ISptLogger<RepeatableQuestChangeRequest> logger,
|
||||
EliminationQuestGenerator eliminationQuestGenerator,
|
||||
CompletionQuestGenerator completionQuestGenerator,
|
||||
ExplorationQuestGenerator explorationQuestGenerator,
|
||||
PickupQuestGenerator pickupQuestGenerator,
|
||||
TimeUtil timeUtil,
|
||||
RandomUtil randomUtil,
|
||||
HttpResponseUtil httpResponseUtil,
|
||||
ProfileHelper profileHelper,
|
||||
ProfileFixerService profileFixerService,
|
||||
ServerLocalisationService serverLocalisationService,
|
||||
EventOutputHolder eventOutputHolder,
|
||||
PaymentService paymentService,
|
||||
RepeatableQuestHelper repeatableQuestHelper,
|
||||
QuestHelper questHelper,
|
||||
DatabaseService databaseService,
|
||||
ConfigServer configServer,
|
||||
ICloner cloner
|
||||
)
|
||||
{
|
||||
protected static readonly List<string> _questTypes = ["PickUp", "Exploration", "Elimination"];
|
||||
protected readonly QuestConfig QuestConfig = _configServer.GetConfig<QuestConfig>();
|
||||
protected readonly QuestConfig QuestConfig = configServer.GetConfig<QuestConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Handle the client accepting a repeatable quest and starting it
|
||||
@@ -64,7 +64,7 @@ public class RepeatableQuestController(
|
||||
)
|
||||
{
|
||||
// Create and store quest status object inside player profile
|
||||
var newRepeatableQuest = _questHelper.GetQuestReadyForProfile(
|
||||
var newRepeatableQuest = questHelper.GetQuestReadyForProfile(
|
||||
pmcData,
|
||||
QuestStatusEnum.Started,
|
||||
acceptedQuest
|
||||
@@ -75,15 +75,15 @@ public class RepeatableQuestController(
|
||||
var repeatableQuestProfile = GetRepeatableQuestFromProfile(pmcData, acceptedQuest.QuestId);
|
||||
if (repeatableQuestProfile is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"repeatable-accepted_repeatable_quest_not_found_in_active_quests",
|
||||
acceptedQuest.QuestId
|
||||
)
|
||||
);
|
||||
|
||||
throw new Exception(
|
||||
_serverLocalisationService.GetText("repeatable-unable_to_accept_quest_see_log")
|
||||
serverLocalisationService.GetText("repeatable-unable_to_accept_quest_see_log")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,13 +93,13 @@ public class RepeatableQuestController(
|
||||
&& _questTypes.Contains(repeatableQuestProfile.Type.ToString())
|
||||
)
|
||||
{
|
||||
var fullProfile = _profileHelper.GetFullProfile(sessionID);
|
||||
var fullProfile = profileHelper.GetFullProfile(sessionID);
|
||||
|
||||
fullProfile.CharacterData.ScavData.Quests ??= [];
|
||||
fullProfile.CharacterData.ScavData.Quests.Add(newRepeatableQuest);
|
||||
}
|
||||
|
||||
var response = _eventOutputHolder.GetOutput(sessionID);
|
||||
var response = eventOutputHolder.GetOutput(sessionID);
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -117,9 +117,9 @@ public class RepeatableQuestController(
|
||||
string sessionID
|
||||
)
|
||||
{
|
||||
var output = _eventOutputHolder.GetOutput(sessionID);
|
||||
var output = eventOutputHolder.GetOutput(sessionID);
|
||||
|
||||
var fullProfile = _profileHelper.GetFullProfile(sessionID);
|
||||
var fullProfile = profileHelper.GetFullProfile(sessionID);
|
||||
|
||||
// Check for existing quest in (daily/weekly/scav arrays)
|
||||
var repeatables = GetRepeatableById(changeRequest.QuestId, pmcData);
|
||||
@@ -128,12 +128,12 @@ public class RepeatableQuestController(
|
||||
if (repeatables.RepeatableType is null || repeatables.Quest is null)
|
||||
{
|
||||
// Unable to find quest being replaced
|
||||
var message = _serverLocalisationService.GetText(
|
||||
var message = serverLocalisationService.GetText(
|
||||
"quest-unable_to_find_repeatable_to_replace"
|
||||
);
|
||||
_logger.Error(message);
|
||||
logger.Error(message);
|
||||
|
||||
return _httpResponseUtil.AppendErrorToOutput(output, message);
|
||||
return httpResponseUtil.AppendErrorToOutput(output, message);
|
||||
}
|
||||
|
||||
// Subtype name of quest - daily/weekly/scav
|
||||
@@ -148,7 +148,7 @@ public class RepeatableQuestController(
|
||||
.ToList();
|
||||
|
||||
// Save for later cost calculations
|
||||
var previousChangeRequirement = _cloner.Clone(
|
||||
var previousChangeRequirement = cloner.Clone(
|
||||
repeatablesOfTypeInProfile.ChangeRequirement[changeRequest.QuestId]
|
||||
);
|
||||
|
||||
@@ -182,18 +182,18 @@ public class RepeatableQuestController(
|
||||
// Unable to find quest being replaced
|
||||
var message =
|
||||
$"Unable to generate repeatable quest of type: {repeatableTypeLower} to replace trader: {replacedQuestTraderId} quest: {changeRequest.QuestId}";
|
||||
_logger.Error(message);
|
||||
logger.Error(message);
|
||||
|
||||
return _httpResponseUtil.AppendErrorToOutput(output, message);
|
||||
return httpResponseUtil.AppendErrorToOutput(output, message);
|
||||
}
|
||||
|
||||
// Add newly generated quest to daily/weekly/scav type array
|
||||
newRepeatableQuest.Side = Enum.GetName(repeatableConfig.Side);
|
||||
repeatablesOfTypeInProfile.ActiveQuests.Add(newRepeatableQuest);
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
logger.Debug(
|
||||
$"Removing: {repeatableConfig.Name} quest: {questToReplace.Id} from trader: {questToReplace.TraderId} as its been replaced"
|
||||
);
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public class RepeatableQuestController(
|
||||
repeatablesOfTypeInProfile.ChangeRequirement[newRepeatableQuest.Id] = new ChangeRequirement
|
||||
{
|
||||
ChangeCost = newRepeatableQuest.ChangeCost,
|
||||
ChangeStandingCost = _randomUtil.GetArrayValue(repeatableConfig.StandingChangeCost),
|
||||
ChangeStandingCost = randomUtil.GetArrayValue(repeatableConfig.StandingChangeCost),
|
||||
};
|
||||
|
||||
// Check if we should charge player for replacing quest
|
||||
@@ -231,7 +231,7 @@ public class RepeatableQuestController(
|
||||
Math.Truncate(
|
||||
cost.Count.Value * (1 - (Math.Truncate(charismaBonus / 100) * 0.001))
|
||||
);
|
||||
_paymentService.AddPaymentToOutput(
|
||||
paymentService.AddPaymentToOutput(
|
||||
pmcData,
|
||||
cost.TemplateId,
|
||||
cost.Count.Value,
|
||||
@@ -246,7 +246,7 @@ public class RepeatableQuestController(
|
||||
}
|
||||
|
||||
// Clone data before we send it to client
|
||||
var repeatableToChangeClone = _cloner.Clone(repeatablesOfTypeInProfile);
|
||||
var repeatableToChangeClone = cloner.Clone(repeatablesOfTypeInProfile);
|
||||
|
||||
// Purge inactive repeatables
|
||||
repeatableToChangeClone.InactiveQuests = [];
|
||||
@@ -275,9 +275,9 @@ public class RepeatableQuestController(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Accepted repeatable quest: {questId} from: {repeatableQuest.Name}");
|
||||
logger.Debug($"Accepted repeatable quest: {questId} from: {repeatableQuest.Name}");
|
||||
}
|
||||
|
||||
matchingQuest.SptRepatableGroupName = repeatableQuest.Name;
|
||||
@@ -312,7 +312,7 @@ public class RepeatableQuestController(
|
||||
}
|
||||
|
||||
// Only certain game versions have access to free refreshes
|
||||
var hasAccessToFreeRefreshSystem = _profileHelper.HasAccessToRepeatableFreeRefreshSystem(
|
||||
var hasAccessToFreeRefreshSystem = profileHelper.HasAccessToRepeatableFreeRefreshSystem(
|
||||
fullProfile.CharacterData.PmcData
|
||||
);
|
||||
|
||||
@@ -396,8 +396,8 @@ public class RepeatableQuestController(
|
||||
|
||||
if (attempts > maxAttempts)
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText(
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"quest-repeatable_generation_failed_please_report",
|
||||
attempts
|
||||
)
|
||||
@@ -427,55 +427,55 @@ public class RepeatableQuestController(
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
{
|
||||
var questType = _randomUtil.DrawRandomFromList(questTypePool.Types).First();
|
||||
var questType = randomUtil.DrawRandomFromList(questTypePool.Types).First();
|
||||
|
||||
// Get traders from whitelist and filter by quest type availability
|
||||
var traders = repeatableConfig
|
||||
.TraderWhitelist.Where(x => x.QuestTypes.Contains(questType))
|
||||
.TraderWhitelist.Where(whitelist => whitelist.QuestTypes.Contains(questType))
|
||||
.Select(x => x.TraderId)
|
||||
// filter out locked traders
|
||||
.Where(x => pmcTraderInfo[x].Unlocked.GetValueOrDefault(false))
|
||||
.Where(mongoId => pmcTraderInfo[mongoId].Unlocked.GetValueOrDefault(false))
|
||||
.ToList();
|
||||
|
||||
var traderId = _randomUtil.DrawRandomFromList(traders).FirstOrDefault();
|
||||
if (traderId is null)
|
||||
var traderId = randomUtil.DrawRandomFromList(traders).FirstOrDefault();
|
||||
if (traderId.IsEmpty())
|
||||
{
|
||||
_logger.Error(
|
||||
_serverLocalisationService.GetText("repeatable-unable_to_find_trader_in_pool")
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("repeatable-unable_to_find_trader_in_pool")
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Generating operation task type: {questType} for {traderId}");
|
||||
logger.Debug($"Generating operation task type: {questType} for {traderId}");
|
||||
}
|
||||
|
||||
return questType switch
|
||||
{
|
||||
"Elimination" => _eliminationQuestGenerator.Generate(
|
||||
"Elimination" => eliminationQuestGenerator.Generate(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
questTypePool,
|
||||
repeatableConfig
|
||||
),
|
||||
"Completion" => _completionQuestGenerator.Generate(
|
||||
"Completion" => completionQuestGenerator.Generate(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
questTypePool,
|
||||
repeatableConfig
|
||||
),
|
||||
"Exploration" => _explorationQuestGenerator.Generate(
|
||||
"Exploration" => explorationQuestGenerator.Generate(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
questTypePool,
|
||||
repeatableConfig
|
||||
),
|
||||
"Pickup" => _pickupQuestGenerator.Generate(
|
||||
"Pickup" => pickupQuestGenerator.Generate(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
@@ -494,7 +494,7 @@ public class RepeatableQuestController(
|
||||
protected void RemoveQuestFromProfile(SptProfile fullProfile, string questToReplaceId)
|
||||
{
|
||||
// Find quest we're replacing in pmc profile quests array and remove it
|
||||
_questHelper.FindAndRemoveQuestFromArrayIfExists(
|
||||
questHelper.FindAndRemoveQuestFromArrayIfExists(
|
||||
questToReplaceId,
|
||||
fullProfile.CharacterData.PmcData.Quests
|
||||
);
|
||||
@@ -502,7 +502,7 @@ public class RepeatableQuestController(
|
||||
// Look for and remove quest we're replacing in scav profile too
|
||||
if (fullProfile.CharacterData.ScavData is not null)
|
||||
{
|
||||
_questHelper.FindAndRemoveQuestFromArrayIfExists(
|
||||
questHelper.FindAndRemoveQuestFromArrayIfExists(
|
||||
questToReplaceId,
|
||||
fullProfile.CharacterData.ScavData.Quests
|
||||
);
|
||||
@@ -563,9 +563,9 @@ public class RepeatableQuestController(
|
||||
public List<PmcDataRepeatableQuest> GetClientRepeatableQuests(string sessionID)
|
||||
{
|
||||
var returnData = new List<PmcDataRepeatableQuest>();
|
||||
var fullProfile = _profileHelper.GetFullProfile(sessionID);
|
||||
var fullProfile = profileHelper.GetFullProfile(sessionID);
|
||||
var pmcData = fullProfile.CharacterData.PmcData;
|
||||
var currentTime = _timeUtil.GetTimeStamp();
|
||||
var currentTime = timeUtil.GetTimeStamp();
|
||||
|
||||
// Daily / weekly / Daily_Savage
|
||||
foreach (var repeatableConfig in QuestConfig.RepeatableQuests)
|
||||
@@ -589,9 +589,9 @@ public class RepeatableQuestController(
|
||||
{
|
||||
returnData.Add(generatedRepeatables);
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"[Quest Check] {repeatableTypeLower} quests are still valid.");
|
||||
logger.Debug($"[Quest Check] {repeatableTypeLower} quests are still valid.");
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -602,9 +602,9 @@ public class RepeatableQuestController(
|
||||
// Set endtime to be now + new duration
|
||||
generatedRepeatables.EndTime = currentTime + repeatableConfig.ResetTime;
|
||||
generatedRepeatables.InactiveQuests = [];
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Generating new {repeatableTypeLower}");
|
||||
logger.Debug($"Generating new {repeatableTypeLower}");
|
||||
}
|
||||
|
||||
// Put old quests to inactive (this is required since only then the client makes them fail due to non-completion)
|
||||
@@ -636,7 +636,7 @@ public class RepeatableQuestController(
|
||||
lifeline++;
|
||||
if (lifeline > 10)
|
||||
{
|
||||
_logger.Error(
|
||||
logger.Error(
|
||||
"We were stuck in repeatable quest generation. This should never happen. Please report"
|
||||
);
|
||||
|
||||
@@ -669,7 +669,7 @@ public class RepeatableQuestController(
|
||||
new ChangeRequirement
|
||||
{
|
||||
ChangeCost = quest.ChangeCost,
|
||||
ChangeStandingCost = _randomUtil.GetArrayValue(
|
||||
ChangeStandingCost = randomUtil.GetArrayValue(
|
||||
repeatableConfig.StandingChangeCost
|
||||
), // Randomise standing loss to replace
|
||||
}
|
||||
@@ -712,7 +712,7 @@ public class RepeatableQuestController(
|
||||
var repeatableQuestDetails = pmcData.RepeatableQuests.FirstOrDefault(repeatable =>
|
||||
repeatable.Name == repeatableConfig.Name
|
||||
);
|
||||
var hasAccess = _profileHelper.HasAccessToRepeatableFreeRefreshSystem(pmcData);
|
||||
var hasAccess = profileHelper.HasAccessToRepeatableFreeRefreshSystem(pmcData);
|
||||
|
||||
if (repeatableQuestDetails is null)
|
||||
{
|
||||
@@ -766,9 +766,9 @@ public class RepeatableQuestController(
|
||||
// Scav and daily quests not unlocked yet
|
||||
if (repeatableConfig.Side == PlayerGroup.Scav && !PlayerHasDailyScavQuestsUnlocked(pmcData))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug("Daily scav quests still locked, Intel center not built");
|
||||
logger.Debug("Daily scav quests still locked, Intel center not built");
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -830,9 +830,9 @@ public class RepeatableQuestController(
|
||||
if (questStatusInProfile.Status == QuestStatusEnum.AvailableForFinish)
|
||||
{
|
||||
questsToKeep.Add(activeQuest);
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug( // TODO: this shouldn't happen, doesn't on live
|
||||
logger.Debug( // TODO: this shouldn't happen, doesn't on live
|
||||
$"Keeping repeatable quest: {activeQuest.Id} in activeQuests since it is available to hand in"
|
||||
);
|
||||
}
|
||||
@@ -841,7 +841,7 @@ public class RepeatableQuestController(
|
||||
}
|
||||
|
||||
// Clean up quest-related counters being left in profile
|
||||
_profileFixerService.RemoveDanglingConditionCounters(pmcData);
|
||||
profileFixerService.RemoveDanglingConditionCounters(pmcData);
|
||||
|
||||
// Remove expired quest from pmc.quest array
|
||||
pmcData.Quests = pmcData.Quests.Where(quest => quest.QId != activeQuest.Id).ToList();
|
||||
@@ -878,12 +878,12 @@ public class RepeatableQuestController(
|
||||
// Add "any" to pickup quest pool
|
||||
questPool.Pool.Pickup.Locations[ELocationName.any] = ["any"];
|
||||
|
||||
var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(
|
||||
var eliminationConfig = repeatableQuestHelper.GetEliminationConfigByPmcLevel(
|
||||
pmcLevel,
|
||||
repeatableConfig
|
||||
);
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
|
||||
_cloner,
|
||||
cloner,
|
||||
eliminationConfig.Targets
|
||||
);
|
||||
|
||||
@@ -929,7 +929,7 @@ public class RepeatableQuestController(
|
||||
{
|
||||
return new QuestTypePool
|
||||
{
|
||||
Types = _cloner.Clone(repeatableConfig.Types)!,
|
||||
Types = cloner.Clone(repeatableConfig.Types)!,
|
||||
Pool = new QuestPool
|
||||
{
|
||||
Exploration = new ExplorationPool
|
||||
@@ -959,20 +959,20 @@ public class RepeatableQuestController(
|
||||
var questCount = repeatableConfig.NumQuests;
|
||||
if (questCount == 0)
|
||||
{
|
||||
_logger.Warning($"Repeatable: {repeatableConfig.Name} quests have a count of 0");
|
||||
logger.Warning($"Repeatable: {repeatableConfig.Name} quests have a count of 0");
|
||||
}
|
||||
|
||||
// Add elite bonus to daily quests
|
||||
if (
|
||||
string.Equals(repeatableConfig.Name, "daily", StringComparison.OrdinalIgnoreCase)
|
||||
&& _profileHelper.HasEliteSkillLevel(
|
||||
&& profileHelper.HasEliteSkillLevel(
|
||||
SkillTypes.Charisma,
|
||||
fullProfile.CharacterData.PmcData
|
||||
)
|
||||
)
|
||||
// Elite charisma skill gives extra daily quest(s)
|
||||
{
|
||||
questCount += _databaseService
|
||||
questCount += databaseService
|
||||
.GetGlobals()
|
||||
.Configuration.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings.RepeatableQuestExtraCount.GetValueOrDefault(
|
||||
0
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Generators;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
@@ -43,20 +44,20 @@ public class TraderController(
|
||||
var traders = databaseService.GetTraders();
|
||||
foreach (var (traderId, trader) in traders)
|
||||
{
|
||||
switch (traderId)
|
||||
if (traderId == Traders.LIGHTHOUSEKEEPER || traderId == "ragfair")
|
||||
{
|
||||
case "ragfair":
|
||||
case Traders.LIGHTHOUSEKEEPER:
|
||||
continue;
|
||||
case Traders.FENCE:
|
||||
fenceBaseAssortGenerator.GenerateFenceBaseAssorts();
|
||||
fenceService.GenerateFenceAssorts();
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
if (traderId == Traders.FENCE)
|
||||
{
|
||||
fenceBaseAssortGenerator.GenerateFenceBaseAssorts();
|
||||
fenceService.GenerateFenceAssorts();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust price by traderPriceMultiplier config property
|
||||
if (TraderConfig.TraderPriceMultiplier != 1)
|
||||
if (!TraderConfig.TraderPriceMultiplier.Approx(1, 0.001))
|
||||
{
|
||||
AdjustTraderItemPrices(trader, TraderConfig.TraderPriceMultiplier);
|
||||
}
|
||||
@@ -101,19 +102,19 @@ public class TraderController(
|
||||
{
|
||||
foreach (var (traderId, trader) in databaseService.GetTables().Traders)
|
||||
{
|
||||
switch (traderId)
|
||||
if (traderId == Traders.LIGHTHOUSEKEEPER)
|
||||
{
|
||||
case Traders.LIGHTHOUSEKEEPER:
|
||||
continue;
|
||||
case Traders.FENCE:
|
||||
{
|
||||
if (fenceService.NeedsPartialRefresh())
|
||||
{
|
||||
fenceService.GenerateFenceAssorts();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
if (traderId == Traders.FENCE)
|
||||
{
|
||||
if (fenceService.NeedsPartialRefresh())
|
||||
{
|
||||
fenceService.GenerateFenceAssorts();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!traderAssortHelper.TraderAssortsHaveExpired(traderId))
|
||||
|
||||
@@ -55,5 +55,29 @@
|
||||
{
|
||||
return values.Select(v => v * factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to determine if one double is approx equal to another double
|
||||
/// </summary>
|
||||
/// <param name="value">Value to check</param>
|
||||
/// <param name="target">Target value</param>
|
||||
/// <param name="error">Error value</param>
|
||||
/// <returns>True if value is approx target within the error range</returns>
|
||||
public static bool Approx(this double value, double target, double error = 0.001d)
|
||||
{
|
||||
return Math.Abs(value - target) <= error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to determine if one float is approx equal to another float
|
||||
/// </summary>
|
||||
/// <param name="value">Value to check</param>
|
||||
/// <param name="target">Target value</param>
|
||||
/// <param name="error">Error value</param>
|
||||
/// <returns>True if value is approx target within the error range</returns>
|
||||
public static bool Approx(this float value, float target, float error = 0.001f)
|
||||
{
|
||||
return Math.Abs(value - target) <= error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ public class CompletionQuestGenerator(
|
||||
public RepeatableQuest? Generate(
|
||||
string sessionId,
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
@@ -269,8 +269,8 @@ public class CompletionQuestGenerator(
|
||||
|
||||
// Filter and concatenate the arrays according to current player level
|
||||
var itemIdsBlacklisted = itemBlacklist
|
||||
.Where(p => p.MinPlayerLevel <= pmcLevel)
|
||||
.SelectMany(x => x.ItemIds)
|
||||
.Where(blacklist => blacklist.MinPlayerLevel <= pmcLevel)
|
||||
.SelectMany(blacklist => blacklist.ItemIds)
|
||||
.ToHashSet(); //.Aggregate(List<ItemsBlacklist> , (a, p) => a.Concat(p.ItemIds) );
|
||||
|
||||
var filteredSelection = itemSelection
|
||||
|
||||
+9
-8
@@ -1,5 +1,6 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
@@ -54,8 +55,8 @@ public class EliminationQuestGenerator(
|
||||
Dictionary<ELocationName, List<string>> LocationsConfig,
|
||||
ProbabilityObjectArray<string, BossInfo> TargetsConfig,
|
||||
ProbabilityObjectArray<string, List<string>> BodyPartsConfig,
|
||||
ProbabilityObjectArray<string, List<string>> WeaponCategoryRequirementConfig,
|
||||
ProbabilityObjectArray<string, List<string>> WeaponRequirementConfig
|
||||
ProbabilityObjectArray<string, List<MongoId>> WeaponCategoryRequirementConfig,
|
||||
ProbabilityObjectArray<string, List<MongoId>> WeaponRequirementConfig
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
@@ -73,7 +74,7 @@ public class EliminationQuestGenerator(
|
||||
public RepeatableQuest? Generate(
|
||||
string sessionId,
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
@@ -213,7 +214,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Only allow a specific weapon requirement if a weapon category was not chosen
|
||||
string? allowedWeapon = null;
|
||||
MongoId? allowedWeapon = null;
|
||||
|
||||
var generateWeaponRequirement = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.WeaponRequirementChance
|
||||
@@ -340,11 +341,11 @@ public class EliminationQuestGenerator(
|
||||
cloner,
|
||||
eliminationConfig.BodyParts
|
||||
);
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(
|
||||
cloner,
|
||||
eliminationConfig.WeaponCategoryRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(
|
||||
cloner,
|
||||
eliminationConfig.WeaponRequirements
|
||||
);
|
||||
@@ -667,7 +668,7 @@ public class EliminationQuestGenerator(
|
||||
/// </summary>
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <returns>Weapon to use</returns>
|
||||
protected string? GenerateSpecificWeaponRequirement(
|
||||
protected MongoId GenerateSpecificWeaponRequirement(
|
||||
EliminationQuestGenerationData generationData
|
||||
)
|
||||
{
|
||||
@@ -681,7 +682,7 @@ public class EliminationQuestGenerator(
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-elimination-specific-weapon-null")
|
||||
);
|
||||
return null;
|
||||
return MongoId.Empty();
|
||||
}
|
||||
|
||||
var allowedWeapons = itemHelper.GetItemTplsOfBaseType(specificAllowedWeaponCategory[0]);
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
@@ -50,7 +51,7 @@ public class ExplorationQuestGenerator(
|
||||
public RepeatableQuest? Generate(
|
||||
string sessionId,
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
using SPTarkov.Server.Core.Models.Spt.Repeatable;
|
||||
|
||||
@@ -9,7 +10,7 @@ public interface IRepeatableQuestGenerator
|
||||
public RepeatableQuest? Generate(
|
||||
string sessionId,
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
);
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
@@ -27,7 +28,7 @@ public class PickupQuestGenerator(
|
||||
public RepeatableQuest? Generate(
|
||||
string sessionId,
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
|
||||
+9
-8
@@ -62,7 +62,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
public QuestRewards? GenerateReward(
|
||||
int pmcLevel,
|
||||
double difficulty,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
RepeatableQuestConfig repeatableConfig,
|
||||
BaseQuestConfig eliminationConfig,
|
||||
List<string>? rewardTplBlacklist = null
|
||||
@@ -707,7 +707,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="foundInRaid"> If generated Item is found in raid, default True </param>
|
||||
/// <returns> Object of "Reward"-item-type </returns>
|
||||
protected Reward GenerateItemReward(
|
||||
string tpl,
|
||||
MongoId tpl,
|
||||
double count,
|
||||
int index,
|
||||
bool foundInRaid = true
|
||||
@@ -740,13 +740,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
return questRewardItem;
|
||||
}
|
||||
|
||||
protected Reward GetMoneyReward(string traderId, double rewardRoubles, int rewardIndex)
|
||||
protected Reward GetMoneyReward(MongoId traderId, double rewardRoubles, int rewardIndex)
|
||||
{
|
||||
// Determine currency based on trader
|
||||
// PK and Fence use Euros, everyone else is Roubles
|
||||
var currency = traderId is Traders.PEACEKEEPER or Traders.FENCE
|
||||
? Money.EUROS
|
||||
: Money.ROUBLES;
|
||||
var currency =
|
||||
traderId == Traders.PEACEKEEPER || traderId == Traders.FENCE
|
||||
? Money.EUROS
|
||||
: Money.ROUBLES;
|
||||
|
||||
// Convert reward amount to Euros if necessary
|
||||
var rewardAmountToGivePlayer =
|
||||
@@ -766,7 +767,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// - Have a price greater than 0
|
||||
/// </summary>
|
||||
/// <param name="repeatableQuestConfig"> Config </param>
|
||||
/// <param name="tradderId"> ID of trader who will give reward to player </param>
|
||||
/// <param name="traderId"> ID of trader who will give reward to player </param>
|
||||
/// <returns> List of rewardable items [[_tpl, itemTemplate],...] </returns>
|
||||
public List<TemplateItem> GetRewardableItems(
|
||||
RepeatableQuestConfig repeatableQuestConfig,
|
||||
@@ -821,7 +822,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
string tpl,
|
||||
HashSet<MongoId> itemTplBlacklist,
|
||||
HashSet<MongoId> itemTypeBlacklist,
|
||||
List<string>? itemBaseWhitelist = null
|
||||
List<MongoId>? itemBaseWhitelist = null
|
||||
)
|
||||
{
|
||||
// Return early if not valid item to give as reward
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
@@ -14,6 +15,7 @@ namespace SPTarkov.Server.Core.Helpers;
|
||||
public class RepeatableQuestHelper(
|
||||
ISptLogger<RepeatableQuestHelper> logger,
|
||||
DatabaseService databaseService,
|
||||
ServerLocalisationService serverLocalisationService,
|
||||
HashUtil hashUtil,
|
||||
ICloner cloner,
|
||||
ConfigServer configServer
|
||||
@@ -43,7 +45,7 @@ public class RepeatableQuestHelper(
|
||||
/// <param name="playerGroup">Side to get the templates for</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public Dictionary<string, string> GetRepeatableQuestTemplatesByGroup(PlayerGroup playerGroup)
|
||||
public Dictionary<string, MongoId> GetRepeatableQuestTemplatesByGroup(PlayerGroup playerGroup)
|
||||
{
|
||||
var templates = QuestConfig.RepeatableQuestTemplates;
|
||||
|
||||
@@ -107,7 +109,7 @@ public class RepeatableQuestHelper(
|
||||
/// </returns>
|
||||
public RepeatableQuest? GenerateRepeatableTemplate(
|
||||
RepeatableQuestType type,
|
||||
string traderId,
|
||||
MongoId traderId,
|
||||
PlayerGroup playerGroup,
|
||||
string sessionId
|
||||
)
|
||||
@@ -116,8 +118,12 @@ public class RepeatableQuestHelper(
|
||||
|
||||
if (questData is null)
|
||||
{
|
||||
// TODO: Localize me!
|
||||
logger.Error($"No repeatable quest template found for type {type}");
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"repeatable-quest_helper_template_not_found",
|
||||
type
|
||||
)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -128,15 +134,20 @@ public class RepeatableQuestHelper(
|
||||
|
||||
if (templateName is null)
|
||||
{
|
||||
// TODO: Localize me!
|
||||
logger.Error($"Could not resolve template name for {type}");
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText(
|
||||
"repeatable-quest_helper_template_name_not_found",
|
||||
type
|
||||
)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
questData.TemplateId = typeIds[templateName];
|
||||
|
||||
// Force REF templates to use prapors ID - solves missing text issue
|
||||
var desiredTraderId = traderId == Traders.REF ? Traders.PRAPOR : traderId;
|
||||
// TODO: Get rid of this new mongoid generation, needs handled in `Traders` but can't be done right now.
|
||||
var desiredTraderId = traderId == Traders.REF ? new MongoId(Traders.PRAPOR) : traderId;
|
||||
|
||||
/* in locale, these id correspond to the text of quests
|
||||
template ids -pmc : Elimination = 616052ea3054fc0e2c24ce6e / Completion = 61604635c725987e815b1a46 / Exploration = 616041eb031af660100c9967
|
||||
@@ -185,8 +196,9 @@ public class RepeatableQuestHelper(
|
||||
|
||||
if (questData.QuestStatus is null)
|
||||
{
|
||||
// TODO: Localize me!
|
||||
logger.Error($"No quest status found for type {type}");
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("repeatable-quest_helper_no_status", type)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -206,8 +218,9 @@ public class RepeatableQuestHelper(
|
||||
{
|
||||
if (!QuestConfig.LocationIdMap.TryGetValue(locationKey, out var locationId))
|
||||
{
|
||||
// TODO - localize me!
|
||||
logger.Error($"No location in LocationIdMap found for key {locationKey}");
|
||||
logger.Error(
|
||||
serverLocalisationService.GetText("repeatable-quest_helper_no_loc_id", locationKey)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
|
||||
namespace SPTarkov.Server.Core.Models.Common;
|
||||
@@ -7,7 +8,13 @@ public readonly partial struct MongoId : IEquatable<MongoId>
|
||||
{
|
||||
private readonly string _stringId;
|
||||
|
||||
public MongoId(string id)
|
||||
public MongoId(
|
||||
string id,
|
||||
// TODO: TEMPORARY REMOVE ME WHEN DONE!!!!
|
||||
[CallerFilePath] string callerFilePath = "",
|
||||
[CallerMemberName] string methodName = "",
|
||||
[CallerLineNumber] int callerLineNumber = 0
|
||||
)
|
||||
{
|
||||
// This is temporary, otherwise item buying is broken as when LINQ searches for string id's it's possible null is passed
|
||||
if (id == null)
|
||||
@@ -18,13 +25,15 @@ public readonly partial struct MongoId : IEquatable<MongoId>
|
||||
if (id.Length != 24)
|
||||
{
|
||||
// TODO: Items.json root item has an empty parentId property
|
||||
Console.WriteLine($"Critical MongoId error: Incorrect length. id: {id}");
|
||||
Console.WriteLine(
|
||||
$"Critical MongoId error [{callerFilePath}::{methodName} L{callerLineNumber}]: Incorrect length. id: {id}"
|
||||
);
|
||||
}
|
||||
|
||||
if (!IsValidMongoId(id))
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"Critical MongoId error: Incorrect format. Must be a hexadecimal [a-f0-9] of 24 characters. id: {id}"
|
||||
$"Critical MongoId error [{callerFilePath}::{methodName} L{callerLineNumber}]: Incorrect format. Must be a hexadecimal [a-f0-9] of 24 characters. id: {id}"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
|
||||
namespace SPTarkov.Server.Core.Models.Enums;
|
||||
|
||||
public static class Traders
|
||||
{
|
||||
public const string PRAPOR = "54cb50c76803fa8b248b4571";
|
||||
public const string THERAPIST = "54cb57776803fa99248b456e";
|
||||
public const string FENCE = "579dc571d53a0658a154fbec";
|
||||
public const string SKIER = "58330581ace78e27b8b10cee";
|
||||
public const string PEACEKEEPER = "5935c25fb3acc3127c3d8cd9";
|
||||
public const string MECHANIC = "5a7c2eca46aef81a7ca2145d";
|
||||
public const string RAGMAN = "5ac3b934156ae10c4430e83c";
|
||||
public const string JAEGER = "5c0647fdd443bc2504c2d371";
|
||||
public const string LIGHTHOUSEKEEPER = "638f541a29ffd1183d187f57";
|
||||
public const string BTR = "656f0f98d80a697f855d34b1";
|
||||
public const string REF = "6617beeaa9cfa777ca915b7c";
|
||||
public static MongoId PRAPOR = new("54cb50c76803fa8b248b4571");
|
||||
public static MongoId THERAPIST = new("54cb57776803fa99248b456e");
|
||||
public static MongoId FENCE = new("579dc571d53a0658a154fbec");
|
||||
public static MongoId SKIER = new("58330581ace78e27b8b10cee");
|
||||
public static MongoId PEACEKEEPER = new("5935c25fb3acc3127c3d8cd9");
|
||||
public static MongoId MECHANIC = new("5a7c2eca46aef81a7ca2145d");
|
||||
public static MongoId RAGMAN = new("5ac3b934156ae10c4430e83c");
|
||||
public static MongoId JAEGER = new("5c0647fdd443bc2504c2d371");
|
||||
public static MongoId LIGHTHOUSEKEEPER = new("638f541a29ffd1183d187f57");
|
||||
public static MongoId BTR = new("656f0f98d80a697f855d34b1");
|
||||
public static MongoId REF = new("6617beeaa9cfa777ca915b7c");
|
||||
}
|
||||
|
||||
@@ -21,25 +21,25 @@ public record QuestConfig : BaseConfig
|
||||
/// Collection of quests by id only available to usec
|
||||
/// </summary>
|
||||
[JsonPropertyName("usecOnlyQuests")]
|
||||
public required HashSet<string> UsecOnlyQuests { get; set; }
|
||||
public required HashSet<MongoId> UsecOnlyQuests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Collection of quests by id only available to bears
|
||||
/// </summary>
|
||||
[JsonPropertyName("bearOnlyQuests")]
|
||||
public required HashSet<string> BearOnlyQuests { get; set; }
|
||||
public required HashSet<MongoId> BearOnlyQuests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quests that the keyed game version do not see/access
|
||||
/// </summary>
|
||||
[JsonPropertyName("profileBlacklist")]
|
||||
public required Dictionary<string, HashSet<string>> ProfileBlacklist { get; set; }
|
||||
public required Dictionary<string, HashSet<MongoId>> ProfileBlacklist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// key=questid, gameversions that can see/access quest
|
||||
/// </summary>
|
||||
[JsonPropertyName("profileWhitelist")]
|
||||
public required Dictionary<string, HashSet<string>> ProfileWhitelist { get; set; }
|
||||
public required Dictionary<MongoId, HashSet<string>> ProfileWhitelist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds repeatable quest template ids for pmc's and scav's
|
||||
@@ -57,7 +57,7 @@ public record QuestConfig : BaseConfig
|
||||
/// Collection of event quest data keyed by quest id.
|
||||
/// </summary>
|
||||
[JsonPropertyName("eventQuests")]
|
||||
public required Dictionary<string, EventQuestData> EventQuests { get; set; }
|
||||
public required Dictionary<MongoId, EventQuestData> EventQuests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of repeatable quest configs for; daily, weekly, and daily scav.
|
||||
@@ -82,14 +82,14 @@ public record RepeatableQuestTemplates
|
||||
/// Keys: elimination, completion, exploration
|
||||
/// </summary>
|
||||
[JsonPropertyName("pmc")]
|
||||
public required Dictionary<string, string> Pmc { get; set; }
|
||||
public required Dictionary<string, MongoId> Pmc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Scav repeatable quest template ids keyed by type of quest
|
||||
/// Keys: elimination, completion, exploration, pickup
|
||||
/// </summary>
|
||||
[JsonPropertyName("scav")]
|
||||
public required Dictionary<string, string> Scav { get; set; }
|
||||
public required Dictionary<string, MongoId> Scav { get; set; }
|
||||
}
|
||||
|
||||
public record EventQuestData
|
||||
@@ -138,7 +138,7 @@ public record RepeatableQuestConfig
|
||||
/// Id for type of repeatable quest
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
public required MongoId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name for repeatable quest type
|
||||
@@ -314,7 +314,7 @@ public record TraderWhitelist
|
||||
/// Trader Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("traderId")]
|
||||
public required string TraderId { get; set; }
|
||||
public required MongoId TraderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name
|
||||
@@ -332,7 +332,7 @@ public record TraderWhitelist
|
||||
/// Item categories that the reward can be
|
||||
/// </summary>
|
||||
[JsonPropertyName("rewardBaseWhitelist")]
|
||||
public required List<string> RewardBaseWhitelist { get; set; }
|
||||
public required List<MongoId> RewardBaseWhitelist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can this reward be a weapon?
|
||||
@@ -614,14 +614,14 @@ public record EliminationConfig : BaseQuestConfig
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponCategoryRequirements")]
|
||||
public required List<
|
||||
ProbabilityObject<string, List<string>>
|
||||
ProbabilityObject<string, List<MongoId>>
|
||||
> WeaponCategoryRequirements { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a weapon requirement is chosen, pick from these weapons
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponRequirements")]
|
||||
public required List<ProbabilityObject<string, List<string>>> WeaponRequirements { get; set; }
|
||||
public required List<ProbabilityObject<string, List<MongoId>>> WeaponRequirements { get; set; }
|
||||
}
|
||||
|
||||
public record BaseQuestConfig
|
||||
|
||||
@@ -2,6 +2,7 @@ using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Generators;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Eft.Match;
|
||||
@@ -580,7 +581,7 @@ public class LocationLifecycleService
|
||||
pmcData.CarExtractCounts[extractName]
|
||||
);
|
||||
|
||||
const string fenceId = Traders.FENCE;
|
||||
var fenceId = Traders.FENCE;
|
||||
pmcData.TradersInfo[fenceId].Standing = newFenceStanding;
|
||||
|
||||
// Check if new standing has leveled up trader
|
||||
@@ -618,7 +619,7 @@ public class LocationLifecycleService
|
||||
pmcData.CoopExtractCounts[extractName]
|
||||
);
|
||||
|
||||
const string fenceId = Traders.FENCE;
|
||||
var fenceId = Traders.FENCE;
|
||||
pmcData.TradersInfo[fenceId].Standing = newFenceStanding;
|
||||
|
||||
// Check if new standing has leveled up trader
|
||||
@@ -649,7 +650,7 @@ public class LocationLifecycleService
|
||||
double extractCount
|
||||
)
|
||||
{
|
||||
const string fenceId = Traders.FENCE;
|
||||
var fenceId = Traders.FENCE;
|
||||
var fenceStanding = pmcData.TradersInfo[fenceId].Standing;
|
||||
|
||||
// get standing after taking extract x times, x.xx format, gain from extract can be no smaller than 0.01
|
||||
@@ -946,7 +947,7 @@ public class LocationLifecycleService
|
||||
// Must occur AFTER experience is set and stats copied over
|
||||
serverPmcProfile.Stats.Eft.TotalSessionExperience = 0;
|
||||
|
||||
const string fenceId = Traders.FENCE;
|
||||
var fenceId = Traders.FENCE;
|
||||
|
||||
// Clamp fence standing
|
||||
var currentFenceStanding = postRaidProfile.TradersInfo[fenceId].Standing;
|
||||
|
||||
Reference in New Issue
Block a user