.NET Format Style Fixes
This commit is contained in:
@@ -37,29 +37,10 @@ public class RepeatableQuestGenerator(
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, List<string>> _bodyPartsToClient = new()
|
||||
{
|
||||
{
|
||||
BodyParts.Arms, [
|
||||
BodyParts.LeftArm,
|
||||
BodyParts.RightArm
|
||||
]
|
||||
},
|
||||
{
|
||||
BodyParts.Legs, [
|
||||
BodyParts.LeftLeg,
|
||||
BodyParts.RightLeg
|
||||
]
|
||||
},
|
||||
{
|
||||
BodyParts.Head, [
|
||||
BodyParts.Head
|
||||
]
|
||||
},
|
||||
{
|
||||
BodyParts.Chest, [
|
||||
BodyParts.Chest,
|
||||
BodyParts.Stomach
|
||||
]
|
||||
},
|
||||
{ BodyParts.Arms, [BodyParts.LeftArm, BodyParts.RightArm] },
|
||||
{ BodyParts.Legs, [BodyParts.LeftLeg, BodyParts.RightLeg] },
|
||||
{ BodyParts.Head, [BodyParts.Head] },
|
||||
{ BodyParts.Chest, [BodyParts.Chest, BodyParts.Stomach] },
|
||||
};
|
||||
|
||||
protected int _maxRandomNumberAttempts = 6;
|
||||
@@ -88,8 +69,8 @@ public class RepeatableQuestGenerator(
|
||||
var questType = _randomUtil.DrawRandomFromList(questTypePool.Types).First();
|
||||
|
||||
// Get traders from whitelist and filter by quest type availability
|
||||
var traders = repeatableConfig.TraderWhitelist
|
||||
.Where(x => x.QuestTypes.Contains(questType))
|
||||
var traders = repeatableConfig
|
||||
.TraderWhitelist.Where(x => x.QuestTypes.Contains(questType))
|
||||
.Select(x => x.TraderId)
|
||||
.ToList();
|
||||
// filter out locked traders
|
||||
@@ -98,11 +79,34 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
return questType switch
|
||||
{
|
||||
"Elimination" => GenerateEliminationQuest(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
|
||||
"Completion" => GenerateCompletionQuest(sessionId, pmcLevel, traderId, repeatableConfig),
|
||||
"Exploration" => GenerateExplorationQuest(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
|
||||
"Pickup" => GeneratePickupQuest(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
|
||||
_ => null
|
||||
"Elimination" => GenerateEliminationQuest(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
questTypePool,
|
||||
repeatableConfig
|
||||
),
|
||||
"Completion" => GenerateCompletionQuest(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
repeatableConfig
|
||||
),
|
||||
"Exploration" => GenerateExplorationQuest(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
questTypePool,
|
||||
repeatableConfig
|
||||
),
|
||||
"Pickup" => GeneratePickupQuest(
|
||||
sessionId,
|
||||
pmcLevel,
|
||||
traderId,
|
||||
questTypePool,
|
||||
repeatableConfig
|
||||
),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -128,13 +132,31 @@ public class RepeatableQuestGenerator(
|
||||
{
|
||||
var rand = new Random();
|
||||
|
||||
var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
|
||||
var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(
|
||||
pmcLevel,
|
||||
repeatableConfig
|
||||
);
|
||||
var locationsConfig = repeatableConfig.Locations;
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(_mathUtil, _cloner, eliminationConfig.Targets);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.BodyParts);
|
||||
var weaponCategoryRequirementConfig =
|
||||
new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.WeaponCategoryRequirements);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.WeaponRequirements);
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
eliminationConfig.Targets
|
||||
);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
eliminationConfig.BodyParts
|
||||
);
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
eliminationConfig.WeaponCategoryRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
eliminationConfig.WeaponRequirements
|
||||
);
|
||||
|
||||
// the difficulty of the quest varies in difficulty depending on the condition
|
||||
// possible conditions are
|
||||
@@ -157,8 +179,7 @@ public class RepeatableQuestGenerator(
|
||||
// times the number of kills we have to perform):
|
||||
|
||||
// The minimum difficulty is the difficulty for the most probable (= easiest target) with no additional conditions
|
||||
var minDifficulty =
|
||||
1 / targetsConfig.MaxProbability(); // min difficulty is the lowest amount of scavs without any constraints
|
||||
var minDifficulty = 1 / targetsConfig.MaxProbability(); // min difficulty is the lowest amount of scavs without any constraints
|
||||
|
||||
// Target on bodyPart max. difficulty is that of the least probable element
|
||||
var maxTargetDifficulty = 1 / targetsConfig.MinProbability();
|
||||
@@ -172,7 +193,10 @@ public class RepeatableQuestGenerator(
|
||||
var targetPool = questTypePool.Pool.Elimination;
|
||||
targetsConfig = targetsConfig.Filter(x => targetPool.Targets.ContainsKey(x.Key));
|
||||
|
||||
if (targetsConfig.Count == 0 || targetsConfig.All(x => x.Data.IsBoss.GetValueOrDefault(false)))
|
||||
if (
|
||||
targetsConfig.Count == 0
|
||||
|| targetsConfig.All(x => x.Data.IsBoss.GetValueOrDefault(false))
|
||||
)
|
||||
{
|
||||
// There are no more targets left for elimination; delete it as a possible quest type
|
||||
// also if only bosses are left we need to leave otherwise it's a guaranteed boss elimination
|
||||
@@ -190,9 +214,13 @@ public class RepeatableQuestGenerator(
|
||||
// we use any as location if "any" is in the pool, and we don't hit the specific location random
|
||||
// we use any also if the random condition is not met in case only "any" was in the pool
|
||||
var locationKey = "any";
|
||||
if (locations.Contains("any") &&
|
||||
(eliminationConfig.SpecificLocationProbability < rand.NextDouble() || locations.Count <= 1)
|
||||
)
|
||||
if (
|
||||
locations.Contains("any")
|
||||
&& (
|
||||
eliminationConfig.SpecificLocationProbability < rand.NextDouble()
|
||||
|| locations.Count <= 1
|
||||
)
|
||||
)
|
||||
{
|
||||
locationKey = "any";
|
||||
targetPool.Targets.Remove(botTypeToEliminate);
|
||||
@@ -207,17 +235,21 @@ public class RepeatableQuestGenerator(
|
||||
locationKey = _randomUtil.DrawRandomFromList(locations).FirstOrDefault();
|
||||
|
||||
// Get a pool of locations the chosen bot type can be eliminated on
|
||||
if (!targetPool.Targets.TryGetValue(
|
||||
if (
|
||||
!targetPool.Targets.TryGetValue(
|
||||
botTypeToEliminate,
|
||||
out var possibleLocationPool
|
||||
))
|
||||
)
|
||||
)
|
||||
{
|
||||
_logger.Warning($"Bot to kill: {botTypeToEliminate} not found in elimination dict");
|
||||
_logger.Warning(
|
||||
$"Bot to kill: {botTypeToEliminate} not found in elimination dict"
|
||||
);
|
||||
}
|
||||
|
||||
// Filter locations bot can be killed on to just those not chosen by key
|
||||
possibleLocationPool.Locations = possibleLocationPool.Locations
|
||||
.Where(location => location != locationKey)
|
||||
possibleLocationPool.Locations = possibleLocationPool
|
||||
.Locations.Where(location => location != locationKey)
|
||||
.ToList();
|
||||
|
||||
// None left after filtering
|
||||
@@ -231,7 +263,11 @@ public class RepeatableQuestGenerator(
|
||||
else
|
||||
{
|
||||
// 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"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,49 +303,54 @@ public class RepeatableQuestGenerator(
|
||||
// Draw a distance condition
|
||||
int? distance = null;
|
||||
var distanceDifficulty = 0;
|
||||
var isDistanceRequirementAllowed = !eliminationConfig.DistLocationBlacklist.Contains(locationKey);
|
||||
var isDistanceRequirementAllowed = !eliminationConfig.DistLocationBlacklist.Contains(
|
||||
locationKey
|
||||
);
|
||||
|
||||
if (targetsConfig.Data(botTypeToEliminate).IsBoss.GetValueOrDefault(false))
|
||||
{
|
||||
// Get all boss spawn information
|
||||
var bossSpawns = _databaseService.GetLocations()
|
||||
var bossSpawns = _databaseService
|
||||
.GetLocations()
|
||||
.GetDictionary()
|
||||
.Select(x => x.Value)
|
||||
.Where(x => x.Base?.Id != null)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Base.Id,
|
||||
BossSpawn = x.Base.BossLocationSpawn
|
||||
}
|
||||
);
|
||||
.Select(x => new { x.Base.Id, BossSpawn = x.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)
|
||||
}
|
||||
)
|
||||
{
|
||||
x.Id,
|
||||
BossSpawn = x.BossSpawn.Where(e => e.BossName == botTypeToEliminate),
|
||||
})
|
||||
.Where(x => x.BossSpawn.Count() > 0);
|
||||
// remove blacklisted locations
|
||||
var allowedSpawns = thisBossSpawns.Where(x => !eliminationConfig.DistLocationBlacklist.Contains(x.Id));
|
||||
var allowedSpawns = thisBossSpawns.Where(x =>
|
||||
!eliminationConfig.DistLocationBlacklist.Contains(x.Id)
|
||||
);
|
||||
// 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 =
|
||||
isDistanceRequirementAllowed && allowedSpawns.Count() > 0;
|
||||
}
|
||||
|
||||
if (eliminationConfig.DistanceProbability > rand.NextDouble() && isDistanceRequirementAllowed)
|
||||
if (
|
||||
eliminationConfig.DistanceProbability > rand.NextDouble()
|
||||
&& isDistanceRequirementAllowed
|
||||
)
|
||||
{
|
||||
// Random distance with lower values more likely; simple distribution for starters...
|
||||
distance = (int) Math.Floor(
|
||||
Math.Abs(rand.NextDouble() - rand.NextDouble()) *
|
||||
(1 + eliminationConfig.MaxDistance - eliminationConfig.MinDistance) +
|
||||
eliminationConfig.MinDistance ??
|
||||
0
|
||||
);
|
||||
distance = (int)
|
||||
Math.Floor(
|
||||
Math.Abs(rand.NextDouble() - rand.NextDouble())
|
||||
* (1 + eliminationConfig.MaxDistance - eliminationConfig.MinDistance)
|
||||
+ eliminationConfig.MinDistance
|
||||
?? 0
|
||||
);
|
||||
|
||||
distance = (int) Math.Ceiling((decimal) (distance / 5)) * 5;
|
||||
distanceDifficulty = (int) (maxDistDifficulty * distance / eliminationConfig.MaxDistance);
|
||||
distance = (int)Math.Ceiling((decimal)(distance / 5)) * 5;
|
||||
distanceDifficulty = (int)(
|
||||
maxDistDifficulty * distance / eliminationConfig.MaxDistance
|
||||
);
|
||||
}
|
||||
|
||||
string? allowedWeaponsCategory = null;
|
||||
@@ -321,18 +362,18 @@ public class RepeatableQuestGenerator(
|
||||
List<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
|
||||
|
||||
// Filter out close range weapons from long distance requirement
|
||||
weaponCategoryRequirementConfig
|
||||
.RemoveAll(category => weaponTypeBlacklist
|
||||
.Contains(category.Key));
|
||||
weaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
}
|
||||
else if (distance < 20)
|
||||
{
|
||||
List<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
|
||||
|
||||
// Filter out far range weapons from close distance requirement
|
||||
weaponCategoryRequirementConfig
|
||||
.RemoveAll(category => weaponTypeBlacklist
|
||||
.Contains(category.Key));
|
||||
weaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
}
|
||||
|
||||
// Pick a weighted weapon category
|
||||
@@ -344,16 +385,25 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
// Only allow a specific weapon requirement if a weapon category was not chosen
|
||||
string? allowedWeapon = null;
|
||||
if (allowedWeaponsCategory is not null && eliminationConfig.WeaponRequirementProbability > rand.NextDouble())
|
||||
if (
|
||||
allowedWeaponsCategory is not null
|
||||
&& eliminationConfig.WeaponRequirementProbability > rand.NextDouble()
|
||||
)
|
||||
{
|
||||
var weaponRequirement = weaponRequirementConfig.Draw(1, false);
|
||||
var specificAllowedWeaponCategory = weaponRequirementConfig.Data(weaponRequirement[0]);
|
||||
var allowedWeapons = _itemHelper.GetItemTplsOfBaseType(specificAllowedWeaponCategory[0]);
|
||||
var allowedWeapons = _itemHelper.GetItemTplsOfBaseType(
|
||||
specificAllowedWeaponCategory[0]
|
||||
);
|
||||
allowedWeapon = _randomUtil.GetArrayValue(allowedWeapons);
|
||||
}
|
||||
|
||||
// Draw how many npm kills are required
|
||||
var desiredKillCount = GetEliminationKillCount(botTypeToEliminate, targetsConfig, eliminationConfig);
|
||||
var desiredKillCount = GetEliminationKillCount(
|
||||
botTypeToEliminate,
|
||||
targetsConfig,
|
||||
eliminationConfig
|
||||
);
|
||||
var killDifficulty = desiredKillCount;
|
||||
|
||||
// not perfectly happy here; we give difficulty = 1 to the quest reward generation when we have the most difficult mission
|
||||
@@ -372,7 +422,12 @@ public class RepeatableQuestGenerator(
|
||||
// crazy maximum difficulty will lead to a higher difficulty reward gain factor than 1
|
||||
var difficulty = _mathUtil.MapToRange(curDifficulty, minDifficulty, maxDifficulty, 0.5, 2);
|
||||
|
||||
var quest = GenerateRepeatableTemplate("Elimination", traderId, repeatableConfig.Side, sessionId);
|
||||
var quest = GenerateRepeatableTemplate(
|
||||
"Elimination",
|
||||
traderId,
|
||||
repeatableConfig.Side,
|
||||
sessionId
|
||||
);
|
||||
|
||||
// ASSUMPTION: All fence quests are for scavs
|
||||
if (traderId == Traders.FENCE)
|
||||
@@ -427,19 +482,29 @@ public class RepeatableQuestGenerator(
|
||||
protected int GetEliminationKillCount(
|
||||
string targetKey,
|
||||
ProbabilityObjectArray<string, BossInfo> targetsConfig,
|
||||
EliminationConfig eliminationConfig)
|
||||
EliminationConfig eliminationConfig
|
||||
)
|
||||
{
|
||||
if (targetsConfig.Data(targetKey).IsBoss.GetValueOrDefault(false))
|
||||
{
|
||||
return _randomUtil.RandInt(eliminationConfig.MinBossKills.Value, eliminationConfig.MaxBossKills + 1);
|
||||
return _randomUtil.RandInt(
|
||||
eliminationConfig.MinBossKills.Value,
|
||||
eliminationConfig.MaxBossKills + 1
|
||||
);
|
||||
}
|
||||
|
||||
if (targetsConfig.Data(targetKey).IsPmc.GetValueOrDefault(false))
|
||||
{
|
||||
return _randomUtil.RandInt(eliminationConfig.MinPmcKills.Value, eliminationConfig.MaxPmcKills + 1);
|
||||
return _randomUtil.RandInt(
|
||||
eliminationConfig.MinPmcKills.Value,
|
||||
eliminationConfig.MaxPmcKills + 1
|
||||
);
|
||||
}
|
||||
|
||||
return _randomUtil.RandInt(eliminationConfig.MinKills.Value, eliminationConfig.MaxKills + 1);
|
||||
return _randomUtil.RandInt(
|
||||
eliminationConfig.MinKills.Value,
|
||||
eliminationConfig.MaxKills + 1
|
||||
);
|
||||
}
|
||||
|
||||
protected double DifficultyWeighing(
|
||||
@@ -447,7 +512,8 @@ public class RepeatableQuestGenerator(
|
||||
double bodyPart,
|
||||
int dist,
|
||||
int kill,
|
||||
int weaponRequirement)
|
||||
int weaponRequirement
|
||||
)
|
||||
{
|
||||
return Math.Sqrt(Math.Sqrt(target) + bodyPart + dist + weaponRequirement) * kill;
|
||||
}
|
||||
@@ -466,7 +532,7 @@ public class RepeatableQuestGenerator(
|
||||
Id = _hashUtil.Generate(),
|
||||
DynamicLocale = true,
|
||||
Target = new ListOrT<string>(location, null),
|
||||
ConditionType = "Location"
|
||||
ConditionType = "Location",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -495,12 +561,8 @@ public class RepeatableQuestGenerator(
|
||||
Value = 1,
|
||||
ResetOnSessionEnd = false,
|
||||
EnemyHealthEffects = [],
|
||||
Daytime = new DaytimeCounter
|
||||
{
|
||||
From = 0,
|
||||
To = 0
|
||||
},
|
||||
ConditionType = "Kills"
|
||||
Daytime = new DaytimeCounter { From = 0, To = 0 },
|
||||
ConditionType = "Kills",
|
||||
};
|
||||
|
||||
if (target.StartsWith("boss"))
|
||||
@@ -521,7 +583,7 @@ public class RepeatableQuestGenerator(
|
||||
killConditionProps.Distance = new CounterConditionDistance
|
||||
{
|
||||
CompareMethod = ">=",
|
||||
Value = distance.Value
|
||||
Value = distance.Value,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -567,39 +629,49 @@ public class RepeatableQuestGenerator(
|
||||
var levelsConfig = repeatableConfig.RewardScaling.Levels;
|
||||
var roublesConfig = repeatableConfig.RewardScaling.Roubles;
|
||||
|
||||
var quest = GenerateRepeatableTemplate("Completion", traderId, repeatableConfig.Side, sessionId);
|
||||
var quest = GenerateRepeatableTemplate(
|
||||
"Completion",
|
||||
traderId,
|
||||
repeatableConfig.Side,
|
||||
sessionId
|
||||
);
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
// 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)
|
||||
(double)(_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier)
|
||||
);
|
||||
roublesBudget = Math.Max(roublesBudget, 5000d);
|
||||
var itemSelection = itemsToRetrievePool.Where(itemTpl => _itemHelper.GetItemPrice(itemTpl) < roublesBudget
|
||||
)
|
||||
var itemSelection = itemsToRetrievePool
|
||||
.Where(itemTpl => _itemHelper.GetItemPrice(itemTpl) < roublesBudget)
|
||||
.ToList();
|
||||
|
||||
// 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))
|
||||
{
|
||||
var itemWhitelist = _databaseService.GetTemplates().RepeatableQuests.Data.Completion.ItemsWhitelist;
|
||||
var itemWhitelist = _databaseService
|
||||
.GetTemplates()
|
||||
.RepeatableQuests.Data.Completion.ItemsWhitelist;
|
||||
|
||||
// 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), []);
|
||||
itemSelection = itemSelection.Where(x =>
|
||||
{
|
||||
// Whitelist can contain item tpls and item base type ids
|
||||
return itemIdsWhitelisted.Any(v => _itemHelper.IsOfBaseclass(x, v)) ||
|
||||
itemIdsWhitelisted.Contains(x);
|
||||
}
|
||||
)
|
||||
itemSelection = itemSelection
|
||||
.Where(x =>
|
||||
{
|
||||
// Whitelist can contain item tpls and item base type ids
|
||||
return itemIdsWhitelisted.Any(v => _itemHelper.IsOfBaseclass(x, v))
|
||||
|| itemIdsWhitelisted.Contains(x);
|
||||
})
|
||||
.ToList();
|
||||
// check if items are missing
|
||||
// var flatList = itemSelection.reduce((a, il) => a.concat(il[0]), []);
|
||||
@@ -608,7 +680,9 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
if (repeatableConfig.QuestConfig.Completion.UseBlacklist.GetValueOrDefault(false))
|
||||
{
|
||||
var itemBlacklist = _databaseService.GetTemplates().RepeatableQuests.Data.Completion.ItemsBlacklist;
|
||||
var itemBlacklist = _databaseService
|
||||
.GetTemplates()
|
||||
.RepeatableQuests.Data.Completion.ItemsBlacklist;
|
||||
|
||||
// Filter and concatenate the arrays according to current player level
|
||||
var itemIdsBlacklisted = itemBlacklist
|
||||
@@ -616,12 +690,12 @@ public class RepeatableQuestGenerator(
|
||||
.SelectMany(x => x.ItemIds)
|
||||
.ToHashSet(); //.Aggregate(List<ItemsBlacklist> , (a, p) => a.Concat(p.ItemIds) );
|
||||
|
||||
itemSelection = itemSelection.Where(x =>
|
||||
{
|
||||
return itemIdsBlacklisted.All(v => !_itemHelper.IsOfBaseclass(x, v)) ||
|
||||
!itemIdsBlacklisted.Contains(x);
|
||||
}
|
||||
)
|
||||
itemSelection = itemSelection
|
||||
.Where(x =>
|
||||
{
|
||||
return itemIdsBlacklisted.All(v => !_itemHelper.IsOfBaseclass(x, v))
|
||||
|| !itemIdsBlacklisted.Contains(x);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -638,7 +712,10 @@ public class RepeatableQuestGenerator(
|
||||
}
|
||||
|
||||
// Store the indexes of items we are asking player to supply
|
||||
var distinctItemsToRetrieveCount = _randomUtil.GetInt(1, completionConfig.UniqueItemCount.Value);
|
||||
var distinctItemsToRetrieveCount = _randomUtil.GetInt(
|
||||
1,
|
||||
completionConfig.UniqueItemCount.Value
|
||||
);
|
||||
var chosenRequirementItemsTpls = new List<string>();
|
||||
var usedItemIndexes = new HashSet<int>();
|
||||
for (var i = 0; i < distinctItemsToRetrieveCount; i++)
|
||||
@@ -664,11 +741,7 @@ public class RepeatableQuestGenerator(
|
||||
_logger.Error(
|
||||
_localisationService.GetText(
|
||||
"repeatable-no_reward_item_found_in_price_range",
|
||||
new
|
||||
{
|
||||
minPrice = 0,
|
||||
roublesBudget
|
||||
}
|
||||
new { minPrice = 0, roublesBudget }
|
||||
)
|
||||
);
|
||||
|
||||
@@ -686,11 +759,11 @@ public class RepeatableQuestGenerator(
|
||||
var value = minValue;
|
||||
|
||||
// Get the value range within budget
|
||||
var x = (int) Math.Floor(roublesBudget / itemPrice);
|
||||
var x = (int)Math.Floor(roublesBudget / itemPrice);
|
||||
maxValue = Math.Min(maxValue, x);
|
||||
if (maxValue > minValue)
|
||||
// If it doesn't blow the budget we have for the request, draw a random amount of the selected
|
||||
// Item type to be requested
|
||||
// If it doesn't blow the budget we have for the request, draw a random amount of the selected
|
||||
// Item type to be requested
|
||||
{
|
||||
value = _randomUtil.RandInt(minValue, maxValue + 1);
|
||||
}
|
||||
@@ -699,13 +772,20 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
// Push a CompletionCondition with the item and the amount of the item into quest
|
||||
chosenRequirementItemsTpls.Add(tplChosen);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCompletionAvailableForFinish(tplChosen, value, repeatableConfig.QuestConfig.Completion));
|
||||
quest.Conditions.AvailableForFinish.Add(
|
||||
GenerateCompletionAvailableForFinish(
|
||||
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)
|
||||
itemSelection = itemSelection
|
||||
.Where(tpl => _itemHelper.GetItemPrice(tpl) < roublesBudget)
|
||||
.ToList();
|
||||
if (!itemSelection.Any())
|
||||
{
|
||||
@@ -737,35 +817,39 @@ public class RepeatableQuestGenerator(
|
||||
/// <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<string> GetItemsToRetrievePool(Completion completionConfig, HashSet<string> itemTplBlacklist)
|
||||
protected HashSet<string> GetItemsToRetrievePool(
|
||||
Completion completionConfig,
|
||||
HashSet<string> itemTplBlacklist
|
||||
)
|
||||
{
|
||||
// Get seasonal items that should not be added to pool as seasonal event is not active
|
||||
var seasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
|
||||
// Check for specific base classes which don't make sense as reward item
|
||||
// also check if the price is greater than 0; there are some items whose price can not be found
|
||||
return _databaseService.GetItems()
|
||||
return _databaseService
|
||||
.GetItems()
|
||||
.Values.Where(itemTemplate =>
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (itemTemplate.Parent == string.Empty)
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (itemTemplate.Parent == string.Empty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seasonalItems.Contains(itemTemplate.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid reward items share same logic as items to retrieve
|
||||
return _repeatableQuestRewardGenerator.IsValidRewardItem(
|
||||
itemTemplate.Id,
|
||||
itemTplBlacklist,
|
||||
completionConfig.RequiredItemTypeBlacklist
|
||||
);
|
||||
return false;
|
||||
}
|
||||
).Select(item => item.Id)
|
||||
|
||||
if (seasonalItems.Contains(itemTemplate.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid reward items share same logic as items to retrieve
|
||||
return _repeatableQuestRewardGenerator.IsValidRewardItem(
|
||||
itemTemplate.Id,
|
||||
itemTplBlacklist,
|
||||
completionConfig.RequiredItemTypeBlacklist
|
||||
);
|
||||
})
|
||||
.Select(item => item.Id)
|
||||
.ToHashSet();
|
||||
}
|
||||
|
||||
@@ -779,13 +863,23 @@ public class RepeatableQuestGenerator(
|
||||
/// <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 GenerateCompletionAvailableForFinish(string itemTpl,
|
||||
protected QuestCondition GenerateCompletionAvailableForFinish(
|
||||
string itemTpl,
|
||||
double value,
|
||||
Completion completionConfig)
|
||||
Completion completionConfig
|
||||
)
|
||||
{
|
||||
var onlyFoundInRaid = completionConfig.RequiredItemsAreFiR;
|
||||
var minDurability = _itemHelper.IsOfBaseclasses(itemTpl, [BaseClasses.WEAPON, BaseClasses.ARMOR])
|
||||
? _randomUtil.GetArrayValue([completionConfig.RequiredItemMinDurabilityMinMax.Min, completionConfig.RequiredItemMinDurabilityMinMax.Max])
|
||||
var minDurability = _itemHelper.IsOfBaseclasses(
|
||||
itemTpl,
|
||||
[BaseClasses.WEAPON, BaseClasses.ARMOR]
|
||||
)
|
||||
? _randomUtil.GetArrayValue(
|
||||
[
|
||||
completionConfig.RequiredItemMinDurabilityMinMax.Min,
|
||||
completionConfig.RequiredItemMinDurabilityMinMax.Max,
|
||||
]
|
||||
)
|
||||
: 0;
|
||||
|
||||
// Dog tags MUST NOT be FiR for them to work
|
||||
@@ -809,7 +903,7 @@ public class RepeatableQuestGenerator(
|
||||
DogtagLevel = 0,
|
||||
OnlyFoundInRaid = onlyFoundInRaid,
|
||||
IsEncoded = false,
|
||||
ConditionType = "HandoverItem"
|
||||
ConditionType = "HandoverItem",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -830,11 +924,13 @@ public class RepeatableQuestGenerator(
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig)
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
{
|
||||
var explorationConfig = repeatableConfig.QuestConfig.Exploration;
|
||||
var requiresSpecificExtract =
|
||||
_randomUtil.Random.Next() < repeatableConfig.QuestConfig.Exploration.SpecificExits.Probability;
|
||||
_randomUtil.Random.Next()
|
||||
< repeatableConfig.QuestConfig.Exploration.SpecificExits.Probability;
|
||||
|
||||
if (questTypePool.Pool.Exploration.Locations.Count == 0)
|
||||
{
|
||||
@@ -845,7 +941,9 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
// If location drawn is factory, it's possible to either get factory4_day and factory4_night or only one
|
||||
// of the both
|
||||
var locationKey = _randomUtil.DrawRandomFromDict(questTypePool.Pool.Exploration.Locations)[0];
|
||||
var locationKey = _randomUtil.DrawRandomFromDict(questTypePool.Pool.Exploration.Locations)[
|
||||
0
|
||||
];
|
||||
var locationTarget = questTypePool.Pool.Exploration.Locations[locationKey];
|
||||
|
||||
// Remove the location from the available pool
|
||||
@@ -857,25 +955,34 @@ public class RepeatableQuestGenerator(
|
||||
: explorationConfig.MaximumExtracts + 1;
|
||||
var numExtracts = _randomUtil.RandInt(1, exitTimesMax);
|
||||
|
||||
var quest = GenerateRepeatableTemplate("Exploration", traderId, repeatableConfig.Side, sessionId);
|
||||
var quest = GenerateRepeatableTemplate(
|
||||
"Exploration",
|
||||
traderId,
|
||||
repeatableConfig.Side,
|
||||
sessionId
|
||||
);
|
||||
|
||||
var exitStatusCondition = new QuestConditionCounterCondition
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
DynamicLocale = true,
|
||||
Status = ["Survived"],
|
||||
ConditionType = "ExitStatus"
|
||||
ConditionType = "ExitStatus",
|
||||
};
|
||||
var locationCondition = new QuestConditionCounterCondition
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
DynamicLocale = true,
|
||||
Target = new ListOrT<string>(locationTarget, null),
|
||||
ConditionType = "Location"
|
||||
ConditionType = "Location",
|
||||
};
|
||||
|
||||
quest.Conditions.AvailableForFinish[0].Counter.Id = _hashUtil.Generate();
|
||||
quest.Conditions.AvailableForFinish[0].Counter.Conditions = [exitStatusCondition, locationCondition];
|
||||
quest.Conditions.AvailableForFinish[0].Counter.Conditions =
|
||||
[
|
||||
exitStatusCondition,
|
||||
locationCondition,
|
||||
];
|
||||
quest.Conditions.AvailableForFinish[0].Value = numExtracts;
|
||||
quest.Conditions.AvailableForFinish[0].Id = _hashUtil.Generate();
|
||||
quest.Location = GetQuestLocationByMapId(locationKey.ToString());
|
||||
@@ -889,9 +996,10 @@ public class RepeatableQuestGenerator(
|
||||
var exitPool = mapExits.Where(exit => exit.Chance > 0).ToList();
|
||||
|
||||
// Exclude exits with a requirement to leave (e.g. car extracts)
|
||||
var possibleExits = exitPool.Where(exit =>
|
||||
exit.PassageRequirement is not null ||
|
||||
repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains(
|
||||
var possibleExits = exitPool
|
||||
.Where(exit =>
|
||||
exit.PassageRequirement is not null
|
||||
|| repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains(
|
||||
"PassageRequirement"
|
||||
)
|
||||
)
|
||||
@@ -899,7 +1007,9 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
if (possibleExits.Count == 0)
|
||||
{
|
||||
_logger.Error($"Unable to choose specific exit on map: {locationKey}, Possible exit pool was empty");
|
||||
_logger.Error(
|
||||
$"Unable to choose specific exit on map: {locationKey}, Possible exit pool was empty"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -914,7 +1024,13 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
// Difficulty for exploration goes from 1 extract to maxExtracts
|
||||
// Difficulty for reward goes from 0.2...1 -> map
|
||||
var difficulty = _mathUtil.MapToRange(numExtracts, 1, explorationConfig.MaximumExtracts.Value, 0.2, 1);
|
||||
var difficulty = _mathUtil.MapToRange(
|
||||
numExtracts,
|
||||
1,
|
||||
explorationConfig.MaximumExtracts.Value,
|
||||
0.2,
|
||||
1
|
||||
);
|
||||
quest.Rewards = _repeatableQuestRewardGenerator.GenerateReward(
|
||||
pmcLevel,
|
||||
difficulty,
|
||||
@@ -944,13 +1060,21 @@ public class RepeatableQuestGenerator(
|
||||
int pmcLevel,
|
||||
string traderId,
|
||||
QuestTypePool questTypePool,
|
||||
RepeatableQuestConfig repeatableConfig)
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
{
|
||||
var pickupConfig = repeatableConfig.QuestConfig.Pickup;
|
||||
|
||||
var quest = GenerateRepeatableTemplate("Pickup", traderId, repeatableConfig.Side, sessionId);
|
||||
var quest = GenerateRepeatableTemplate(
|
||||
"Pickup",
|
||||
traderId,
|
||||
repeatableConfig.Side,
|
||||
sessionId
|
||||
);
|
||||
|
||||
var itemTypeToFetchWithCount = _randomUtil.GetArrayValue(pickupConfig.ItemTypeToFetchWithMaxCount);
|
||||
var itemTypeToFetchWithCount = _randomUtil.GetArrayValue(
|
||||
pickupConfig.ItemTypeToFetchWithMaxCount
|
||||
);
|
||||
var itemCountToFetch = _randomUtil.RandInt(
|
||||
itemTypeToFetchWithCount.MinimumPickupCount.Value,
|
||||
itemTypeToFetchWithCount.MaximumPickupCount + 1
|
||||
@@ -959,18 +1083,25 @@ public class RepeatableQuestGenerator(
|
||||
// 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]];
|
||||
equipmentCondition.EquipmentInclusive =
|
||||
[
|
||||
[itemTypeToFetchWithCount.ItemType],
|
||||
];
|
||||
|
||||
// Add rewards
|
||||
quest.Rewards = _repeatableQuestRewardGenerator.GenerateReward(
|
||||
@@ -1007,7 +1138,7 @@ public class RepeatableQuestGenerator(
|
||||
Id = _hashUtil.Generate(),
|
||||
DynamicLocale = true,
|
||||
ExitName = exit.Name,
|
||||
ConditionType = "ExitName"
|
||||
ConditionType = "ExitName",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1027,7 +1158,8 @@ public class RepeatableQuestGenerator(
|
||||
string type,
|
||||
string traderId,
|
||||
string side,
|
||||
string sessionId)
|
||||
string sessionId
|
||||
)
|
||||
{
|
||||
RepeatableQuest questData = null;
|
||||
switch (type)
|
||||
@@ -1082,35 +1214,35 @@ public class RepeatableQuestGenerator(
|
||||
// Force REF templates to use prapors ID - solves missing text issue
|
||||
var desiredTraderId = traderId == Traders.REF ? Traders.PRAPOR : traderId;
|
||||
|
||||
questClone.Name = questClone.Name
|
||||
.Replace("{traderId}", traderId)
|
||||
questClone.Name = questClone
|
||||
.Name.Replace("{traderId}", traderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.Note = questClone.Note
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.Note = questClone
|
||||
.Note.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.Description = questClone.Description
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.Description = questClone
|
||||
.Description.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.SuccessMessageText = questClone.SuccessMessageText
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.SuccessMessageText = questClone
|
||||
.SuccessMessageText.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.FailMessageText = questClone.FailMessageText
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.FailMessageText = questClone
|
||||
.FailMessageText.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.StartedMessageText = questClone.StartedMessageText
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.StartedMessageText = questClone
|
||||
.StartedMessageText.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.ChangeQuestMessageText = questClone.ChangeQuestMessageText
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.ChangeQuestMessageText = questClone
|
||||
.ChangeQuestMessageText.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.AcceptPlayerMessage = questClone.AcceptPlayerMessage
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.AcceptPlayerMessage = questClone
|
||||
.AcceptPlayerMessage.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.DeclinePlayerMessage = questClone.DeclinePlayerMessage
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.DeclinePlayerMessage = questClone
|
||||
.DeclinePlayerMessage.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
questClone.CompletePlayerMessage = questClone.CompletePlayerMessage
|
||||
.Replace("{traderId}", desiredTraderId)
|
||||
questClone.CompletePlayerMessage = questClone
|
||||
.CompletePlayerMessage.Replace("{traderId}", desiredTraderId)
|
||||
.Replace("{templateId}", questClone.TemplateId);
|
||||
|
||||
questClone.QuestStatus.Id = _hashUtil.Generate();
|
||||
|
||||
Reference in New Issue
Block a user