Format Style Fixes
This commit is contained in:
+21
-83
@@ -60,25 +60,15 @@ public class CompletionQuestGenerator(
|
||||
|
||||
if (quest is null)
|
||||
{
|
||||
logger.Error(
|
||||
"Quest template null when attempting to create completion operational task."
|
||||
);
|
||||
logger.Error("Quest template null when attempting to create completion operational task.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Filter the items.json items to items the player must retrieve to complete quest: shouldn't be a quest item or "non-existent"
|
||||
var itemsToRetrievePool = GetItemsToRetrievePool(
|
||||
completionConfig,
|
||||
repeatableConfig.RewardBlacklist
|
||||
);
|
||||
var itemsToRetrievePool = GetItemsToRetrievePool(completionConfig, repeatableConfig.RewardBlacklist);
|
||||
|
||||
// Filter items within our budget
|
||||
var (hashSet, budget) = GetItemsWithinBudget(
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
roublesConfig,
|
||||
itemsToRetrievePool
|
||||
);
|
||||
var (hashSet, budget) = GetItemsWithinBudget(pmcLevel, levelsConfig, roublesConfig, itemsToRetrievePool);
|
||||
itemsToRetrievePool = hashSet;
|
||||
|
||||
// We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as
|
||||
@@ -96,22 +86,12 @@ public class CompletionQuestGenerator(
|
||||
// Filtering too harsh
|
||||
if (itemsToRetrievePool.Count == 0)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive"));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var selectedItems = GenerateAvailableForFinish(
|
||||
quest,
|
||||
completionConfig,
|
||||
repeatableConfig,
|
||||
itemsToRetrievePool.ToList(),
|
||||
budget
|
||||
);
|
||||
var selectedItems = GenerateAvailableForFinish(quest, completionConfig, repeatableConfig, itemsToRetrievePool.ToList(), budget);
|
||||
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
@@ -131,10 +111,7 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="completionConfig">Completion quest type config</param>
|
||||
/// <param name="itemTplBlacklist">Item tpls to not add to pool</param>
|
||||
/// <returns>Set of item tpls</returns>
|
||||
protected HashSet<MongoId> GetItemsToRetrievePool(
|
||||
Completion completionConfig,
|
||||
HashSet<MongoId> itemTplBlacklist
|
||||
)
|
||||
protected HashSet<MongoId> GetItemsToRetrievePool(Completion completionConfig, HashSet<MongoId> itemTplBlacklist)
|
||||
{
|
||||
// Get seasonal items that should not be added to pool as seasonal event is not active
|
||||
var seasonalItems = seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
@@ -184,19 +161,12 @@ public class CompletionQuestGenerator(
|
||||
{
|
||||
// Be fair, don't value the items be more expensive than the reward
|
||||
var multiplier = randomUtil.GetDouble(0.5, 1);
|
||||
var roublesBudget = Math.Floor(
|
||||
mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier
|
||||
);
|
||||
var roublesBudget = Math.Floor(mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier);
|
||||
|
||||
// Make sure there is always a 5000 rouble budget available for selection
|
||||
roublesBudget = Math.Max(roublesBudget, 5000d);
|
||||
|
||||
return (
|
||||
itemsToRetrievePool
|
||||
.Where(itemTpl => itemHelper.GetItemPrice(itemTpl) < roublesBudget)
|
||||
.ToHashSet(),
|
||||
roublesBudget
|
||||
);
|
||||
return (itemsToRetrievePool.Where(itemTpl => itemHelper.GetItemPrice(itemTpl) < roublesBudget).ToHashSet(), roublesBudget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,14 +175,9 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="itemSelection">Item selection to filter</param>
|
||||
/// <param name="pmcLevel">Level of pmc</param>
|
||||
/// <returns>Filtered selection, or original if null or empty</returns>
|
||||
protected HashSet<MongoId> GetWhitelistedItemSelection(
|
||||
HashSet<MongoId> itemSelection,
|
||||
int pmcLevel
|
||||
)
|
||||
protected HashSet<MongoId> GetWhitelistedItemSelection(HashSet<MongoId> itemSelection, int pmcLevel)
|
||||
{
|
||||
var itemWhitelist = databaseService
|
||||
.GetTemplates()
|
||||
.RepeatableQuests?.Data?.Completion?.ItemsWhitelist;
|
||||
var itemWhitelist = databaseService.GetTemplates().RepeatableQuests?.Data?.Completion?.ItemsWhitelist;
|
||||
|
||||
// Whitelist doesn't exist or is empty, return original
|
||||
if (itemWhitelist is null || itemWhitelist.Count == 0)
|
||||
@@ -221,17 +186,13 @@ public class CompletionQuestGenerator(
|
||||
}
|
||||
|
||||
// Filter and concatenate items according to current player level
|
||||
var itemIdsWhitelisted = itemWhitelist
|
||||
.Where(p => p.MinPlayerLevel <= pmcLevel)
|
||||
.SelectMany(x => x.ItemIds)
|
||||
.ToHashSet(); //.Aggregate((a, p) => a.Concat(p.ItemIds), []);
|
||||
var itemIdsWhitelisted = itemWhitelist.Where(p => p.MinPlayerLevel <= pmcLevel).SelectMany(x => x.ItemIds).ToHashSet(); //.Aggregate((a, p) => a.Concat(p.ItemIds), []);
|
||||
|
||||
var filteredSelection = itemSelection
|
||||
.Where(x =>
|
||||
{
|
||||
// Whitelist can contain item tpls and item base type ids
|
||||
return itemIdsWhitelisted.Any(v => itemHelper.IsOfBaseclass(x, v))
|
||||
|| itemIdsWhitelisted.Contains(x);
|
||||
return itemIdsWhitelisted.Any(v => itemHelper.IsOfBaseclass(x, v)) || itemIdsWhitelisted.Contains(x);
|
||||
})
|
||||
.ToHashSet();
|
||||
|
||||
@@ -248,14 +209,9 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="itemSelection">Item selection to filter</param>
|
||||
/// <param name="pmcLevel">Level of pmc</param>
|
||||
/// <returns>Filtered selection, or original if null or empty</returns>
|
||||
protected HashSet<MongoId> GetBlacklistedItemSelection(
|
||||
HashSet<MongoId> itemSelection,
|
||||
int pmcLevel
|
||||
)
|
||||
protected HashSet<MongoId> GetBlacklistedItemSelection(HashSet<MongoId> itemSelection, int pmcLevel)
|
||||
{
|
||||
var itemBlacklist = databaseService
|
||||
.GetTemplates()
|
||||
.RepeatableQuests?.Data?.Completion?.ItemsBlacklist;
|
||||
var itemBlacklist = databaseService.GetTemplates().RepeatableQuests?.Data?.Completion?.ItemsBlacklist;
|
||||
|
||||
// Blacklist doesn't exist or is empty, return original
|
||||
if (itemBlacklist is null || itemBlacklist.Count == 0)
|
||||
@@ -272,8 +228,7 @@ public class CompletionQuestGenerator(
|
||||
var filteredSelection = itemSelection
|
||||
.Where(x =>
|
||||
{
|
||||
return itemIdsBlacklisted.All(v => !itemHelper.IsOfBaseclass(x, v))
|
||||
|| !itemIdsBlacklisted.Contains(x);
|
||||
return itemIdsBlacklisted.All(v => !itemHelper.IsOfBaseclass(x, v)) || !itemIdsBlacklisted.Contains(x);
|
||||
})
|
||||
.ToHashSet();
|
||||
|
||||
@@ -323,10 +278,7 @@ public class CompletionQuestGenerator(
|
||||
if (!found)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-no_reward_item_found_in_price_range",
|
||||
new { minPrice = 0, roublesBudget }
|
||||
)
|
||||
localisationService.GetText("repeatable-no_reward_item_found_in_price_range", new { minPrice = 0, roublesBudget })
|
||||
);
|
||||
|
||||
return chosenRequirementItemsTpls;
|
||||
@@ -356,17 +308,13 @@ public class CompletionQuestGenerator(
|
||||
|
||||
// Push a CompletionCondition with the item and the amount of the item into quest
|
||||
chosenRequirementItemsTpls.Add(tplChosen);
|
||||
quest.Conditions.AvailableForFinish.Add(
|
||||
GenerateCondition(tplChosen, value, repeatableConfig.QuestConfig.Completion)
|
||||
);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCondition(tplChosen, value, repeatableConfig.QuestConfig.Completion));
|
||||
|
||||
// Is there budget left for more items
|
||||
if (roublesBudget > 0)
|
||||
{
|
||||
// Reduce item pool to fit budget
|
||||
itemSelection = itemSelection
|
||||
.Where(tpl => itemHelper.GetItemPrice(tpl) < roublesBudget)
|
||||
.ToList();
|
||||
itemSelection = itemSelection.Where(tpl => itemHelper.GetItemPrice(tpl) < roublesBudget).ToList();
|
||||
|
||||
if (itemSelection.Count == 0)
|
||||
{
|
||||
@@ -393,22 +341,12 @@ public class CompletionQuestGenerator(
|
||||
/// <param name="value">Amount of items of this specific type to request</param>
|
||||
/// <param name="completionConfig">Completion config from quest.json</param>
|
||||
/// <returns>object of "Completion"-condition</returns>
|
||||
protected QuestCondition GenerateCondition(
|
||||
MongoId itemTpl,
|
||||
double value,
|
||||
Completion completionConfig
|
||||
)
|
||||
protected QuestCondition GenerateCondition(MongoId itemTpl, double value, Completion completionConfig)
|
||||
{
|
||||
var onlyFoundInRaid = completionConfig.RequiredItemsAreFiR;
|
||||
var minDurability = itemHelper.IsOfBaseclasses(
|
||||
itemTpl,
|
||||
[BaseClasses.WEAPON, BaseClasses.ARMOR]
|
||||
)
|
||||
var minDurability = itemHelper.IsOfBaseclasses(itemTpl, [BaseClasses.WEAPON, BaseClasses.ARMOR])
|
||||
? randomUtil.GetArrayValue(
|
||||
[
|
||||
completionConfig.RequiredItemMinDurabilityMinMax.Min,
|
||||
completionConfig.RequiredItemMinDurabilityMinMax.Max,
|
||||
]
|
||||
[completionConfig.RequiredItemMinDurabilityMinMax.Min, completionConfig.RequiredItemMinDurabilityMinMax.Max]
|
||||
)
|
||||
: 0;
|
||||
|
||||
|
||||
+49
-174
@@ -35,14 +35,13 @@ public class EliminationQuestGenerator(
|
||||
/// <summary>
|
||||
/// Body parts to present to the client as opposed to the body part information in quest data.
|
||||
/// </summary>
|
||||
private static readonly FrozenDictionary<string, List<string>> _bodyPartsToClient =
|
||||
new Dictionary<string, List<string>>
|
||||
{
|
||||
{ BodyParts.Arms, [BodyParts.LeftArm, BodyParts.RightArm] },
|
||||
{ BodyParts.Legs, [BodyParts.LeftLeg, BodyParts.RightLeg] },
|
||||
{ BodyParts.Head, [BodyParts.Head] },
|
||||
{ BodyParts.Chest, [BodyParts.Chest, BodyParts.Stomach] },
|
||||
}.ToFrozenDictionary();
|
||||
private static readonly FrozenDictionary<string, List<string>> _bodyPartsToClient = new Dictionary<string, List<string>>
|
||||
{
|
||||
{ BodyParts.Arms, [BodyParts.LeftArm, BodyParts.RightArm] },
|
||||
{ BodyParts.Legs, [BodyParts.LeftLeg, BodyParts.RightLeg] },
|
||||
{ BodyParts.Head, [BodyParts.Head] },
|
||||
{ BodyParts.Chest, [BodyParts.Chest, BodyParts.Stomach] },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// MaxDistDifficulty is defined by 2, this could be a tuning parameter if we don't like the reward generation
|
||||
@@ -83,9 +82,7 @@ public class EliminationQuestGenerator(
|
||||
var generationData = GetGenerationData(repeatableConfig, pmcLevel);
|
||||
if (generationData is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-eliminationQuestGenerationData-is-null")
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-eliminationQuestGenerationData-is-null"));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -114,18 +111,13 @@ public class EliminationQuestGenerator(
|
||||
|
||||
// Target on bodyPart max. difficulty is that of the least probable element
|
||||
var maxTargetDifficulty = 1 / generationData.TargetsConfig.MinProbability();
|
||||
var maxBodyPartsDifficulty =
|
||||
generationData.EliminationConfig.MinKills
|
||||
/ generationData.BodyPartsConfig.MinProbability();
|
||||
var maxBodyPartsDifficulty = generationData.EliminationConfig.MinKills / generationData.BodyPartsConfig.MinProbability();
|
||||
|
||||
var maxKillDifficulty = generationData.EliminationConfig.MaxKills;
|
||||
var targetPool = questTypePool.Pool.Elimination;
|
||||
|
||||
// Get a random bot type to eliminate
|
||||
var (botTypeToEliminate, targetsConfig) = GetBotTypeToEliminate(
|
||||
generationData,
|
||||
questTypePool
|
||||
);
|
||||
var (botTypeToEliminate, targetsConfig) = GetBotTypeToEliminate(generationData, questTypePool);
|
||||
if (botTypeToEliminate is null || targetsConfig is null)
|
||||
{
|
||||
logger.Warning(localisationService.GetText("repeatable-no-bot-types-remain"));
|
||||
@@ -143,30 +135,18 @@ public class EliminationQuestGenerator(
|
||||
// Try and get a target location pool for this bot type
|
||||
if (!targetPool.Targets.TryGetValue(botTypeToEliminate, out var targetLocationPool))
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-unable-get-target-pool", botTypeToEliminate)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable-get-target-pool", botTypeToEliminate));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try and get a location key for this quest
|
||||
if (
|
||||
!TryGetLocationKey(
|
||||
generationData,
|
||||
targetPool,
|
||||
botTypeToEliminate,
|
||||
targetLocationPool.Locations,
|
||||
out var locationKey
|
||||
) || locationKey is null
|
||||
!TryGetLocationKey(generationData, targetPool, botTypeToEliminate, targetLocationPool.Locations, out var locationKey)
|
||||
|| locationKey is null
|
||||
)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable-get-location-key",
|
||||
botTypeToEliminate
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable-get-location-key", botTypeToEliminate));
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -174,9 +154,7 @@ public class EliminationQuestGenerator(
|
||||
// Generate a body part, make sure we ref the body part difficulty so it can be adjusted
|
||||
var bodyPartsToClient = new List<string>();
|
||||
var bodyPartDifficulty = 0d;
|
||||
var generateBodyParts = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.BodyPartChance
|
||||
);
|
||||
var generateBodyParts = randomUtil.GetChance100(generationData.EliminationConfig.BodyPartChance);
|
||||
if (generateBodyParts)
|
||||
{
|
||||
// draw the target body part and calculate the difficulty factor
|
||||
@@ -184,12 +162,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Draw a distance condition
|
||||
var isDistanceRequirementAllowed = IsDistanceRequirementAllowed(
|
||||
generationData,
|
||||
botTypeToEliminate,
|
||||
locationKey,
|
||||
targetsConfig
|
||||
);
|
||||
var isDistanceRequirementAllowed = IsDistanceRequirementAllowed(generationData, botTypeToEliminate, locationKey, targetsConfig);
|
||||
|
||||
int? distance = null;
|
||||
var distanceDifficulty = 0;
|
||||
@@ -204,9 +177,7 @@ public class EliminationQuestGenerator(
|
||||
|
||||
string? allowedWeaponsCategory = null;
|
||||
|
||||
var generateWeaponCategoryRequirement = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.WeaponCategoryRequirementChance
|
||||
);
|
||||
var generateWeaponCategoryRequirement = randomUtil.GetChance100(generationData.EliminationConfig.WeaponCategoryRequirementChance);
|
||||
|
||||
// Generate a weapon category requirement
|
||||
if (generateWeaponCategoryRequirement)
|
||||
@@ -217,9 +188,7 @@ public class EliminationQuestGenerator(
|
||||
// Only allow a specific weapon requirement if a weapon category was not chosen
|
||||
MongoId? allowedWeapon = null;
|
||||
|
||||
var generateWeaponRequirement = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.WeaponRequirementChance
|
||||
);
|
||||
var generateWeaponRequirement = randomUtil.GetChance100(generationData.EliminationConfig.WeaponRequirementChance);
|
||||
|
||||
// Generate a weapon requirement
|
||||
if (!generateWeaponCategoryRequirement && generateWeaponRequirement)
|
||||
@@ -228,11 +197,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Draw how many npm kills are required
|
||||
var desiredKillCount = GetEliminationKillCount(
|
||||
botTypeToEliminate,
|
||||
targetsConfig,
|
||||
generationData.EliminationConfig
|
||||
);
|
||||
var desiredKillCount = GetEliminationKillCount(botTypeToEliminate, targetsConfig, generationData.EliminationConfig);
|
||||
|
||||
var killDifficulty = desiredKillCount;
|
||||
|
||||
@@ -261,12 +226,7 @@ public class EliminationQuestGenerator(
|
||||
|
||||
if (quest is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-quest_generation_failed_no_template",
|
||||
"elimination"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-quest_generation_failed_no_template", "elimination"));
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -285,19 +245,11 @@ public class EliminationQuestGenerator(
|
||||
if (locationKey != "any")
|
||||
{
|
||||
var locationId = Enum.Parse<ELocationName>(locationKey);
|
||||
availableForFinishCondition.Counter.Conditions.Add(
|
||||
GenerateEliminationLocation(generationData.LocationsConfig[locationId])
|
||||
);
|
||||
availableForFinishCondition.Counter.Conditions.Add(GenerateEliminationLocation(generationData.LocationsConfig[locationId]));
|
||||
}
|
||||
|
||||
availableForFinishCondition.Counter.Conditions.Add(
|
||||
GenerateEliminationCondition(
|
||||
botTypeToEliminate,
|
||||
bodyPartsToClient,
|
||||
distance,
|
||||
allowedWeapon,
|
||||
allowedWeaponsCategory
|
||||
)
|
||||
GenerateEliminationCondition(botTypeToEliminate, bodyPartsToClient, distance, allowedWeapon, allowedWeaponsCategory)
|
||||
);
|
||||
availableForFinishCondition.Value = desiredKillCount;
|
||||
availableForFinishCondition.Id = new MongoId();
|
||||
@@ -316,15 +268,9 @@ public class EliminationQuestGenerator(
|
||||
return quest;
|
||||
}
|
||||
|
||||
protected EliminationQuestGenerationData? GetGenerationData(
|
||||
RepeatableQuestConfig repeatableConfig,
|
||||
int pmcLevel
|
||||
)
|
||||
protected EliminationQuestGenerationData? GetGenerationData(RepeatableQuestConfig repeatableConfig, int pmcLevel)
|
||||
{
|
||||
var eliminationConfig = repeatableQuestHelper.GetEliminationConfigByPmcLevel(
|
||||
pmcLevel,
|
||||
repeatableConfig
|
||||
);
|
||||
var eliminationConfig = repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
|
||||
|
||||
if (eliminationConfig is null)
|
||||
{
|
||||
@@ -334,22 +280,13 @@ public class EliminationQuestGenerator(
|
||||
|
||||
var locationsConfig = repeatableConfig.Locations;
|
||||
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
|
||||
cloner,
|
||||
eliminationConfig.Targets
|
||||
);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
cloner,
|
||||
eliminationConfig.BodyParts
|
||||
);
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(cloner, eliminationConfig.Targets);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(cloner, eliminationConfig.BodyParts);
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(
|
||||
cloner,
|
||||
eliminationConfig.WeaponCategoryRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(
|
||||
cloner,
|
||||
eliminationConfig.WeaponRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<MongoId>>(cloner, eliminationConfig.WeaponRequirements);
|
||||
|
||||
return new EliminationQuestGenerationData(
|
||||
eliminationConfig,
|
||||
@@ -374,9 +311,7 @@ public class EliminationQuestGenerator(
|
||||
{
|
||||
var targetPool = questTypePool.Pool.Elimination;
|
||||
|
||||
var targetsConfig = generationData.TargetsConfig.Filter(x =>
|
||||
targetPool.Targets.ContainsKey(x.Key)
|
||||
);
|
||||
var targetsConfig = generationData.TargetsConfig.Filter(x => targetPool.Targets.ContainsKey(x.Key));
|
||||
|
||||
if (targetsConfig.Count != 0 && !targetsConfig.All(x => x.Data?.IsBoss ?? false))
|
||||
{
|
||||
@@ -407,9 +342,7 @@ public class EliminationQuestGenerator(
|
||||
out string? locationKey
|
||||
)
|
||||
{
|
||||
var useSpecificLocation = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.SpecificLocationChance
|
||||
);
|
||||
var useSpecificLocation = randomUtil.GetChance100(generationData.EliminationConfig.SpecificLocationChance);
|
||||
|
||||
switch (useSpecificLocation)
|
||||
{
|
||||
@@ -432,11 +365,7 @@ public class EliminationQuestGenerator(
|
||||
if (locations.Count == 0)
|
||||
{
|
||||
// Never should reach this if everything works out
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"quest-repeatable_elimination_generation_failed_please_report"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("quest-repeatable_elimination_generation_failed_please_report"));
|
||||
|
||||
locationKey = null;
|
||||
return false;
|
||||
@@ -459,9 +388,7 @@ public class EliminationQuestGenerator(
|
||||
var tmpKey = locationKey;
|
||||
|
||||
// Filter locations bot can be killed on to just those not chosen by key
|
||||
possibleLocationPool.Locations = possibleLocationPool
|
||||
.Locations?.Where(location => location != tmpKey)
|
||||
.ToList();
|
||||
possibleLocationPool.Locations = possibleLocationPool.Locations?.Where(location => location != tmpKey).ToList();
|
||||
|
||||
// None left after filtering
|
||||
if (possibleLocationPool.Locations?.Count is null or 0)
|
||||
@@ -480,10 +407,7 @@ public class EliminationQuestGenerator(
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <param name="bodyPartDifficulty">BodyPartDifficulty to modify based on selection</param>
|
||||
/// <returns>List of selected body parts</returns>
|
||||
protected List<string> GenerateBodyParts(
|
||||
EliminationQuestGenerationData generationData,
|
||||
ref double bodyPartDifficulty
|
||||
)
|
||||
protected List<string> GenerateBodyParts(EliminationQuestGenerationData generationData, ref double bodyPartDifficulty)
|
||||
{
|
||||
// if we add a bodyPart condition, we draw randomly one or two parts
|
||||
// each bodyPart of the BODYPARTS ProbabilityObjectArray includes the string(s)
|
||||
@@ -533,9 +457,7 @@ public class EliminationQuestGenerator(
|
||||
)
|
||||
{
|
||||
// This location is can be chosen for a distance requirement
|
||||
var whitelisted = !generationData.EliminationConfig.DistLocationBlacklist.Contains(
|
||||
locationKey
|
||||
);
|
||||
var whitelisted = !generationData.EliminationConfig.DistLocationBlacklist.Contains(locationKey);
|
||||
|
||||
// We're not whitelisted, exit early to avoid doing a roll for no reason
|
||||
if (!whitelisted)
|
||||
@@ -544,9 +466,7 @@ public class EliminationQuestGenerator(
|
||||
}
|
||||
|
||||
// Are we allowed a distance condition by chance?
|
||||
var isAllowedByChance = randomUtil.GetChance100(
|
||||
generationData.EliminationConfig.DistanceProbability
|
||||
);
|
||||
var isAllowedByChance = randomUtil.GetChance100(generationData.EliminationConfig.DistanceProbability);
|
||||
|
||||
// Not allowed by chance, return early.
|
||||
// We now just assume we rolled this condition and don't take it into account anymore.
|
||||
@@ -567,25 +487,15 @@ public class EliminationQuestGenerator(
|
||||
.GetDictionary()
|
||||
.Select(x => x.Value)
|
||||
.Where(location => location.Base?.Id != null)
|
||||
.Select(location => new
|
||||
{
|
||||
location.Base.Id,
|
||||
BossSpawn = location.Base.BossLocationSpawn,
|
||||
});
|
||||
.Select(location => new { location.Base.Id, BossSpawn = location.Base.BossLocationSpawn });
|
||||
|
||||
// filter for the current boss to spawn on map
|
||||
var thisBossSpawns = bossSpawns
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
BossSpawn = x.BossSpawn.Where(e => e.BossName == botTypeToEliminate),
|
||||
})
|
||||
.Select(x => new { x.Id, BossSpawn = x.BossSpawn.Where(e => e.BossName == botTypeToEliminate) })
|
||||
.Where(x => x.BossSpawn.Any());
|
||||
|
||||
// remove blacklisted locations
|
||||
var allowedSpawns = thisBossSpawns.Where(x =>
|
||||
!generationData.EliminationConfig.DistLocationBlacklist.Contains(x.Id)
|
||||
);
|
||||
var allowedSpawns = thisBossSpawns.Where(x => !generationData.EliminationConfig.DistLocationBlacklist.Contains(x.Id));
|
||||
|
||||
// if the boss spawns on non-blacklisted locations and the current location is allowed,
|
||||
// we can generate a distance kill requirement
|
||||
@@ -603,19 +513,13 @@ public class EliminationQuestGenerator(
|
||||
var distance = (int)
|
||||
Math.Floor(
|
||||
Math.Abs(randomUtil.Random.NextDouble() - randomUtil.Random.NextDouble())
|
||||
* (
|
||||
1
|
||||
+ generationData.EliminationConfig.MaxDistance
|
||||
- generationData.EliminationConfig.MinDistance
|
||||
)
|
||||
* (1 + generationData.EliminationConfig.MaxDistance - generationData.EliminationConfig.MinDistance)
|
||||
+ generationData.EliminationConfig.MinDistance
|
||||
);
|
||||
|
||||
distance = (int)Math.Ceiling((decimal)(distance / 5d)) * 5;
|
||||
|
||||
var distanceDifficulty = (int)(
|
||||
MaxDistDifficulty * distance / generationData.EliminationConfig.MaxDistance
|
||||
);
|
||||
var distanceDifficulty = (int)(MaxDistDifficulty * distance / generationData.EliminationConfig.MaxDistance);
|
||||
|
||||
return (distance, distanceDifficulty);
|
||||
}
|
||||
@@ -626,10 +530,7 @@ public class EliminationQuestGenerator(
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <param name="distance">Distance to generate it for, pass null if not required</param>
|
||||
/// <returns>Weapon requirement category selected</returns>
|
||||
protected string? GenerateWeaponCategoryRequirement(
|
||||
EliminationQuestGenerationData generationData,
|
||||
int? distance
|
||||
)
|
||||
protected string? GenerateWeaponCategoryRequirement(EliminationQuestGenerationData generationData, int? distance)
|
||||
{
|
||||
switch (distance)
|
||||
{
|
||||
@@ -639,9 +540,7 @@ public class EliminationQuestGenerator(
|
||||
HashSet<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
|
||||
|
||||
// Filter out close range weapons from long distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category => weaponTypeBlacklist.Contains(category.Key));
|
||||
break;
|
||||
}
|
||||
// Filter out long range weapons from close distance requirement
|
||||
@@ -650,9 +549,7 @@ public class EliminationQuestGenerator(
|
||||
HashSet<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
|
||||
|
||||
// Filter out far range weapons from close distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category => weaponTypeBlacklist.Contains(category.Key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -669,20 +566,14 @@ public class EliminationQuestGenerator(
|
||||
/// </summary>
|
||||
/// <param name="generationData">Generation data</param>
|
||||
/// <returns>Weapon to use</returns>
|
||||
protected MongoId GenerateSpecificWeaponRequirement(
|
||||
EliminationQuestGenerationData generationData
|
||||
)
|
||||
protected MongoId GenerateSpecificWeaponRequirement(EliminationQuestGenerationData generationData)
|
||||
{
|
||||
var weaponRequirement = generationData.WeaponRequirementConfig.Draw(1, false);
|
||||
var specificAllowedWeaponCategory = generationData.WeaponRequirementConfig.Data(
|
||||
weaponRequirement[0]
|
||||
);
|
||||
var specificAllowedWeaponCategory = generationData.WeaponRequirementConfig.Data(weaponRequirement[0]);
|
||||
|
||||
if (specificAllowedWeaponCategory?[0] is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText("repeatable-elimination-specific-weapon-null")
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-elimination-specific-weapon-null"));
|
||||
return MongoId.Empty();
|
||||
}
|
||||
|
||||
@@ -706,30 +597,18 @@ public class EliminationQuestGenerator(
|
||||
{
|
||||
if (targetsConfig.Data(targetKey)?.IsBoss ?? false)
|
||||
{
|
||||
return randomUtil.RandInt(
|
||||
eliminationConfig.MinBossKills,
|
||||
eliminationConfig.MaxBossKills + 1
|
||||
);
|
||||
return randomUtil.RandInt(eliminationConfig.MinBossKills, eliminationConfig.MaxBossKills + 1);
|
||||
}
|
||||
|
||||
if (targetsConfig.Data(targetKey)?.IsPmc ?? false)
|
||||
{
|
||||
return randomUtil.RandInt(
|
||||
eliminationConfig.MinPmcKills,
|
||||
eliminationConfig.MaxPmcKills + 1
|
||||
);
|
||||
return randomUtil.RandInt(eliminationConfig.MinPmcKills, eliminationConfig.MaxPmcKills + 1);
|
||||
}
|
||||
|
||||
return randomUtil.RandInt(eliminationConfig.MinKills, eliminationConfig.MaxKills + 1);
|
||||
}
|
||||
|
||||
protected double DifficultyWeighing(
|
||||
double target,
|
||||
double bodyPart,
|
||||
int dist,
|
||||
int kill,
|
||||
int weaponRequirement
|
||||
)
|
||||
protected double DifficultyWeighing(double target, double bodyPart, int dist, int kill, int weaponRequirement)
|
||||
{
|
||||
return Math.Sqrt(Math.Sqrt(target) + bodyPart + dist + weaponRequirement) * kill;
|
||||
}
|
||||
@@ -796,11 +675,7 @@ public class EliminationQuestGenerator(
|
||||
// Don't allow distance + melee requirement
|
||||
if (distance is not null && allowedWeaponCategory != "5b5f7a0886f77409407a7f96")
|
||||
{
|
||||
killConditionProps.Distance = new CounterConditionDistance
|
||||
{
|
||||
CompareMethod = ">=",
|
||||
Value = distance.Value,
|
||||
};
|
||||
killConditionProps.Distance = new CounterConditionDistance { CompareMethod = ">=", Value = distance.Value };
|
||||
}
|
||||
|
||||
// Has specific weapon requirement
|
||||
|
||||
+19
-94
@@ -54,20 +54,9 @@ public class ExplorationQuestGenerator(
|
||||
var explorationConfig = repeatableConfig.QuestConfig.Exploration;
|
||||
|
||||
// Try and get a location to generate for
|
||||
if (
|
||||
!TryGetLocationInfo(
|
||||
repeatableConfig,
|
||||
explorationConfig,
|
||||
questTypePool,
|
||||
out var locationInfo
|
||||
) || locationInfo is null
|
||||
)
|
||||
if (!TryGetLocationInfo(repeatableConfig, explorationConfig, questTypePool, out var locationInfo) || locationInfo is null)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText(
|
||||
"repeatable-no_location_found_for_exploration_quest_generation"
|
||||
)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("repeatable-no_location_found_for_exploration_quest_generation"));
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -82,12 +71,7 @@ public class ExplorationQuestGenerator(
|
||||
|
||||
if (quest is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-quest_generation_failed_no_template",
|
||||
"exploration"
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-quest_generation_failed_no_template", "exploration"));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,45 +79,24 @@ public class ExplorationQuestGenerator(
|
||||
if (!TryGenerateAvailableForFinish(quest, locationInfo))
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-available_for_finish_condition_failed_to_generate",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
localisationService.GetText("repeatable-available_for_finish_condition_failed_to_generate", locationInfo.LocationName)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we require a specific extract requirement, generate it
|
||||
if (
|
||||
locationInfo.RequiresSpecificExtract
|
||||
&& !TryGenerateSpecificExtractRequirement(quest, repeatableConfig, locationInfo)
|
||||
)
|
||||
if (locationInfo.RequiresSpecificExtract && !TryGenerateSpecificExtractRequirement(quest, repeatableConfig, locationInfo))
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-specific_extract_condition_failed_to_generate",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
localisationService.GetText("repeatable-specific_extract_condition_failed_to_generate", locationInfo.LocationName)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Difficulty for exploration goes from 1 extract to maxExtracts
|
||||
// Difficulty for reward goes from 0.2...1 -> map
|
||||
var difficulty = mathUtil.MapToRange(
|
||||
locationInfo.NumOfExtractsRequired,
|
||||
1,
|
||||
explorationConfig.MaximumExtracts,
|
||||
0.2,
|
||||
1
|
||||
);
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
difficulty,
|
||||
traderId,
|
||||
repeatableConfig,
|
||||
explorationConfig
|
||||
);
|
||||
var difficulty = mathUtil.MapToRange(locationInfo.NumOfExtractsRequired, 1, explorationConfig.MaximumExtracts, 0.2, 1);
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(pmcLevel, difficulty, traderId, repeatableConfig, explorationConfig);
|
||||
|
||||
return quest;
|
||||
}
|
||||
@@ -168,18 +131,11 @@ public class ExplorationQuestGenerator(
|
||||
// Make the location info object
|
||||
var locationTarget = pool.Pool!.Exploration!.Locations![locationKey];
|
||||
|
||||
var requiresSpecificExtract = randomUtil.GetChance100(
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.Chance
|
||||
);
|
||||
var requiresSpecificExtract = randomUtil.GetChance100(repeatableConfig.QuestConfig.Exploration.SpecificExits.Chance);
|
||||
|
||||
var numExtracts = GetNumberOfExits(explorationConfig, requiresSpecificExtract);
|
||||
|
||||
locationInfo = new LocationInfo(
|
||||
locationKey,
|
||||
locationTarget.ToList(),
|
||||
requiresSpecificExtract,
|
||||
numExtracts
|
||||
);
|
||||
locationInfo = new LocationInfo(locationKey, locationTarget.ToList(), requiresSpecificExtract, numExtracts);
|
||||
|
||||
// Remove the location from the available pool
|
||||
pool.Pool.Exploration.Locations.Remove(locationKey);
|
||||
@@ -196,9 +152,7 @@ public class ExplorationQuestGenerator(
|
||||
protected int GetNumberOfExits(Exploration config, bool requiresSpecificExtract)
|
||||
{
|
||||
// Different max extract count when specific extract needed
|
||||
var exitTimesMax = requiresSpecificExtract
|
||||
? config.MaximumExtractsWithSpecificExit
|
||||
: config.MaximumExtracts + 1;
|
||||
var exitTimesMax = requiresSpecificExtract ? config.MaximumExtractsWithSpecificExit : config.MaximumExtracts + 1;
|
||||
|
||||
return randomUtil.RandInt(1, exitTimesMax);
|
||||
}
|
||||
@@ -209,10 +163,7 @@ public class ExplorationQuestGenerator(
|
||||
/// <param name="locationKey">Map id (e.g. factory4_day)</param>
|
||||
/// <param name="playerGroup">Pmc/Scav</param>
|
||||
/// <returns>List of Exit objects</returns>
|
||||
protected IEnumerable<Exit>? GetLocationExitsForSide(
|
||||
string locationKey,
|
||||
PlayerGroup playerGroup
|
||||
)
|
||||
protected IEnumerable<Exit>? GetLocationExitsForSide(string locationKey, PlayerGroup playerGroup)
|
||||
{
|
||||
var mapExtracts = databaseService.GetLocation(locationKey.ToLowerInvariant())?.AllExtracts;
|
||||
|
||||
@@ -235,18 +186,11 @@ public class ExplorationQuestGenerator(
|
||||
}
|
||||
|
||||
// Lookup the location
|
||||
var location = repeatableQuestHelper.GetQuestLocationByMapId(
|
||||
locationInfo.LocationName.ToString()
|
||||
);
|
||||
var location = repeatableQuestHelper.GetQuestLocationByMapId(locationInfo.LocationName.ToString());
|
||||
|
||||
if (location is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable_to_find_location_id_for_location_name",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable_to_find_location_id_for_location_name", locationInfo.LocationName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -267,11 +211,7 @@ public class ExplorationQuestGenerator(
|
||||
};
|
||||
|
||||
quest.Conditions.AvailableForFinish![0].Counter!.Id = new MongoId();
|
||||
quest.Conditions.AvailableForFinish![0].Counter!.Conditions =
|
||||
[
|
||||
exitStatusCondition,
|
||||
locationCondition,
|
||||
];
|
||||
quest.Conditions.AvailableForFinish![0].Counter!.Conditions = [exitStatusCondition, locationCondition];
|
||||
quest.Conditions.AvailableForFinish[0].Value = locationInfo.NumOfExtractsRequired;
|
||||
quest.Conditions.AvailableForFinish[0].Id = new MongoId();
|
||||
|
||||
@@ -294,19 +234,11 @@ public class ExplorationQuestGenerator(
|
||||
)
|
||||
{
|
||||
// Fetch extracts for the requested side
|
||||
var mapExits = GetLocationExitsForSide(
|
||||
locationInfo.LocationName.ToString(),
|
||||
repeatableConfig.Side
|
||||
);
|
||||
var mapExits = GetLocationExitsForSide(locationInfo.LocationName.ToString(), repeatableConfig.Side);
|
||||
|
||||
if (mapExits is null)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable_to_find_exits_for_location",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable_to_find_exits_for_location", locationInfo.LocationName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -315,19 +247,12 @@ public class ExplorationQuestGenerator(
|
||||
|
||||
// Exclude exits with a requirement to leave (e.g. car extracts)
|
||||
var possibleExits = exitPool.Where(exit =>
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains(
|
||||
"PassageRequirement"
|
||||
)
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains("PassageRequirement")
|
||||
);
|
||||
|
||||
if (!possibleExits.Any())
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"repeatable-unable_choose_exit_pool_empty",
|
||||
locationInfo.LocationName
|
||||
)
|
||||
);
|
||||
logger.Error(localisationService.GetText("repeatable-unable_choose_exit_pool_empty", locationInfo.LocationName));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+5
-19
@@ -42,9 +42,7 @@ public class PickupQuestGenerator(
|
||||
sessionId
|
||||
);
|
||||
|
||||
var itemTypeToFetchWithCount = randomUtil.GetArrayValue(
|
||||
pickupConfig.ItemTypeToFetchWithMaxCount
|
||||
);
|
||||
var itemTypeToFetchWithCount = randomUtil.GetArrayValue(pickupConfig.ItemTypeToFetchWithMaxCount);
|
||||
|
||||
var itemCountToFetch = randomUtil.RandInt(
|
||||
itemTypeToFetchWithCount.MinimumPickupCount.Value,
|
||||
@@ -54,34 +52,22 @@ public class PickupQuestGenerator(
|
||||
// var locationKey: string = this.randomUtil.drawRandomFromDict(questTypePool.pool.Pickup.locations)[0];
|
||||
// var locationTarget = questTypePool.pool.Pickup.locations[locationKey];
|
||||
|
||||
var findCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x =>
|
||||
x.ConditionType == "FindItem"
|
||||
);
|
||||
var findCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x => x.ConditionType == "FindItem");
|
||||
findCondition.Target = new ListOrT<string>([itemTypeToFetchWithCount.ItemType], null);
|
||||
findCondition.Value = itemCountToFetch;
|
||||
|
||||
var counterCreatorCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x =>
|
||||
x.ConditionType == "CounterCreator"
|
||||
);
|
||||
var counterCreatorCondition = quest.Conditions.AvailableForFinish.FirstOrDefault(x => x.ConditionType == "CounterCreator");
|
||||
// var locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
|
||||
// (locationCondition._props as ILocationConditionProps).target = [...locationTarget];
|
||||
|
||||
var equipmentCondition = counterCreatorCondition.Counter.Conditions.FirstOrDefault(x =>
|
||||
x.ConditionType == "Equipment"
|
||||
);
|
||||
var equipmentCondition = counterCreatorCondition.Counter.Conditions.FirstOrDefault(x => x.ConditionType == "Equipment");
|
||||
equipmentCondition.EquipmentInclusive =
|
||||
[
|
||||
[itemTypeToFetchWithCount.ItemType],
|
||||
];
|
||||
|
||||
// Add rewards
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
1,
|
||||
traderId,
|
||||
repeatableConfig,
|
||||
pickupConfig
|
||||
);
|
||||
quest.Rewards = repeatableQuestRewardGenerator.GenerateReward(pmcLevel, 1, traderId, repeatableConfig, pickupConfig);
|
||||
|
||||
return quest;
|
||||
}
|
||||
|
||||
+36
-158
@@ -68,11 +68,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
)
|
||||
{
|
||||
// Get vars to configure rewards with
|
||||
var rewardParams = GetQuestRewardValues(
|
||||
repeatableConfig.RewardScaling,
|
||||
difficulty,
|
||||
pmcLevel
|
||||
);
|
||||
var rewardParams = GetQuestRewardValues(repeatableConfig.RewardScaling, difficulty, pmcLevel);
|
||||
|
||||
// Get budget to spend on item rewards (copy of raw roubles given)
|
||||
var itemRewardBudget = rewardParams.RewardRoubles;
|
||||
@@ -112,13 +108,12 @@ public class RepeatableQuestRewardGenerator(
|
||||
rewardIndex++;
|
||||
|
||||
// Add GP coin reward
|
||||
rewards["Success"]
|
||||
.Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex));
|
||||
rewards["Success"].Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex));
|
||||
rewardIndex++;
|
||||
|
||||
// Add preset weapon to reward if checks pass
|
||||
var traderWhitelistDetails = repeatableConfig.TraderWhitelist.FirstOrDefault(
|
||||
traderWhitelist => traderWhitelist.TraderId == traderId
|
||||
var traderWhitelistDetails = repeatableConfig.TraderWhitelist.FirstOrDefault(traderWhitelist =>
|
||||
traderWhitelist.TraderId == traderId
|
||||
);
|
||||
|
||||
if (traderWhitelistDetails is null)
|
||||
@@ -127,10 +122,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
traderWhitelistDetails.RewardCanBeWeapon
|
||||
&& randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent)
|
||||
)
|
||||
if (traderWhitelistDetails.RewardCanBeWeapon && randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent))
|
||||
{
|
||||
var chosenWeapon = GetRandomWeaponPresetWithinBudget(itemRewardBudget, rewardIndex);
|
||||
if (chosenWeapon is not null)
|
||||
@@ -143,17 +135,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
var inBudgetRewardItemPool = ChooseRewardItemsWithinBudget(
|
||||
repeatableConfig,
|
||||
itemRewardBudget,
|
||||
traderId
|
||||
);
|
||||
var inBudgetRewardItemPool = ChooseRewardItemsWithinBudget(repeatableConfig, itemRewardBudget, traderId);
|
||||
if (rewardTplBlacklist is not null)
|
||||
{
|
||||
// Filter reward pool of items from blacklist, only use if there's at least 1 item remaining
|
||||
var filteredRewardItemPool = inBudgetRewardItemPool
|
||||
.Where(item => !rewardTplBlacklist.Contains(item.Id))
|
||||
.ToList();
|
||||
var filteredRewardItemPool = inBudgetRewardItemPool.Where(item => !rewardTplBlacklist.Contains(item.Id)).ToList();
|
||||
|
||||
if (filteredRewardItemPool.Count != 0)
|
||||
{
|
||||
@@ -180,8 +166,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
// Add item rewards
|
||||
foreach (var itemReward in itemsToReward)
|
||||
{
|
||||
rewards["Success"]
|
||||
.Add(GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex));
|
||||
rewards["Success"].Add(GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex));
|
||||
rewardIndex++;
|
||||
}
|
||||
}
|
||||
@@ -205,9 +190,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Adding: {rewardParams.RewardReputation} {traderId.ToString()} trader reputation reward"
|
||||
);
|
||||
logger.Debug($"Adding: {rewardParams.RewardReputation} {traderId.ToString()} trader reputation reward");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,20 +213,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}"
|
||||
);
|
||||
logger.Debug($"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}");
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
protected QuestRewardValues GetQuestRewardValues(
|
||||
RewardScaling rewardScaling,
|
||||
double effectiveDifficulty,
|
||||
int pmcLevel
|
||||
)
|
||||
protected QuestRewardValues GetQuestRewardValues(RewardScaling rewardScaling, double effectiveDifficulty, int pmcLevel)
|
||||
{
|
||||
// difficulty could go from 0.2 ... -> for lowest difficulty receive 0.2*nominal reward
|
||||
var levelsConfig = rewardScaling.Levels;
|
||||
@@ -260,35 +237,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
SkillPointReward = mathUtil.Interp1(pmcLevel, levelsConfig, skillPointRewardConfig),
|
||||
SkillRewardChance = mathUtil.Interp1(pmcLevel, levelsConfig, skillRewardChanceConfig),
|
||||
RewardReputation = GetRewardRep(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
reputationConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
RewardReputation = GetRewardRep(effectiveDifficulty, pmcLevel, levelsConfig, reputationConfig, rewardSpreadConfig),
|
||||
RewardNumItems = GetRewardNumItems(pmcLevel, levelsConfig, itemsConfig),
|
||||
RewardRoubles = GetRewardRoubles(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
roublesConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
GpCoinRewardCount = GetGpCoinRewardCount(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
gpCoinConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
RewardXP = GetRewardXp(
|
||||
effectiveDifficulty,
|
||||
pmcLevel,
|
||||
levelsConfig,
|
||||
xpConfig,
|
||||
rewardSpreadConfig
|
||||
),
|
||||
RewardRoubles = GetRewardRoubles(effectiveDifficulty, pmcLevel, levelsConfig, roublesConfig, rewardSpreadConfig),
|
||||
GpCoinRewardCount = GetGpCoinRewardCount(effectiveDifficulty, pmcLevel, levelsConfig, gpCoinConfig, rewardSpreadConfig),
|
||||
RewardXP = GetRewardXp(effectiveDifficulty, pmcLevel, levelsConfig, xpConfig, rewardSpreadConfig),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -336,11 +289,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
return Math.Round(multiplier) / 100;
|
||||
}
|
||||
|
||||
protected int GetRewardNumItems(
|
||||
int pmcLevel,
|
||||
List<double> levelsConfig,
|
||||
List<double> itemsConfig
|
||||
)
|
||||
protected int GetRewardNumItems(int pmcLevel, List<double> levelsConfig, List<double> itemsConfig)
|
||||
{
|
||||
var interpolatedNumItems = mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig);
|
||||
|
||||
@@ -395,21 +344,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
if (itemHelper.IsOfBaseclass(chosenItemFromPool.Id, BaseClasses.AMMO))
|
||||
{
|
||||
// Don't reward ammo that stacks to less than what's allowed in config
|
||||
if (
|
||||
chosenItemFromPool.Properties.StackMaxSize
|
||||
< repeatableConfig.RewardAmmoStackMinSize
|
||||
)
|
||||
if (chosenItemFromPool.Properties.StackMaxSize < repeatableConfig.RewardAmmoStackMinSize)
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Choose the smallest value between budget, fitting size and stack max
|
||||
rewardItemStackCount = CalculateAmmoStackSizeThatFitsBudget(
|
||||
chosenItemFromPool,
|
||||
itemRewardBudget,
|
||||
maxItemCount
|
||||
);
|
||||
rewardItemStackCount = CalculateAmmoStackSizeThatFitsBudget(chosenItemFromPool, itemRewardBudget, maxItemCount);
|
||||
}
|
||||
|
||||
// 25% chance to double, triple or quadruple reward stack
|
||||
@@ -425,9 +367,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
var calculatedItemRewardBudget = itemRewardBudget - rewardItemStackCount * itemCost;
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}"
|
||||
);
|
||||
logger.Debug($"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}");
|
||||
}
|
||||
|
||||
// If we still have budget narrow down possible items
|
||||
@@ -444,9 +384,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining"
|
||||
);
|
||||
logger.Debug($"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,11 +404,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="roublesBudget"> Rouble budget </param>
|
||||
/// <param name="rewardNumItems"> Count of rewarded items </param>
|
||||
/// <returns> Count that fits budget (min 1) </returns>
|
||||
protected int CalculateAmmoStackSizeThatFitsBudget(
|
||||
TemplateItem itemSelected,
|
||||
double roublesBudget,
|
||||
int rewardNumItems
|
||||
)
|
||||
protected int CalculateAmmoStackSizeThatFitsBudget(TemplateItem itemSelected, double roublesBudget, int rewardNumItems)
|
||||
{
|
||||
// Calculate budget per reward item
|
||||
var stackRoubleBudget = roublesBudget / rewardNumItems;
|
||||
@@ -487,18 +421,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
return (int)Math.Clamp(stackSizeThatFitsBudget, 1, stackMaxCount);
|
||||
}
|
||||
|
||||
protected bool CanIncreaseRewardItemStackSize(
|
||||
TemplateItem item,
|
||||
int maxRoublePriceToStack,
|
||||
int randomChanceToPass = 100
|
||||
)
|
||||
protected bool CanIncreaseRewardItemStackSize(TemplateItem item, int maxRoublePriceToStack, int randomChanceToPass = 100)
|
||||
{
|
||||
var isEligibleForStackSizeIncrease =
|
||||
presetHelper.GetDefaultPresetOrItemPrice(item.Id) < maxRoublePriceToStack
|
||||
&& !itemHelper.IsOfBaseclasses(
|
||||
item.Id,
|
||||
[BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO]
|
||||
)
|
||||
&& !itemHelper.IsOfBaseclasses(item.Id, [BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO])
|
||||
&& !itemHelper.ItemRequiresSoftInserts(item.Id);
|
||||
|
||||
return isEligibleForStackSizeIncrease && randomUtil.GetChance100(randomChanceToPass);
|
||||
@@ -548,25 +475,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
var rewardableItemPool = GetRewardableItems(repeatableConfig, traderId);
|
||||
var minPrice = Math.Min(25000, 0.5 * roublesBudget.Value);
|
||||
|
||||
var rewardableItemPoolWithinBudget = FilterRewardPoolWithinBudget(
|
||||
rewardableItemPool,
|
||||
roublesBudget.Value,
|
||||
minPrice
|
||||
);
|
||||
var rewardableItemPoolWithinBudget = FilterRewardPoolWithinBudget(rewardableItemPool, roublesBudget.Value, minPrice);
|
||||
|
||||
if (rewardableItemPoolWithinBudget.Count == 0)
|
||||
{
|
||||
logger.Warning(
|
||||
localisationService.GetText(
|
||||
"repeatable-no_reward_item_found_in_price_range",
|
||||
new { minPrice, roublesBudget }
|
||||
)
|
||||
);
|
||||
logger.Warning(localisationService.GetText("repeatable-no_reward_item_found_in_price_range", new { minPrice, roublesBudget }));
|
||||
|
||||
// In case we don't find any items in the price range
|
||||
rewardableItemPoolWithinBudget = rewardableItemPool
|
||||
.Where(x => itemHelper.GetItemPrice(x.Id) < roublesBudget)
|
||||
.ToList();
|
||||
rewardableItemPoolWithinBudget = rewardableItemPool.Where(x => itemHelper.GetItemPrice(x.Id) < roublesBudget).ToList();
|
||||
}
|
||||
|
||||
return rewardableItemPoolWithinBudget;
|
||||
@@ -579,11 +495,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="roublesBudget"> The budget remaining for rewards </param>
|
||||
/// <param name="minPrice"> The minimum priced item to include </param>
|
||||
/// <returns> List of Items </returns>
|
||||
protected List<TemplateItem> FilterRewardPoolWithinBudget(
|
||||
List<TemplateItem> rewardItems,
|
||||
double roublesBudget,
|
||||
double minPrice
|
||||
)
|
||||
protected List<TemplateItem> FilterRewardPoolWithinBudget(List<TemplateItem> rewardItems, double roublesBudget, double minPrice)
|
||||
{
|
||||
return rewardItems
|
||||
.Where(item =>
|
||||
@@ -600,17 +512,10 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="roublesBudget"> Budget in roubles </param>
|
||||
/// <param name="rewardIndex"> Index of the reward </param>
|
||||
/// <returns> Dictionary of the reward and it's price, can return null. </returns>
|
||||
protected KeyValuePair<Reward, double>? GetRandomWeaponPresetWithinBudget(
|
||||
double roublesBudget,
|
||||
int rewardIndex
|
||||
)
|
||||
protected KeyValuePair<Reward, double>? GetRandomWeaponPresetWithinBudget(double roublesBudget, int rewardIndex)
|
||||
{
|
||||
// Add a random default preset weapon as reward
|
||||
var defaultPresetPool = new ExhaustableArray<Preset>(
|
||||
presetHelper.GetDefaultWeaponPresets().Values.ToList(),
|
||||
randomUtil,
|
||||
cloner
|
||||
);
|
||||
var defaultPresetPool = new ExhaustableArray<Preset>(presetHelper.GetDefaultWeaponPresets().Values.ToList(), randomUtil, cloner);
|
||||
|
||||
while (defaultPresetPool.HasValues())
|
||||
{
|
||||
@@ -631,12 +536,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
var chosenPreset = cloner.Clone(randomPreset);
|
||||
|
||||
return new KeyValuePair<Reward, double>(
|
||||
GeneratePresetReward(
|
||||
chosenPreset.Encyclopedia.Value,
|
||||
1,
|
||||
rewardIndex,
|
||||
chosenPreset.Items
|
||||
),
|
||||
GeneratePresetReward(chosenPreset.Encyclopedia.Value, 1, rewardIndex, chosenPreset.Items),
|
||||
presetPrice
|
||||
);
|
||||
}
|
||||
@@ -654,13 +554,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="preset"> Optional list of preset items </param>
|
||||
/// <param name="foundInRaid"> If generated Item is found in raid, default True </param>
|
||||
/// <returns> Object of "Reward"-item-type </returns>
|
||||
protected Reward GeneratePresetReward(
|
||||
MongoId tpl,
|
||||
int count,
|
||||
int index,
|
||||
List<Item>? preset,
|
||||
bool foundInRaid = true
|
||||
)
|
||||
protected Reward GeneratePresetReward(MongoId tpl, int count, int index, List<Item>? preset, bool foundInRaid = true)
|
||||
{
|
||||
var id = new MongoId();
|
||||
var questRewardItem = new Reward
|
||||
@@ -704,12 +598,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="index"> All rewards will be appended to a list, for unknown reasons the client wants the index</param>
|
||||
/// <param name="foundInRaid"> If generated Item is found in raid, default True </param>
|
||||
/// <returns> Object of "Reward"-item-type </returns>
|
||||
protected Reward GenerateItemReward(
|
||||
MongoId tpl,
|
||||
double count,
|
||||
int index,
|
||||
bool foundInRaid = true
|
||||
)
|
||||
protected Reward GenerateItemReward(MongoId tpl, double count, int index, bool foundInRaid = true)
|
||||
{
|
||||
var id = new MongoId();
|
||||
var questRewardItem = new Reward
|
||||
@@ -742,16 +631,10 @@ public class RepeatableQuestRewardGenerator(
|
||||
{
|
||||
// Determine currency based on trader
|
||||
// PK and Fence use Euros, everyone else is Roubles
|
||||
var currency =
|
||||
traderId == Traders.PEACEKEEPER || traderId == 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 =
|
||||
currency == Money.EUROS
|
||||
? handbookHelper.FromRUB(rewardRoubles, Money.EUROS)
|
||||
: rewardRoubles;
|
||||
var rewardAmountToGivePlayer = currency == Money.EUROS ? handbookHelper.FromRUB(rewardRoubles, Money.EUROS) : rewardRoubles;
|
||||
|
||||
// Get chosen currency + amount and return
|
||||
return GenerateItemReward(currency, rewardAmountToGivePlayer, rewardIndex, false);
|
||||
@@ -767,10 +650,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// <param name="repeatableQuestConfig"> Config </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,
|
||||
MongoId traderId
|
||||
)
|
||||
public List<TemplateItem> GetRewardableItems(RepeatableQuestConfig repeatableQuestConfig, MongoId traderId)
|
||||
{
|
||||
// Get an array of seasonal items that should not be shown right now as seasonal event is not active
|
||||
var seasonalItems = seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
@@ -793,9 +673,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
return false;
|
||||
}
|
||||
|
||||
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(trader =>
|
||||
trader.TraderId == traderId
|
||||
);
|
||||
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(trader => trader.TraderId == traderId);
|
||||
|
||||
return IsValidRewardItem(
|
||||
itemTemplate.Id,
|
||||
|
||||
Reference in New Issue
Block a user