Quest config nullability and documentation (Part 2) (#415)
* Make Interp1 generic * Finish quest controller nullability fixes * More model and nullability improvements and fixes * Rename `specificLocationChance` * rename `bodyPartChance` * finish comments
This commit is contained in:
@@ -440,7 +440,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.15,
|
||||
"bodyPartChance": 15,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -463,7 +463,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0,
|
||||
"specificLocationChance": 0,
|
||||
"distLocationBlacklist": ["laboratory", "factory4_day", "factory4_night"],
|
||||
"distProb": 0.15,
|
||||
"maxDist": 75,
|
||||
@@ -666,7 +666,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.15,
|
||||
"bodyPartChance": 15,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -689,7 +689,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.15,
|
||||
"specificLocationChance": 15,
|
||||
"distLocationBlacklist": ["laboratory", "factory4_day", "factory4_night"],
|
||||
"distProb": 0.25,
|
||||
"maxDist": 100,
|
||||
@@ -887,7 +887,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.15,
|
||||
"bodyPartChance": 15,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -910,7 +910,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.15,
|
||||
"specificLocationChance": 15,
|
||||
"distLocationBlacklist": ["laboratory", "factory4_day", "factory4_night"],
|
||||
"distProb": 0.25,
|
||||
"maxDist": 100,
|
||||
@@ -1214,7 +1214,7 @@
|
||||
"maxRequestedBulletAmount": 60,
|
||||
"useWhitelist": true,
|
||||
"useBlacklist": false,
|
||||
"requiredItemsAreFiR": true,
|
||||
"requiredItemsAreFiR": true,
|
||||
"requiredItemMinDurabilityMinMax": {
|
||||
"min": 60,
|
||||
"max": 80
|
||||
@@ -1317,7 +1317,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.15,
|
||||
"bodyPartChance": 15,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -1340,7 +1340,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.15,
|
||||
"specificLocationChance": 15,
|
||||
"distLocationBlacklist": ["laboratory", "factory4_day", "factory4_night"],
|
||||
"distProb": 0.15,
|
||||
"maxDist": 75,
|
||||
@@ -1538,7 +1538,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.15,
|
||||
"bodyPartChance": 15,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -1561,7 +1561,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.15,
|
||||
"specificLocationChance": 15,
|
||||
"distLocationBlacklist": ["laboratory", "factory4_day", "factory4_night"],
|
||||
"distProb": 0.15,
|
||||
"maxDist": 75,
|
||||
@@ -1759,7 +1759,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.15,
|
||||
"bodyPartChance": 15,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -1782,7 +1782,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.15,
|
||||
"specificLocationChance": 15,
|
||||
"distLocationBlacklist": ["laboratory", "factory4_day", "factory4_night"],
|
||||
"distProb": 0.15,
|
||||
"maxDist": 100,
|
||||
@@ -1927,6 +1927,7 @@
|
||||
},
|
||||
"traderWhitelist": [
|
||||
{
|
||||
"name": "fence",
|
||||
"traderId": "579dc571d53a0658a154fbec",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination", "Pickup"],
|
||||
"rewardBaseWhitelist": [
|
||||
@@ -2046,7 +2047,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.2,
|
||||
"bodyPartChance": 20,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -2069,7 +2070,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.15,
|
||||
"specificLocationChance": 15,
|
||||
"distLocationBlacklist": ["laboratory"],
|
||||
"distProb": 0.2,
|
||||
"maxDist": 75,
|
||||
@@ -2183,7 +2184,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"bodyPartProb": 0.4,
|
||||
"bodyPartChance": 40,
|
||||
"bodyParts": [
|
||||
{
|
||||
"key": "Head",
|
||||
@@ -2206,7 +2207,7 @@
|
||||
"data": ["LeftLeg", "RightLeg"]
|
||||
}
|
||||
],
|
||||
"specificLocationProb": 0.25,
|
||||
"specificLocationChance": 25,
|
||||
"distLocationBlacklist": ["laboratory"],
|
||||
"distProb": 0.25,
|
||||
"maxDist": 75,
|
||||
|
||||
@@ -810,7 +810,7 @@ public class RepeatableQuestController(
|
||||
foreach (var target in targetsConfig)
|
||||
{
|
||||
// Target is boss
|
||||
if (target.Data.IsBoss.GetValueOrDefault(false))
|
||||
if (target.Data?.IsBoss ?? false)
|
||||
{
|
||||
questPool.Pool.Elimination.Targets.Add(
|
||||
target.Key,
|
||||
|
||||
@@ -195,7 +195,7 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
if (
|
||||
targetsConfig.Count == 0
|
||||
|| targetsConfig.All(x => x.Data.IsBoss.GetValueOrDefault(false))
|
||||
|| targetsConfig.All(x => x.Data?.IsBoss ?? false)
|
||||
)
|
||||
{
|
||||
// There are no more targets left for elimination; delete it as a possible quest type
|
||||
@@ -217,7 +217,7 @@ public class RepeatableQuestGenerator(
|
||||
if (
|
||||
locations.Contains("any")
|
||||
&& (
|
||||
eliminationConfig.SpecificLocationProbability < rand.NextDouble()
|
||||
_randomUtil.GetChance100(eliminationConfig.SpecificLocationChance)
|
||||
|| locations.Count <= 1
|
||||
)
|
||||
)
|
||||
@@ -274,7 +274,7 @@ public class RepeatableQuestGenerator(
|
||||
// draw the target body part and calculate the difficulty factor
|
||||
var bodyPartsToClient = new List<string>();
|
||||
var bodyPartDifficulty = 0d;
|
||||
if (eliminationConfig.BodyPartProbability > rand.NextDouble())
|
||||
if (_randomUtil.GetChance100(eliminationConfig.BodyPartChance))
|
||||
{
|
||||
// if we add a bodyPart condition, we draw randomly one or two parts
|
||||
// each bodyPart of the BODYPARTS ProbabilityObjectArray includes the string(s) which need to be presented to the client in ProbabilityObjectArray.data
|
||||
@@ -307,7 +307,7 @@ public class RepeatableQuestGenerator(
|
||||
locationKey
|
||||
);
|
||||
|
||||
if (targetsConfig.Data(botTypeToEliminate).IsBoss.GetValueOrDefault(false))
|
||||
if (targetsConfig.Data(botTypeToEliminate)?.IsBoss ?? false)
|
||||
{
|
||||
// Get all boss spawn information
|
||||
var bossSpawns = _databaseService
|
||||
@@ -330,11 +330,11 @@ public class RepeatableQuestGenerator(
|
||||
);
|
||||
// if the boss spawns on nom-blacklisted locations and the current location is allowed we can generate a distance kill requirement
|
||||
isDistanceRequirementAllowed =
|
||||
isDistanceRequirementAllowed && allowedSpawns.Count() > 0;
|
||||
isDistanceRequirementAllowed && allowedSpawns.Any();
|
||||
}
|
||||
|
||||
if (
|
||||
eliminationConfig.DistanceProbability > rand.NextDouble()
|
||||
_randomUtil.GetChance100(eliminationConfig.DistanceProbability)
|
||||
&& isDistanceRequirementAllowed
|
||||
)
|
||||
{
|
||||
@@ -344,7 +344,6 @@ public class RepeatableQuestGenerator(
|
||||
Math.Abs(rand.NextDouble() - rand.NextDouble())
|
||||
* (1 + eliminationConfig.MaxDistance - eliminationConfig.MinDistance)
|
||||
+ eliminationConfig.MinDistance
|
||||
?? 0
|
||||
);
|
||||
|
||||
distance = (int)Math.Ceiling((decimal)(distance / 5)) * 5;
|
||||
@@ -354,7 +353,7 @@ public class RepeatableQuestGenerator(
|
||||
}
|
||||
|
||||
string? allowedWeaponsCategory = null;
|
||||
if (eliminationConfig.WeaponCategoryRequirementProbability > rand.NextDouble())
|
||||
if (_randomUtil.GetChance100(eliminationConfig.WeaponCategoryRequirementProbability))
|
||||
{
|
||||
// Filter out close range weapons from far distance requirement
|
||||
if (distance > 50)
|
||||
@@ -411,9 +410,9 @@ public class RepeatableQuestGenerator(
|
||||
var maxDifficulty = DifficultyWeighing(1, 1, 1, 1, 1);
|
||||
var curDifficulty = DifficultyWeighing(
|
||||
targetDifficulty.Value / maxTargetDifficulty,
|
||||
bodyPartDifficulty / maxBodyPartsDifficulty.Value,
|
||||
bodyPartDifficulty / maxBodyPartsDifficulty,
|
||||
distanceDifficulty / maxDistDifficulty,
|
||||
killDifficulty / maxKillDifficulty.Value,
|
||||
killDifficulty / maxKillDifficulty,
|
||||
allowedWeaponsCategory is not null || allowedWeapon is not null ? 1 : 0
|
||||
);
|
||||
|
||||
@@ -485,24 +484,24 @@ public class RepeatableQuestGenerator(
|
||||
EliminationConfig eliminationConfig
|
||||
)
|
||||
{
|
||||
if (targetsConfig.Data(targetKey).IsBoss.GetValueOrDefault(false))
|
||||
if (targetsConfig.Data(targetKey)?.IsBoss ?? false)
|
||||
{
|
||||
return _randomUtil.RandInt(
|
||||
eliminationConfig.MinBossKills.Value,
|
||||
eliminationConfig.MinBossKills,
|
||||
eliminationConfig.MaxBossKills + 1
|
||||
);
|
||||
}
|
||||
|
||||
if (targetsConfig.Data(targetKey).IsPmc.GetValueOrDefault(false))
|
||||
if (targetsConfig.Data(targetKey)?.IsPmc ?? false)
|
||||
{
|
||||
return _randomUtil.RandInt(
|
||||
eliminationConfig.MinPmcKills.Value,
|
||||
eliminationConfig.MinPmcKills,
|
||||
eliminationConfig.MaxPmcKills + 1
|
||||
);
|
||||
}
|
||||
|
||||
return _randomUtil.RandInt(
|
||||
eliminationConfig.MinKills.Value,
|
||||
eliminationConfig.MinKills,
|
||||
eliminationConfig.MaxKills + 1
|
||||
);
|
||||
}
|
||||
@@ -620,12 +619,7 @@ public class RepeatableQuestGenerator(
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
{
|
||||
var completionConfig = repeatableConfig?.QuestConfig?.Completion;
|
||||
if (completionConfig is null)
|
||||
{
|
||||
_logger.Error("Unable to generate Completion quest, no Completion config found");
|
||||
return null;
|
||||
}
|
||||
var completionConfig = repeatableConfig.QuestConfig.Completion;
|
||||
var levelsConfig = repeatableConfig.RewardScaling.Levels;
|
||||
var roublesConfig = repeatableConfig.RewardScaling.Roubles;
|
||||
|
||||
@@ -645,7 +639,7 @@ public class RepeatableQuestGenerator(
|
||||
// Be fair, don't value the items be more expensive than the reward
|
||||
var multiplier = _randomUtil.GetDouble(0.5, 1);
|
||||
var roublesBudget = Math.Floor(
|
||||
(double)(_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier)
|
||||
_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier
|
||||
);
|
||||
roublesBudget = Math.Max(roublesBudget, 5000d);
|
||||
var itemSelection = itemsToRetrievePool
|
||||
@@ -654,7 +648,7 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
// We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as
|
||||
// [{"minPlayerLevel": 1, "itemIds": ["id1",...]}, {"minPlayerLevel": 15, "itemIds": ["id3",...]}]
|
||||
if (repeatableConfig.QuestConfig.Completion.UseWhitelist.GetValueOrDefault(false))
|
||||
if (repeatableConfig.QuestConfig.Completion.UseWhitelist)
|
||||
{
|
||||
var itemWhitelist = _databaseService
|
||||
.GetTemplates()
|
||||
@@ -678,7 +672,7 @@ public class RepeatableQuestGenerator(
|
||||
// var missing = itemIdsWhitelisted.filter(l => !flatList.includes(l));
|
||||
}
|
||||
|
||||
if (repeatableConfig.QuestConfig.Completion.UseBlacklist.GetValueOrDefault(false))
|
||||
if (repeatableConfig.QuestConfig.Completion.UseBlacklist)
|
||||
{
|
||||
var itemBlacklist = _databaseService
|
||||
.GetTemplates()
|
||||
@@ -714,7 +708,7 @@ public class RepeatableQuestGenerator(
|
||||
// Store the indexes of items we are asking player to supply
|
||||
var distinctItemsToRetrieveCount = _randomUtil.GetInt(
|
||||
1,
|
||||
completionConfig.UniqueItemCount.Value
|
||||
completionConfig.UniqueItemCount
|
||||
);
|
||||
var chosenRequirementItemsTpls = new List<string>();
|
||||
var usedItemIndexes = new HashSet<int>();
|
||||
@@ -753,8 +747,8 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
var tplChosen = itemSelection[chosenItemIndex];
|
||||
var itemPrice = _itemHelper.GetItemPrice(tplChosen).Value;
|
||||
var minValue = completionConfig.MinimumRequestedAmount.Value;
|
||||
var maxValue = completionConfig.MaximumRequestedAmount.Value;
|
||||
var minValue = completionConfig.MinimumRequestedAmount;
|
||||
var maxValue = completionConfig.MaximumRequestedAmount;
|
||||
|
||||
var value = minValue;
|
||||
|
||||
@@ -928,7 +922,7 @@ public class RepeatableQuestGenerator(
|
||||
{
|
||||
var explorationConfig = repeatableConfig.QuestConfig.Exploration;
|
||||
var requiresSpecificExtract =
|
||||
_randomUtil.Random.Next()
|
||||
_randomUtil.Random.NextDouble()
|
||||
< repeatableConfig.QuestConfig.Exploration.SpecificExits.Probability;
|
||||
|
||||
if (questTypePool.Pool.Exploration.Locations.Count == 0)
|
||||
@@ -1026,7 +1020,7 @@ public class RepeatableQuestGenerator(
|
||||
var difficulty = _mathUtil.MapToRange(
|
||||
numExtracts,
|
||||
1,
|
||||
explorationConfig.MaximumExtracts.Value,
|
||||
explorationConfig.MaximumExtracts,
|
||||
0.2,
|
||||
1
|
||||
);
|
||||
|
||||
@@ -58,7 +58,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="eliminationConfig"> Base Quest config</param>
|
||||
/// <param name="rewardTplBlacklist"> Optional: list of tpls to NOT use when picking a reward </param>
|
||||
/// <returns> QuestRewards </returns>
|
||||
public QuestRewards GenerateReward(
|
||||
public QuestRewards? GenerateReward(
|
||||
int pmcLevel,
|
||||
double difficulty,
|
||||
string traderId,
|
||||
@@ -122,13 +122,17 @@ public class RepeatableQuestRewardGenerator(
|
||||
var traderWhitelistDetails = repeatableConfig.TraderWhitelist.FirstOrDefault(
|
||||
traderWhitelist => traderWhitelist.TraderId == traderId
|
||||
);
|
||||
|
||||
if (traderWhitelistDetails is null)
|
||||
{
|
||||
_logger.Error($"Cound not find trader id: {traderId} in whitelist");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
traderWhitelistDetails?.RewardCanBeWeapon
|
||||
?? (
|
||||
false
|
||||
&& _randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent ?? 0)
|
||||
traderWhitelistDetails.RewardCanBeWeapon &&
|
||||
_randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent)
|
||||
)
|
||||
)
|
||||
{
|
||||
var chosenWeapon = GetRandomWeaponPresetWithinBudget(
|
||||
itemRewardBudget.Value,
|
||||
@@ -365,7 +369,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
return _randomUtil.RandInt(
|
||||
1,
|
||||
(int)Math.Round(_mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig) ?? 0) + 1
|
||||
(int)Math.Round(_mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig)) + 1
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ public record RepeatableQuestConfig
|
||||
/// Quest config, holds information on how a task should be generated
|
||||
/// </summary>
|
||||
[JsonPropertyName("questConfig")]
|
||||
public RepeatableQuestTypesConfig QuestConfig { get; set; }
|
||||
public required RepeatableQuestTypesConfig QuestConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item base types to block when generating rewards
|
||||
@@ -250,32 +250,59 @@ public record RewardScaling
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtensionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Levels at which to increase to the next level of reward potential
|
||||
/// </summary>
|
||||
[JsonPropertyName("levels")]
|
||||
public List<double>? Levels { get; set; }
|
||||
public required List<double> Levels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Experience reward tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("experience")]
|
||||
public List<double>? Experience { get; set; }
|
||||
public required List<double> Experience { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rouble reward tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("roubles")]
|
||||
public List<double>? Roubles { get; set; }
|
||||
public required List<double> Roubles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gp coin reward tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("gpCoins")]
|
||||
public List<double>? GpCoins { get; set; }
|
||||
public required List<double> GpCoins { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item amount reward tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("items")]
|
||||
public List<double>? Items { get; set; }
|
||||
public required List<double> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// reputation amount reward tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("reputation")]
|
||||
public List<double>? Reputation { get; set; }
|
||||
public required List<double> Reputation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reward spread
|
||||
/// </summary>
|
||||
[JsonPropertyName("rewardSpread")]
|
||||
public double? RewardSpread { get; set; }
|
||||
public required double RewardSpread { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skill reward chance tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("skillRewardChance")]
|
||||
public List<double>? SkillRewardChance { get; set; }
|
||||
public required List<double> SkillRewardChance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skill reward amount tiers
|
||||
/// </summary>
|
||||
[JsonPropertyName("skillPointReward")]
|
||||
public List<double>? SkillPointReward { get; set; }
|
||||
public required List<double> SkillPointReward { get; set; }
|
||||
}
|
||||
|
||||
public record TraderWhitelist
|
||||
@@ -283,23 +310,41 @@ public record TraderWhitelist
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtensionData { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trader Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("traderId")]
|
||||
public string? TraderId { get; set; }
|
||||
public required string TraderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quest types this trader can provide: Completion/Exploration/Elimination.
|
||||
/// </summary>
|
||||
[JsonPropertyName("questTypes")]
|
||||
public List<string>? QuestTypes { get; set; }
|
||||
public required List<string> QuestTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item categories that the reward can be
|
||||
/// </summary>
|
||||
[JsonPropertyName("rewardBaseWhitelist")]
|
||||
public List<string>? RewardBaseWhitelist { get; set; }
|
||||
public required List<string> RewardBaseWhitelist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can this reward be a weapon?
|
||||
/// </summary>
|
||||
[JsonPropertyName("rewardCanBeWeapon")]
|
||||
public bool? RewardCanBeWeapon { get; set; }
|
||||
public required bool RewardCanBeWeapon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chance that the reward is a weapon
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponRewardChancePercent")]
|
||||
public double? WeaponRewardChancePercent { get; set; }
|
||||
public required double WeaponRewardChancePercent { get; set; }
|
||||
}
|
||||
|
||||
public record RepeatableQuestTypesConfig
|
||||
@@ -307,29 +352,50 @@ public record RepeatableQuestTypesConfig
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtensionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines exploration repeatable task generation parameters
|
||||
/// </summary>
|
||||
[JsonPropertyName("Exploration")]
|
||||
public Exploration? Exploration { get; set; }
|
||||
public required Exploration Exploration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines completion repeatable task generation parameters
|
||||
/// </summary>
|
||||
[JsonPropertyName("Completion")]
|
||||
public Completion? Completion { get; set; }
|
||||
public required Completion Completion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines pickup repeatable task generation parameters - TODO: Not implemented/No Data
|
||||
/// </summary>
|
||||
[JsonPropertyName("Pickup")]
|
||||
public Pickup? Pickup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines elimination repeatable task generation parameters
|
||||
/// </summary>
|
||||
[JsonPropertyName("Elimination")]
|
||||
public List<EliminationConfig>? Elimination { get; set; }
|
||||
public required List<EliminationConfig> Elimination { get; set; }
|
||||
}
|
||||
|
||||
public record Exploration : BaseQuestConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum extract count that a per map extract requirement can be generated with
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxExtracts")]
|
||||
public int? MaximumExtracts { get; set; }
|
||||
public required int MaximumExtracts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum extract count that a specific extract can be generated with
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxExtractsWithSpecificExit")]
|
||||
public int? MaximumExtractsWithSpecificExit { get; set; }
|
||||
public required int MaximumExtractsWithSpecificExit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specific extract generation data
|
||||
/// </summary>
|
||||
[JsonPropertyName("specificExits")]
|
||||
public SpecificExits? SpecificExits { get; set; }
|
||||
public required SpecificExits SpecificExits { get; set; }
|
||||
}
|
||||
|
||||
public record SpecificExits
|
||||
@@ -337,44 +403,71 @@ public record SpecificExits
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtensionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chance that an operational task is generated with a specific extract
|
||||
/// </summary>
|
||||
[JsonPropertyName("probability")]
|
||||
public double? Probability { get; set; }
|
||||
public required double Probability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist of specific extract types
|
||||
/// </summary>
|
||||
[JsonPropertyName("passageRequirementWhitelist")]
|
||||
public List<string>? PassageRequirementWhitelist { get; set; }
|
||||
public required List<string> PassageRequirementWhitelist { get; set; }
|
||||
}
|
||||
|
||||
public record Completion : BaseQuestConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum item count that can be requested
|
||||
/// </summary>
|
||||
[JsonPropertyName("minRequestedAmount")]
|
||||
public int? MinimumRequestedAmount { get; set; }
|
||||
|
||||
[JsonPropertyName("maxRequestedAmount")]
|
||||
public int? MaximumRequestedAmount { get; set; }
|
||||
|
||||
[JsonPropertyName("uniqueItemCount")]
|
||||
public int? UniqueItemCount { get; set; }
|
||||
|
||||
[JsonPropertyName("minRequestedBulletAmount")]
|
||||
public int? MinimumRequestedBulletAmount { get; set; }
|
||||
|
||||
[JsonPropertyName("maxRequestedBulletAmount")]
|
||||
public int? MaximumRequestedBulletAmount { get; set; }
|
||||
|
||||
[JsonPropertyName("useWhitelist")]
|
||||
public bool? UseWhitelist { get; set; }
|
||||
|
||||
[JsonPropertyName("useBlacklist")]
|
||||
public bool? UseBlacklist { get; set; }
|
||||
public required int MinimumRequestedAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should supplied items be required FiR
|
||||
/// Maximum item count that can be requested
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxRequestedAmount")]
|
||||
public required int MaximumRequestedAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many unique items should be requested - TODO: This needs to be a range
|
||||
/// </summary>
|
||||
[JsonPropertyName("uniqueItemCount")]
|
||||
public required int UniqueItemCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum bullet count that can be requested - TODO: Not implemented
|
||||
/// </summary>
|
||||
[JsonPropertyName("minRequestedBulletAmount")]
|
||||
public required int MinimumRequestedBulletAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum bullet count that can be requested - TODO: Not implemented
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxRequestedBulletAmount")]
|
||||
public required int MaximumRequestedBulletAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the item whitelist be used
|
||||
/// </summary>
|
||||
[JsonPropertyName("useWhitelist")]
|
||||
public required bool UseWhitelist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the item blacklist be used
|
||||
/// </summary>
|
||||
[JsonPropertyName("useBlacklist")]
|
||||
public required bool UseBlacklist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the supplied items be required FiR
|
||||
/// </summary>
|
||||
[JsonPropertyName("requiredItemsAreFiR")]
|
||||
public bool? RequiredItemsAreFiR { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should supplied items be required FiR
|
||||
/// Should the supplied items be required FiR
|
||||
/// </summary>
|
||||
[JsonPropertyName("requiredItemMinDurabilityMinMax")]
|
||||
public MinMax<double>? RequiredItemMinDurabilityMinMax { get; set; }
|
||||
@@ -414,62 +507,119 @@ public record PickupTypeWithMaxCount
|
||||
|
||||
public record EliminationConfig : BaseQuestConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Level range at which elimination tasks should be generated from this config
|
||||
/// </summary>
|
||||
[JsonPropertyName("levelRange")]
|
||||
public MinMax<int>? LevelRange { get; set; }
|
||||
public required MinMax<int> LevelRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target data probabilities
|
||||
/// </summary>
|
||||
[JsonPropertyName("targets")]
|
||||
public List<ProbabilityObject<string, BossInfo>>? Targets { get; set; }
|
||||
public required List<ProbabilityObject<string, BossInfo>> Targets { get; set; }
|
||||
|
||||
[JsonPropertyName("bodyPartProb")]
|
||||
public double? BodyPartProbability { get; set; }
|
||||
/// <summary>
|
||||
/// Chance that a specific body part is needed as a requirement
|
||||
/// </summary>
|
||||
[JsonPropertyName("bodyPartChance")]
|
||||
public required int BodyPartChance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the specific body part requirement is chosen, pick from these body parts
|
||||
/// </summary>
|
||||
[JsonPropertyName("bodyParts")]
|
||||
public List<ProbabilityObject<string, List<string>>>? BodyParts { get; set; }
|
||||
public required List<ProbabilityObject<string, List<string>>> BodyParts { get; set; }
|
||||
|
||||
[JsonPropertyName("specificLocationProb")]
|
||||
public double? SpecificLocationProbability { get; set; }
|
||||
/// <summary>
|
||||
/// Chance that a specific location modifier is selected
|
||||
/// </summary>
|
||||
[JsonPropertyName("specificLocationChance")]
|
||||
public required int SpecificLocationChance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Locations that should be blacklisted as a requirement
|
||||
/// </summary>
|
||||
[JsonPropertyName("distLocationBlacklist")]
|
||||
public List<string>? DistLocationBlacklist { get; set; }
|
||||
public required List<string> DistLocationBlacklist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Probability that a distance requirement is chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("distProb")]
|
||||
public double? DistanceProbability { get; set; }
|
||||
public required double DistanceProbability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance in meters that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxDist")]
|
||||
public double? MaxDistance { get; set; }
|
||||
public required double MaxDistance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum distance in meters that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("minDist")]
|
||||
public double? MinDistance { get; set; }
|
||||
public required double MinDistance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of kills that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxKills")]
|
||||
public int? MaxKills { get; set; }
|
||||
public required int MaxKills { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum amount of kills that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("minKills")]
|
||||
public int? MinKills { get; set; }
|
||||
|
||||
[JsonPropertyName("minBossKills")]
|
||||
public int? MinBossKills { get; set; }
|
||||
public required int MinKills { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of boss kills that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxBossKills")]
|
||||
public int? MaxBossKills { get; set; }
|
||||
public required int MaxBossKills { get; set; }
|
||||
|
||||
[JsonPropertyName("minPmcKills")]
|
||||
public int? MinPmcKills { get; set; }
|
||||
/// <summary>
|
||||
/// Minimum amount of boss kills that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("minBossKills")]
|
||||
public required int MinBossKills { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of PMC kills that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxPmcKills")]
|
||||
public int? MaxPmcKills { get; set; }
|
||||
public required int MaxPmcKills { get; set; }
|
||||
|
||||
[JsonPropertyName("weaponCategoryRequirementProb")]
|
||||
public double? WeaponCategoryRequirementProbability { get; set; }
|
||||
|
||||
[JsonPropertyName("weaponCategoryRequirements")]
|
||||
public List<ProbabilityObject<string, List<string>>>? WeaponCategoryRequirements { get; set; }
|
||||
/// <summary>
|
||||
/// Minimum amount of PMC kills that can be chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("minPmcKills")]
|
||||
public required int MinPmcKills { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Probability that a specific weapon requirement is chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponRequirementProb")]
|
||||
public double? WeaponRequirementProbability { get; set; }
|
||||
public required double WeaponRequirementProbability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Probability that a weapon category requirement is chosen
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponCategoryRequirementProb")]
|
||||
public required double WeaponCategoryRequirementProbability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a weapon category requirement is chosen, pick from these categories
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponCategoryRequirements")]
|
||||
public required List<ProbabilityObject<string, List<string>>> WeaponCategoryRequirements { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If a weapon requirement is chosen, pick from these weapons
|
||||
/// </summary>
|
||||
[JsonPropertyName("weaponRequirements")]
|
||||
public List<ProbabilityObject<string, List<string>>>? WeaponRequirements { get; set; }
|
||||
public required List<ProbabilityObject<string, List<string>>> WeaponRequirements { get; set; }
|
||||
}
|
||||
|
||||
public record BaseQuestConfig
|
||||
@@ -477,8 +627,11 @@ public record BaseQuestConfig
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtensionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Possible skills that can be rewarded expirence points
|
||||
/// </summary>
|
||||
[JsonPropertyName("possibleSkillRewards")]
|
||||
public List<string>? PossibleSkillRewards { get; set; }
|
||||
public List<string> PossibleSkillRewards { get; set; }
|
||||
}
|
||||
|
||||
public record BossInfo
|
||||
@@ -486,9 +639,16 @@ public record BossInfo
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object> ExtensionData { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is this target a boss
|
||||
/// </summary>
|
||||
[JsonPropertyName("isBoss")]
|
||||
public bool? IsBoss { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is ths target a PMC
|
||||
/// </summary>
|
||||
[JsonPropertyName("isPmc")]
|
||||
public bool? IsPmc { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using SPTarkov.DI.Annotations;
|
||||
|
||||
namespace SPTarkov.Server.Core.Utils;
|
||||
@@ -98,7 +99,7 @@ public class MathUtil
|
||||
/// <param name="x">Support points in x (of same length as y)</param>
|
||||
/// <param name="y">Support points in y (of same length as x)</param>
|
||||
/// <returns>Interpolated value at xp, or null if xp is out of bounds</returns>
|
||||
public double? Interp1(double xp, IReadOnlyList<double> x, IReadOnlyList<double> y)
|
||||
public T? Interp1<T>(T xp, IReadOnlyList<T> x, IReadOnlyList<T> y) where T : INumber<T>
|
||||
{
|
||||
if (xp > x[^1]) // ^1 is the last index in C#
|
||||
{
|
||||
@@ -120,6 +121,6 @@ public class MathUtil
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user