Partially implemented RepeatableQuestController

This commit is contained in:
Chomp
2025-01-16 21:21:23 +00:00
parent 6a13e91839
commit 3c0c94245c
3 changed files with 235 additions and 10 deletions
+215 -7
View File
@@ -7,6 +7,7 @@ using Core.Models.Spt.Config;
using Core.Models.Spt.Repeatable;
using Core.Generators;
using Core.Helpers;
using Core.Models.Enums;
using Core.Models.Utils;
using Core.Routers;
using Core.Servers;
@@ -32,9 +33,10 @@ public class RepeatableQuestController
protected RepeatableQuestGenerator _repeatableQuestGenerator;
protected RepeatableQuestHelper _repeatableQuestHelper;
protected QuestHelper _questHelper;
protected DatabaseService _databaseService;
protected ConfigServer _configServer;
protected ICloner _cloner;
private readonly QuestConfig _questConfig;
protected QuestConfig _questConfig;
public RepeatableQuestController(
ISptLogger<RepeatableQuestChangeRequest> logger,
@@ -50,6 +52,7 @@ public class RepeatableQuestController
RepeatableQuestGenerator repeatableQuestGenerator,
RepeatableQuestHelper repeatableQuestHelper,
QuestHelper questHelper,
DatabaseService databaseService,
ConfigServer configServer,
ICloner cloner)
{
@@ -66,6 +69,7 @@ public class RepeatableQuestController
_repeatableQuestGenerator = repeatableQuestGenerator;
_repeatableQuestHelper = repeatableQuestHelper;
_questHelper = questHelper;
_databaseService = databaseService;
_configServer = configServer;
_cloner = cloner;
@@ -190,26 +194,230 @@ public class RepeatableQuestController
private PmcDataRepeatableQuest GetRepeatableQuestSubTypeFromProfile(RepeatableQuestConfig repeatableConfig, PmcData pmcData)
{
throw new NotImplementedException();
// Get from profile, add if missing
var repeatableQuestDetails = pmcData.RepeatableQuests.FirstOrDefault(
(repeatable) => repeatable.Name == repeatableConfig.Name);
if (repeatableQuestDetails is not null)
{
// Not in profile, generate
var hasAccess = _profileHelper.HasAccessToRepeatableFreeRefreshSystem(pmcData);
repeatableQuestDetails = new PmcDataRepeatableQuest(){
Id = repeatableConfig.Id,
Name= repeatableConfig.Name,
ActiveQuests= [],
InactiveQuests= [],
EndTime= 0,
ChangeRequirement= { },
FreeChanges= hasAccess? repeatableConfig.FreeChanges: 0,
FreeChangesAvailable= hasAccess? repeatableConfig.FreeChangesAvailable: 0,
};
// Add base object that holds repeatable data to profile
pmcData.RepeatableQuests.Add(repeatableQuestDetails);
}
return repeatableQuestDetails;
}
private bool CanProfileAccessRepeatableQuests(RepeatableQuestConfig repeatableConfig, PmcData pmcData)
{
throw new NotImplementedException();
// PMC and daily quests not unlocked yet
if (repeatableConfig.Side == "Pmc" && !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig))
{
return false;
}
// Scav and daily quests not unlocked yet
if (repeatableConfig.Side == "Scav" && !PlayerHasDailyScavQuestsUnlocked(pmcData))
{
_logger.Debug("Daily scav quests still locked, Intel center not built");
return false;
}
return true;
}
/**
* Does player have daily pmc quests unlocked
* @param pmcData Player profile to check
* @param repeatableConfig Config of daily type to check
* @returns True if unlocked
*/
private bool PlayerHasDailyPmcQuestsUnlocked(PmcData pmcData, RepeatableQuestConfig repeatableConfig)
{
return pmcData.Info.Level >= repeatableConfig.MinPlayerLevel;
}
/**
* Does player have daily scav quests unlocked
* @param pmcData Player profile to check
* @returns True if unlocked
*/
private bool PlayerHasDailyScavQuestsUnlocked(PmcData pmcData)
{
return (
pmcData?.Hideout?.Areas?.FirstOrDefault((hideoutArea) => hideoutArea.Type == HideoutAreas.INTEL_CENTER)?.Level >= 1
);
}
private void ProcessExpiredQuests(PmcDataRepeatableQuest generatedRepeatables, PmcData pmcData)
{
throw new NotImplementedException();
var questsToKeep = new List<RepeatableQuest>();
foreach (var activeQuest in generatedRepeatables.ActiveQuests) {
var questStatusInProfile = pmcData.Quests.FirstOrDefault((quest) => quest.QId == activeQuest.Id);
if (questStatusInProfile is null)
{
continue;
}
// Keep finished quests in list so player can hand in
if (questStatusInProfile.Status == QuestStatusEnum.AvailableForFinish)
{
questsToKeep.Add(activeQuest);
_logger.Debug($"Keeping repeatable quest: ${ activeQuest.Id} in activeQuests since it is available to hand in");
continue;
}
// Clean up quest-related counters being left in profile
_profileFixerService.RemoveDanglingConditionCounters(pmcData);
// Remove expired quest from pmc.quest array
pmcData.Quests = pmcData.Quests.Where((quest) => quest.QId != activeQuest.Id).ToList();
// Store in inactive array
generatedRepeatables.InactiveQuests.Add(activeQuest);
}
generatedRepeatables.ActiveQuests = questsToKeep;
}
private QuestTypePool GenerateQuestPool(RepeatableQuestConfig repeatableConfig, double? infoLevel)
private QuestTypePool GenerateQuestPool(RepeatableQuestConfig repeatableConfig, int? pmcLevel)
{
throw new NotImplementedException();
var questPool = CreateBaseQuestPool(repeatableConfig);
// Get the allowed locations based on the PMC's level
var locations = GetAllowedLocationsForPmcLevel(repeatableConfig.Locations, pmcLevel.Value);
// Populate Exploration and Pickup quest locations
foreach (var location in locations) {
if (location.Key != ELocationName.any)
{
questPool.Pool.Exploration.Locations[location.Key] = location.Value;
questPool.Pool.Pickup.Locations[location.Key] = location.Value;
}
}
// Add "any" to pickup quest pool
questPool.Pool.Pickup.Locations[ELocationName.any] = ["any"];
var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel.Value, repeatableConfig);
var targetsConfig = _repeatableQuestHelper.ProbabilityObjectArray<string, BossInfo>(eliminationConfig.Targets);
// Populate Elimination quest targets and their locations
foreach (var target in targetsConfig) {
// Target is boss
//if (target.isBoss)
//{
// questPool.Pool.Elimination.Targets[targetKey] = new { locations: ["any"] };
//}
//else
//{
// // Non-boss targets
// var possibleLocations = locations;
// var allowedLocations =
// targetKey == "Savage"
// ? possibleLocations.filter((location) => location != "laboratory") // Exclude labs for Savage targets.
// : possibleLocations;
// questPool.Pool.Elimination.Targets[targetKey] = new { Locations = allowedLocations };
//}
_logger.Error("NOT IMPLEMENTED - GenerateQuestPool");
}
return questPool;
}
private QuestTypePool CreateBaseQuestPool(RepeatableQuestConfig repeatableConfig)
{
return new QuestTypePool
{
Types = _cloner.Clone(repeatableConfig.Types),
Pool = new QuestPool
{
Exploration = new ExplorationPool
{
Locations = new Dictionary<ELocationName, List<string>>()
},
Elimination = new EliminationPool
{
Targets = new EliminationTargetPool()
},
Pickup = new ExplorationPool
{
Locations = new Dictionary<ELocationName, List<string>>()
}
},
};
}
private Dictionary<ELocationName, List<string>> GetAllowedLocationsForPmcLevel(Dictionary<ELocationName, List<string>> locations, int pmcLevel)
{
var allowedLocation = new Dictionary<ELocationName, List<string>>();
foreach (var locationKvP in locations) {
var locationNames = new List<string>();
foreach (var locationName in locationKvP.Value) {
if (IsPmcLevelAllowedOnLocation(locationName, pmcLevel))
{
locationNames.Add(locationName);
}
}
if (locationNames.Count > 0)
{
allowedLocation[locationKvP.Key] = locationNames;
}
}
return allowedLocation;
}
/**
* Return true if the given pmcLevel is allowed on the given location
* @param location The location name to check
* @param pmcLevel The level of the pmc
* @returns True if the given pmc level is allowed to access the given location
*/
protected bool IsPmcLevelAllowedOnLocation(string location, int pmcLevel) {
// All PMC levels are allowed for 'any' location requirement
if (location == ELocationName.any.ToString()) {
return true;
}
var locationBase = _databaseService.GetLocation(location.ToLower())?.Base;
if (locationBase is not null) {
return true;
}
return pmcLevel <= locationBase.RequiredPlayerLevelMax && pmcLevel >= locationBase.RequiredPlayerLevelMin;
}
private int GetQuestCount(RepeatableQuestConfig repeatableConfig, PmcData pmcData)
{
throw new NotImplementedException();
if (repeatableConfig.Name.ToLower() == "daily"
&& _profileHelper.HasEliteSkillLevel(SkillTypes.Charisma, pmcData)
)
{
// Elite charisma skill gives extra daily quest(s)
return (repeatableConfig.NumQuests +
_databaseService.GetGlobals().Configuration.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
.RepeatableQuestExtraCount.GetValueOrDefault(0)
);
}
return repeatableConfig.NumQuests;
}
}
+19 -2
View File
@@ -1,11 +1,20 @@
using Core.Annotations;
using Core.Annotations;
using Core.Models.Spt.Config;
using Core.Models.Utils;
namespace Core.Helpers;
[Injectable]
public class RepeatableQuestHelper
{
protected ISptLogger<RepeatableQuestHelper> _logger;
public RepeatableQuestHelper(
ISptLogger<RepeatableQuestHelper> logger)
{
_logger = logger;
}
/// <summary>
/// Get the relevant elimination config based on the current players PMC level
/// </summary>
@@ -19,8 +28,16 @@ public class RepeatableQuestHelper
throw new NotImplementedException();
}
public object ProbabilityObjectArray<K, V>(object configArrayInput) // TODO: ProbabilityObjectArray<K, V> for return type , param type was List<ProbabilityObject<K, V>>
public Dictionary<K, ProbabilityData<V>> ProbabilityObjectArray<K, V>(object configArrayInput) // TODO: ProbabilityObjectArray<K, V> for return type , param type was List<ProbabilityObject<K, V>>
{
_logger.Error("Fuck this in particular, go look up ProbabilityObjectArray in node server, candidate for rewrite");
throw new NotImplementedException();
var x = new Dictionary<K, ProbabilityData<V>>();
}
public class ProbabilityData<T>
{
public int RelativeProbability { get; set; }
public T Data { get; set; }
}
}
+1 -1
View File
@@ -3662,7 +3662,7 @@ public class EliteBonusSettings
public double? FenceStandingLossDiscount { get; set; }
[JsonPropertyName("RepeatableQuestExtraCount")]
public double? RepeatableQuestExtraCount { get; set; }
public int? RepeatableQuestExtraCount { get; set; }
[JsonPropertyName("ScavCaseDiscount")]
public double? ScavCaseDiscount { get; set; }