.NET Format Style Fixes

This commit is contained in:
refringe
2025-06-18 17:09:20 +00:00
committed by Format Bot
parent ca0a7d6345
commit 6e01428b2b
774 changed files with 23507 additions and 40003 deletions
-5
View File
@@ -1,12 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Build.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="FastCloner" Version="3.3.8" />
@@ -15,11 +12,9 @@
<ProjectReference Include="..\Libraries\SPTarkov.Common\SPTarkov.Common.csproj" />
<ProjectReference Include="..\Libraries\SPTarkov.DI\SPTarkov.DI.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
+2 -3
View File
@@ -20,9 +20,8 @@ public class ClonerBenchmarks
[GlobalSetup]
public void Setup()
{
var jsonUtil = new JsonUtil([ new SptJsonConverterRegistrator() ]);
var importer = new ImporterUtil(new MockLogger<ImporterUtil>(), new FileUtil(),
jsonUtil);
var jsonUtil = new JsonUtil([new SptJsonConverterRegistrator()]);
var importer = new ImporterUtil(new MockLogger<ImporterUtil>(), new FileUtil(), jsonUtil);
var loadTask = importer.LoadRecursiveAsync<Templates>("./Assets/database/templates/");
loadTask.Wait();
_templates = loadTask.Result;
+6 -1
View File
@@ -6,7 +6,12 @@ namespace Benchmarks.Mock;
public class MockLogger<T> : ISptLogger<T>
{
public void LogWithColor(string data, LogTextColor? textColor = null, LogBackgroundColor? backgroundColor = null, Exception? ex = null)
public void LogWithColor(
string data,
LogTextColor? textColor = null,
LogBackgroundColor? backgroundColor = null,
Exception? ex = null
)
{
throw new NotImplementedException();
}
+7 -9
View File
@@ -1,11 +1,9 @@
<Project>
<PropertyGroup>
<Version>4.0.0-prerelease2</Version>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<RepositoryType>Git</RepositoryType>
</PropertyGroup>
<PropertyGroup>
<Version>4.0.0-prerelease2</Version>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<RepositoryType>Git</RepositoryType>
</PropertyGroup>
</Project>
@@ -8,7 +8,10 @@ public static class MemberInfoExtensions
public static string GetJsonName(this MemberInfo memberInfo)
{
return Attribute.IsDefined(memberInfo, typeof(JsonPropertyNameAttribute))
? (Attribute.GetCustomAttribute(memberInfo, typeof(JsonPropertyNameAttribute)) as JsonPropertyNameAttribute).Name
? (
Attribute.GetCustomAttribute(memberInfo, typeof(JsonPropertyNameAttribute))
as JsonPropertyNameAttribute
).Name
: memberInfo.Name;
}
}
@@ -6,7 +6,8 @@ namespace SPTarkov.Common.Extensions;
public static class ObjectExtensions
{
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _indexedProperties = new();
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _indexedProperties =
new();
private static readonly Lock _indexedPropertiesLockObject = new();
private static bool TryGetCachedProperty(Type type, string key, out PropertyInfo cachedProperty)
@@ -15,7 +16,8 @@ public static class ObjectExtensions
{
if (!_indexedProperties.TryGetValue(type, out var properties))
{
properties = type.GetProperties().ToDictionary(prop => prop.GetJsonName(), prop => prop);
properties = type.GetProperties()
.ToDictionary(prop => prop.GetJsonName(), prop => prop);
_indexedProperties.Add(type, properties);
}
@@ -49,7 +51,7 @@ public static class ObjectExtensions
return default;
}
return (T?) cachedProperty.GetValue(obj);
return (T?)cachedProperty.GetValue(obj);
}
public static List<object> GetAllPropValuesAsList(this object? obj)
@@ -8,7 +8,11 @@ public static class StringExtensions
private static readonly Dictionary<string, Regex> _regexCache = new();
private static readonly Lock _regexCacheLock = new();
public static string RegexReplace(this string source, [StringSyntax(StringSyntaxAttribute.Regex)] string regexString, string newValue)
public static string RegexReplace(
this string source,
[StringSyntax(StringSyntaxAttribute.Regex)] string regexString,
string newValue
)
{
Regex regex;
lock (_regexCacheLock)
@@ -23,7 +27,11 @@ public static class StringExtensions
return regex.Replace(source, newValue);
}
public static bool RegexMatch(this string source, [StringSyntax(StringSyntaxAttribute.Regex)] string regexString, out Match? matchedString)
public static bool RegexMatch(
this string source,
[StringSyntax(StringSyntaxAttribute.Regex)] string regexString,
out Match? matchedString
)
{
Regex regex;
lock (_regexCacheLock)
@@ -1,7 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\Build.props"/>
<Import Project="..\..\Build.props" />
<PropertyGroup>
<PackageId>SPTarkov.Common</PackageId>
<Authors>Single Player Tarkov</Authors>
@@ -14,13 +12,10 @@
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SemanticVersioning" Version="3.0.0"/>
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath=""/>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
</Project>
@@ -1,30 +1,22 @@
namespace SPTarkov.DI.Annotations;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type? typeOverride = null, int typePriority = int.MaxValue) : Attribute
public class Injectable(
InjectionType injectionType = InjectionType.Scoped,
Type? typeOverride = null,
int typePriority = int.MaxValue
) : Attribute
{
public InjectionType InjectionType
{
get;
set;
} = injectionType;
public InjectionType InjectionType { get; set; } = injectionType;
public int TypePriority
{
get;
set;
} = typePriority;
public int TypePriority { get; set; } = typePriority;
public Type? TypeOverride
{
get;
set;
} = typeOverride;
public Type? TypeOverride { get; set; } = typeOverride;
}
public enum InjectionType
{
Singleton,
Transient,
Scoped
Scoped,
}
@@ -16,7 +16,6 @@ public class DependencyInjectionHandler
private bool _oneTimeUseFlag;
public DependencyInjectionHandler(IServiceCollection serviceCollection)
{
_serviceCollection = serviceCollection;
@@ -43,8 +42,9 @@ public class DependencyInjectionHandler
public void AddInjectableTypesFromTypeList(IEnumerable<Type> types)
{
var typesToInject = types.Where(type =>
Attribute.IsDefined(type, typeof(Injectable)) &&
!_injectedTypeNames.ContainsKey($"{type.Namespace}.{type.Name}"));
Attribute.IsDefined(type, typeof(Injectable))
&& !_injectedTypeNames.ContainsKey($"{type.Namespace}.{type.Name}")
);
if (typesToInject.Any())
{
foreach (var type in typesToInject)
@@ -58,16 +58,24 @@ public class DependencyInjectionHandler
{
if (_oneTimeUseFlag)
{
throw new Exception("Invalid usage of DependencyInjectionHandler, this is a one time use service!");
throw new Exception(
"Invalid usage of DependencyInjectionHandler, this is a one time use service!"
);
}
_oneTimeUseFlag = true;
var typeRefValues = _injectedTypeNames.Values
.Select(t => new TypeRefContainer(((Injectable[]) Attribute.GetCustomAttributes(t, typeof(Injectable)))[0], t, t));
var typeRefValues = _injectedTypeNames.Values.Select(t => new TypeRefContainer(
((Injectable[])Attribute.GetCustomAttributes(t, typeof(Injectable)))[0],
t,
t
));
// All the components that have a type override, we need to find them and remove them before injecting everything
var componentsToRemove = typeRefValues.Where(tr => tr.InjectableAttribute.TypeOverride != null).Select(tr =>
string.IsNullOrEmpty(tr.InjectableAttribute.TypeOverride!.FullName)
? $"{tr.InjectableAttribute.TypeOverride.Namespace}.{tr.InjectableAttribute.TypeOverride.Name}"
: tr.InjectableAttribute.TypeOverride.FullName!)
var componentsToRemove = typeRefValues
.Where(tr => tr.InjectableAttribute.TypeOverride != null)
.Select(tr =>
string.IsNullOrEmpty(tr.InjectableAttribute.TypeOverride!.FullName)
? $"{tr.InjectableAttribute.TypeOverride.Namespace}.{tr.InjectableAttribute.TypeOverride.Name}"
: tr.InjectableAttribute.TypeOverride.FullName!
)
.ToHashSet();
// All the components without the removed overrides
var cleanedComponents = typeRefValues.Where(tr =>
@@ -78,17 +86,27 @@ public class DependencyInjectionHandler
return !componentsToRemove.Contains(name);
});
// All the components sorted and ready to be inserted into the DI container
var sortedInjectableTypes = cleanedComponents.OrderBy(tRef => tRef.InjectableAttribute.TypePriority);
var sortedInjectableTypes = cleanedComponents.OrderBy(tRef =>
tRef.InjectableAttribute.TypePriority
);
foreach (var typeRefToInject in sortedInjectableTypes)
{
var nodes = new Queue<TypeRefContainer>();
nodes.Enqueue(typeRefToInject);
foreach (var implementedInterface in typeRefToInject.Type.GetInterfaces()
.Where(t => !t.Namespace.StartsWith("System")))
foreach (
var implementedInterface in typeRefToInject
.Type.GetInterfaces()
.Where(t => !t.Namespace.StartsWith("System"))
)
{
nodes.Enqueue(new TypeRefContainer(typeRefToInject.InjectableAttribute, typeRefToInject.Type,
implementedInterface));
nodes.Enqueue(
new TypeRefContainer(
typeRefToInject.InjectableAttribute,
typeRefToInject.Type,
implementedInterface
)
);
}
while (nodes.Any())
@@ -96,8 +114,13 @@ public class DependencyInjectionHandler
var node = nodes.Dequeue();
if (node.Type.BaseType != null && node.Type.BaseType != typeof(object))
{
nodes.Enqueue(new TypeRefContainer(node.InjectableAttribute, typeRefToInject.Type,
node.Type.BaseType));
nodes.Enqueue(
new TypeRefContainer(
node.InjectableAttribute,
typeRefToInject.Type,
node.Type.BaseType
)
);
}
if (node.Type.IsGenericType)
@@ -106,9 +129,11 @@ public class DependencyInjectionHandler
}
else
{
RegisterComponent(node.InjectableAttribute.InjectionType,
RegisterComponent(
node.InjectableAttribute.InjectionType,
node.Type,
node.ParentType);
node.ParentType
);
}
}
}
@@ -118,7 +143,10 @@ public class DependencyInjectionHandler
{
try
{
_allLoadedTypes ??= AppDomain.CurrentDomain.GetAssemblies().SelectMany(t => t.GetTypes()).ToList();
_allLoadedTypes ??= AppDomain
.CurrentDomain.GetAssemblies()
.SelectMany(t => t.GetTypes())
.ToList();
}
catch (ReflectionTypeLoadException ex)
{
@@ -130,10 +158,12 @@ public class DependencyInjectionHandler
var typeName = $"{typeRef.Type.Namespace}.{typeRef.Type.Name}";
try
{
var matchedConstructors = _allConstructors.Where(c => c.GetParameters()
.Any(p => p.ParameterType.IsGenericType &&
p.ParameterType.GetGenericTypeDefinition().FullName == typeName
)
var matchedConstructors = _allConstructors.Where(c =>
c.GetParameters()
.Any(p =>
p.ParameterType.IsGenericType
&& p.ParameterType.GetGenericTypeDefinition().FullName == typeName
)
);
var constructorInfos = matchedConstructors.ToList();
@@ -145,7 +175,11 @@ public class DependencyInjectionHandler
foreach (var matchedConstructor in constructorInfos)
{
var constructorParams = matchedConstructor.GetParameters();
foreach (var parameterInfo in constructorParams.Where(x => IsMatchingGenericType(x, typeName)))
foreach (
var parameterInfo in constructorParams.Where(x =>
IsMatchingGenericType(x, typeName)
)
)
{
var parameters = parameterInfo.ParameterType.GetGenericArguments();
var typedGeneric = typeRef.ParentType.MakeGenericType(parameters);
@@ -166,8 +200,8 @@ public class DependencyInjectionHandler
private static bool IsMatchingGenericType(ParameterInfo paramInfo, string typeName)
{
return paramInfo.ParameterType.IsGenericType &&
paramInfo.ParameterType.GetGenericTypeDefinition().FullName == typeName;
return paramInfo.ParameterType.IsGenericType
&& paramInfo.ParameterType.GetGenericTypeDefinition().FullName == typeName;
}
private void RegisterComponent(
@@ -188,7 +222,10 @@ public class DependencyInjectionHandler
_serviceCollection.AddScoped(registrableInterface, implementationType);
break;
default:
throw new ArgumentOutOfRangeException(nameof(injectionType), $"Unknown injection type on {implementationType.Namespace}.{implementationType.Name}");
throw new ArgumentOutOfRangeException(
nameof(injectionType),
$"Unknown injection type on {implementationType.Namespace}.{implementationType.Name}"
);
}
}
@@ -197,20 +234,23 @@ public class DependencyInjectionHandler
var serviceKey = $"{implementationType.Namespace}.{implementationType.Name}";
if (registrableInterface != implementationType)
{
_serviceCollection.AddSingleton(registrableInterface, (serviceProvider) =>
{
object service;
lock (_injectedValuesLock)
_serviceCollection.AddSingleton(
registrableInterface,
(serviceProvider) =>
{
if (!_injectedValues.TryGetValue(serviceKey, out service))
object service;
lock (_injectedValuesLock)
{
service = serviceProvider.GetService(implementationType);
_injectedValues.Add(serviceKey, service);
if (!_injectedValues.TryGetValue(serviceKey, out service))
{
service = serviceProvider.GetService(implementationType);
_injectedValues.Add(serviceKey, service);
}
}
}
return service;
});
return service;
}
);
}
else
{
+4 -7
View File
@@ -1,7 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\Build.props" />
<PropertyGroup>
<PackageId>SPTarkov.DI</PackageId>
<Authors>Single Player Tarkov</Authors>
@@ -14,17 +12,16 @@
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.5" />
<PackageReference
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
Version="9.0.5"
/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SPTarkov.Common\SPTarkov.Common.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
</Project>
@@ -2,10 +2,7 @@
public class SingletonStateHolder<T>
{
public T State
{
get;
}
public T State { get; }
public SingletonStateHolder(T state)
{
@@ -27,35 +27,76 @@ public class CodeGenerator
return new CodeInstruction(code.OpCode) { labels = GetLabelList(code) };
}
if (code.OpCode == OpCodes.Ldfld || code.OpCode == OpCodes.Ldflda || code.OpCode == OpCodes.Stfld)
if (
code.OpCode == OpCodes.Ldfld
|| code.OpCode == OpCodes.Ldflda
|| code.OpCode == OpCodes.Stfld
)
{
return new CodeInstruction(code.OpCode, AccessTools.Field(code.CallerType, code.OperandTarget as string)) { labels = GetLabelList(code) };
return new CodeInstruction(
code.OpCode,
AccessTools.Field(code.CallerType, code.OperandTarget as string)
)
{
labels = GetLabelList(code),
};
}
if (code.OpCode == OpCodes.Call || code.OpCode == OpCodes.Callvirt)
{
return new CodeInstruction(code.OpCode, AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)) { labels = GetLabelList(code) };
return new CodeInstruction(
code.OpCode,
AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)
)
{
labels = GetLabelList(code),
};
}
if (code.OpCode == OpCodes.Box)
{
return new CodeInstruction(code.OpCode, code.CallerType) { labels = GetLabelList(code) };
return new CodeInstruction(code.OpCode, code.CallerType)
{
labels = GetLabelList(code),
};
}
if (code.OpCode == OpCodes.Br || code.OpCode == OpCodes.Brfalse || code.OpCode == OpCodes.Brtrue || code.OpCode == OpCodes.Brtrue_S
|| code.OpCode == OpCodes.Brfalse_S || code.OpCode == OpCodes.Br_S)
if (
code.OpCode == OpCodes.Br
|| code.OpCode == OpCodes.Brfalse
|| code.OpCode == OpCodes.Brtrue
|| code.OpCode == OpCodes.Brtrue_S
|| code.OpCode == OpCodes.Brfalse_S
|| code.OpCode == OpCodes.Br_S
)
{
return new CodeInstruction(code.OpCode, code.OperandTarget) { labels = GetLabelList(code) };
return new CodeInstruction(code.OpCode, code.OperandTarget)
{
labels = GetLabelList(code),
};
}
if (code.OpCode == OpCodes.Ldftn)
{
return new CodeInstruction(code.OpCode, AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)) { labels = GetLabelList(code) };
return new CodeInstruction(
code.OpCode,
AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)
)
{
labels = GetLabelList(code),
};
}
if (code.OpCode == OpCodes.Newobj)
{
return new CodeInstruction(code.OpCode, code.CallerType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == code.Parameters.Length)) { labels = GetLabelList(code) };
return new CodeInstruction(
code.OpCode,
code.CallerType.GetConstructors()
.FirstOrDefault(x => x.GetParameters().Length == code.Parameters.Length)
)
{
labels = GetLabelList(code),
};
}
throw new ArgumentException($"Code with OpCode {code.OpCode.ToString()} is not supported.");
@@ -68,6 +109,6 @@ public class CodeGenerator
return [];
}
return [ (Label)code.GetLabel() ];
return [(Label)code.GetLabel()];
}
}
@@ -6,22 +6,32 @@ public class CodeWithLabel : Code
{
public Label Label { get; }
public CodeWithLabel(OpCode opCode, Label label) : base(opCode)
public CodeWithLabel(OpCode opCode, Label label)
: base(opCode)
{
Label = label;
}
public CodeWithLabel(OpCode opCode, Label label, object operandTarget) : base(opCode, operandTarget)
public CodeWithLabel(OpCode opCode, Label label, object operandTarget)
: base(opCode, operandTarget)
{
Label = label;
}
public CodeWithLabel(OpCode opCode, Label label, Type callerType) : base(opCode, callerType)
public CodeWithLabel(OpCode opCode, Label label, Type callerType)
: base(opCode, callerType)
{
Label = label;
}
public CodeWithLabel(OpCode opCode, Label label, Type callerType, object operandTarget, Type[] parameters = null) : base(opCode, callerType, operandTarget, parameters)
public CodeWithLabel(
OpCode opCode,
Label label,
Type callerType,
object operandTarget,
Type[] parameters = null
)
: base(opCode, callerType, operandTarget, parameters)
{
Label = label;
}
@@ -25,13 +25,17 @@ public abstract class AbstractPatch
_finalizerList = GetPatchMethods(typeof(PatchFinalizerAttribute));
_ilManipulatorList = GetPatchMethods(typeof(PatchIlManipulatorAttribute));
if (_prefixList.Count == 0
if (
_prefixList.Count == 0
&& _postfixList.Count == 0
&& _transpilerList.Count == 0
&& _finalizerList.Count == 0
&& _ilManipulatorList.Count == 0)
&& _ilManipulatorList.Count == 0
)
{
throw new Exception($"{_harmony.Id}: At least one of the patch methods must be specified");
throw new Exception(
$"{_harmony.Id}: At least one of the patch methods must be specified"
);
}
}
@@ -51,8 +55,14 @@ public abstract class AbstractPatch
var T = GetType();
var methods = new List<HarmonyMethod>();
foreach (var method in T.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.DeclaredOnly))
foreach (
var method in T.GetMethods(
BindingFlags.Static
| BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.DeclaredOnly
)
)
{
if (method.GetCustomAttribute(attributeType) != null)
{
@@ -1,27 +1,17 @@
namespace SPTarkov.Reflection.Patching
{
[AttributeUsage(AttributeTargets.Method)]
public class PatchPrefixAttribute : Attribute
{
}
public class PatchPrefixAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public class PatchPostfixAttribute : Attribute
{
}
public class PatchPostfixAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public class PatchTranspilerAttribute : Attribute
{
}
public class PatchTranspilerAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public class PatchFinalizerAttribute : Attribute
{
}
public class PatchFinalizerAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public class PatchIlManipulatorAttribute : Attribute
{
}
public class PatchIlManipulatorAttribute : Attribute { }
}
@@ -1,26 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Build.props"/>
<PropertyGroup>
<PackageId>SPTarkov.Reflection</PackageId>
<Authors>Single Player Tarkov</Authors>
<Description>Reflection library for the Single Player Tarkov server.</Description>
<Copyright>Copyright (c) Single Player Tarkov 2025</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://sp-tarkov.com</PackageProjectUrl>
<RepositoryUrl>https://github.com/sp-tarkov/server-csharp</RepositoryUrl>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath=""/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="HarmonyX" Version="2.14.0"/>
</ItemGroup>
<Import Project="..\..\Build.props" />
<PropertyGroup>
<PackageId>SPTarkov.Reflection</PackageId>
<Authors>Single Player Tarkov</Authors>
<Description>Reflection library for the Single Player Tarkov server.</Description>
<Copyright>Copyright (c) Single Player Tarkov 2025</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://sp-tarkov.com</PackageProjectUrl>
<RepositoryUrl>https://github.com/sp-tarkov/server-csharp</RepositoryUrl>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HarmonyX" Version="2.14.0" />
</ItemGroup>
</Project>
@@ -1,7 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Build.props"/>
<Import Project="..\..\Build.props" />
<PropertyGroup>
<PackageId>SPTarkov.Server.Assets</PackageId>
<Authors>Single Player Tarkov</Authors>
@@ -16,21 +14,16 @@
<ContentTargetFolders>content</ContentTargetFolders>
<RootNamespace>SptAssets</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Content Include="SPT_Data\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<ExcludeFromSingleFile>True</ExcludeFromSingleFile>
</Content>
</ItemGroup>
<Target Name="PostBuildHashFile" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
<Exec Command="pwsh -NoProfile -ExecutionPolicy Bypass -File &quot;$(ProjectDir)PostBuild.ps1&quot;" />
</Target>
<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath=""/>
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
</Project>
@@ -17,7 +17,9 @@ public class AchievementCallbacks(
/// <returns></returns>
public ValueTask<string> GetAchievements(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_achievementController.GetAchievements(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_achievementController.GetAchievements(sessionID))
);
}
/// <summary>
@@ -26,6 +28,8 @@ public class AchievementCallbacks(
/// <returns></returns>
public ValueTask<string> Statistic(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_achievementController.GetAchievementStatics(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_achievementController.GetAchievementStatics(sessionID))
);
}
}
@@ -7,10 +7,7 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class BotCallbacks(
BotController _botController,
HttpResponseUtil _httpResponseUtil
)
public class BotCallbacks(BotController _botController, HttpResponseUtil _httpResponseUtil)
{
/// <summary>
/// Handle singleplayer/settings/bot/limit
@@ -21,7 +18,9 @@ public class BotCallbacks(
{
var splitUrl = url.Split('/');
var type = splitUrl[^1];
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotPresetGenerationLimit(type)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_botController.GetBotPresetGenerationLimit(type))
);
}
/// <summary>
@@ -35,10 +34,14 @@ public class BotCallbacks(
var difficulty = splitUrl[^1];
if (difficulty == "core")
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotCoreDifficulty()));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_botController.GetBotCoreDifficulty())
);
}
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetBotDifficulty(sessionID, type, difficulty)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_botController.GetBotDifficulty(sessionID, type, difficulty))
);
}
/// <summary>
@@ -47,16 +50,24 @@ public class BotCallbacks(
/// <returns></returns>
public ValueTask<string> GetAllBotDifficulties(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_botController.GetAllBotDifficulties()));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_botController.GetAllBotDifficulties())
);
}
/// <summary>
/// Handle client/game/bot/generate
/// </summary>
/// <returns></returns>
public ValueTask<string> GenerateBots(string url, GenerateBotsRequestData info, string sessionID)
public ValueTask<string> GenerateBots(
string url,
GenerateBotsRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_botController.Generate(sessionID, info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_botController.Generate(sessionID, info))
);
}
/// <summary>
@@ -20,10 +20,10 @@ public class BtrDeliveryCallbacks(
SaveServer _saveServer,
HashUtil _hashUtil,
ItemHelper _itemHelper
)
: IOnUpdate
) : IOnUpdate
{
private readonly BtrDeliveryConfig _btrDeliveryConfig = _configServer.GetConfig<BtrDeliveryConfig>();
private readonly BtrDeliveryConfig _btrDeliveryConfig =
_configServer.GetConfig<BtrDeliveryConfig>();
public Task<bool> OnUpdate(long secondsSinceLastRun)
{
@@ -81,9 +81,13 @@ public class BtrDeliveryCallbacks(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Found {deliveryList.Count} BTR delivery package(s) in profile {sessionId}");
_logger.Debug(
$"Found {deliveryList.Count} BTR delivery package(s) in profile {sessionId}"
);
}
return deliveryList.Where(toBeDelivered => currentTime >= toBeDelivered.ScheduledTime).ToList();
return deliveryList
.Where(toBeDelivered => currentTime >= toBeDelivered.ScheduledTime)
.ToList();
}
return [];
@@ -8,10 +8,7 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class BuildsCallbacks(
HttpResponseUtil _httpResponseUtil,
BuildController _buildController
)
public class BuildsCallbacks(HttpResponseUtil _httpResponseUtil, BuildController _buildController)
{
/// <summary>
/// Handle client/builds/list
@@ -19,7 +16,9 @@ public class BuildsCallbacks(
/// <returns></returns>
public ValueTask<string> GetBuilds(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_buildController.GetUserBuilds(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_buildController.GetUserBuilds(sessionID))
);
}
/// <summary>
@@ -29,7 +28,11 @@ public class BuildsCallbacks(
/// <param name="request"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> CreateMagazineTemplate(string url, SetMagazineRequest request, string sessionID)
public ValueTask<string> CreateMagazineTemplate(
string url,
SetMagazineRequest request,
string sessionID
)
{
_buildController.CreateMagazineTemplate(sessionID, request);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -39,7 +42,11 @@ public class BuildsCallbacks(
/// Handle client/builds/weapon/save
/// </summary>
/// <returns></returns>
public ValueTask<string> SetWeapon(string url, PresetBuildActionRequestData request, string sessionID)
public ValueTask<string> SetWeapon(
string url,
PresetBuildActionRequestData request,
string sessionID
)
{
_buildController.SaveWeaponBuild(sessionID, request);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -49,7 +56,11 @@ public class BuildsCallbacks(
/// Handle client/builds/equipment/save
/// </summary>
/// <returns></returns>
public ValueTask<string> SetEquipment(string url, PresetBuildActionRequestData request, string sessionID)
public ValueTask<string> SetEquipment(
string url,
PresetBuildActionRequestData request,
string sessionID
)
{
_buildController.SaveEquipmentBuild(sessionID, request);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -59,7 +70,11 @@ public class BuildsCallbacks(
/// Handle client/builds/delete
/// </summary>
/// <returns></returns>
public ValueTask<string> DeleteBuild(string url, RemoveBuildRequestData request, string sessionID)
public ValueTask<string> DeleteBuild(
string url,
RemoveBuildRequestData request,
string sessionID
)
{
_buildController.RemoveBuild(sessionID, request);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -6,9 +6,7 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class BundleCallbacks(
HttpResponseUtil _httpResponseUtil,
BundleLoader _bundleLoader)
public class BundleCallbacks(HttpResponseUtil _httpResponseUtil, BundleLoader _bundleLoader)
{
/// <summary>
/// Handle singleplayer/bundles
@@ -41,15 +41,26 @@ public class ClientLogCallbacks(
? _localisationService.GetText("release-beta-disclaimer-mods-enabled")
: _localisationService.GetText("release-beta-disclaimer");
data.BetaDisclaimerAcceptText = _localisationService.GetText("release-beta-disclaimer-accept");
data.BetaDisclaimerAcceptText = _localisationService.GetText(
"release-beta-disclaimer-accept"
);
data.ServerModsLoadedText = _localisationService.GetText("release-server-mods-loaded");
data.ServerModsLoadedDebugText = _localisationService.GetText("release-server-mods-debug-message");
data.ServerModsLoadedDebugText = _localisationService.GetText(
"release-server-mods-debug-message"
);
data.ClientModsLoadedText = _localisationService.GetText("release-plugins-loaded");
data.ClientModsLoadedDebugText = _localisationService.GetText("release-plugins-loaded-debug-message");
data.IllegalPluginsLoadedText = _localisationService.GetText("release-illegal-plugins-loaded");
data.IllegalPluginsExceptionText = _localisationService.GetText("release-illegal-plugins-exception");
data.ClientModsLoadedDebugText = _localisationService.GetText(
"release-plugins-loaded-debug-message"
);
data.IllegalPluginsLoadedText = _localisationService.GetText(
"release-illegal-plugins-loaded"
);
data.IllegalPluginsExceptionText = _localisationService.GetText(
"release-illegal-plugins-exception"
);
data.ReleaseSummaryText = _localisationService.GetText("release-summary");
data.IsBeta = ProgramStatics.ENTRY_TYPE() is EntryType.BLEEDING_EDGE or EntryType.BLEEDING_EDGE_MODS;
data.IsBeta =
ProgramStatics.ENTRY_TYPE() is EntryType.BLEEDING_EDGE or EntryType.BLEEDING_EDGE_MODS;
data.IsModdable = ProgramStatics.MODS();
data.IsModded = _loadedMods.Count > 0;
@@ -19,9 +19,15 @@ public class CustomizationCallbacks(
/// Handle client/trading/customization/storage
/// </summary>
/// <returns></returns>
public ValueTask<string> GetCustomisationUnlocks(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetCustomisationUnlocks(
string url,
EmptyRequestData _,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_saveServer.GetProfile(sessionID).CustomisationUnlocks));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_saveServer.GetProfile(sessionID).CustomisationUnlocks)
);
}
/// <summary>
@@ -33,14 +39,20 @@ public class CustomizationCallbacks(
var splitUrl = url.Split('/');
var traderId = splitUrl[^3];
return new ValueTask<string>(_httpResponseUtil.GetBody(_customizationController.GetTraderSuits(traderId, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_customizationController.GetTraderSuits(traderId, sessionID))
);
}
/// <summary>
/// Handle CustomizationBuy event
/// </summary>
/// <returns></returns>
public ItemEventRouterResponse BuyCustomisation(PmcData pmcData, BuyClothingRequestData request, string sessionID)
public ItemEventRouterResponse BuyCustomisation(
PmcData pmcData,
BuyClothingRequestData request,
string sessionID
)
{
return _customizationController.BuyCustomisation(pmcData, request, sessionID);
}
@@ -49,9 +61,15 @@ public class CustomizationCallbacks(
/// Handle client/hideout/customization/offer/list
/// </summary>
/// <returns></returns>
public ValueTask<string> GetHideoutCustomisation(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetHideoutCustomisation(
string url,
EmptyRequestData _,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_customizationController.GetHideoutCustomisation(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_customizationController.GetHideoutCustomisation(sessionID))
);
}
/// <summary>
@@ -60,14 +78,20 @@ public class CustomizationCallbacks(
/// <returns></returns>
public ValueTask<string> GetStorage(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_customizationController.GetCustomisationStorage(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_customizationController.GetCustomisationStorage(sessionID))
);
}
/// <summary>
/// Handle CustomizationSet
/// </summary>
/// <returns></returns>
public ItemEventRouterResponse SetCustomisation(PmcData pmcData, CustomizationSetRequest request, string sessionID)
public ItemEventRouterResponse SetCustomisation(
PmcData pmcData,
CustomizationSetRequest request,
string sessionID
)
{
return _customizationController.SetCustomisation(sessionID, request, pmcData);
}
@@ -43,7 +43,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetTemplateItems(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetUnclearedBody(_databaseService.GetItems()));
return new ValueTask<string>(
_httpResponseUtil.GetUnclearedBody(_databaseService.GetItems())
);
}
/// <summary>
@@ -61,7 +63,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetTemplateSuits(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetTemplates().Customization));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetTemplates().Customization)
);
}
/// <summary>
@@ -70,7 +74,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetTemplateCharacter(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetTemplates().Character));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetTemplates().Character)
);
}
/// <summary>
@@ -79,7 +85,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetHideoutSettings(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHideout().Settings));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetHideout().Settings)
);
}
/// <summary>
@@ -88,7 +96,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetHideoutAreas(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHideout().Areas));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetHideout().Areas)
);
}
/// <summary>
@@ -97,7 +107,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetHideoutProduction(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetHideout().Production));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetHideout().Production)
);
}
/// <summary>
@@ -106,7 +118,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetLocalesLanguages(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetLocales().Languages));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetLocales().Languages)
);
}
/// <summary>
@@ -117,7 +131,8 @@ public class DataCallbacks(
{
var localeId = url.Replace("/client/menu/locale/", "");
var locales = _databaseService.GetLocales();
var result = locales.Menu?[localeId] ?? locales.Menu?.FirstOrDefault(m => m.Key == "en").Value;
var result =
locales.Menu?[localeId] ?? locales.Menu?.FirstOrDefault(m => m.Key == "en").Value;
if (result == null)
{
@@ -145,7 +160,9 @@ public class DataCallbacks(
/// <returns></returns>
public ValueTask<string> GetQteList(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetUnclearedBody(_hideoutController.GetQteList(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetUnclearedBody(_hideoutController.GetQteList(sessionID))
);
}
/// <summary>
@@ -156,6 +173,8 @@ public class DataCallbacks(
{
var traderId = url.Replace("/client/items/prices/", "");
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetItemPrices(sessionID, traderId)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_traderController.GetItemPrices(sessionID, traderId))
);
}
}
@@ -14,8 +14,7 @@ public class DialogueCallbacks(
TimeUtil _timeUtil,
HttpResponseUtil _httpResponseUtil,
DialogueController _dialogueController
)
: IOnUpdate
) : IOnUpdate
{
public Task<bool> OnUpdate(long timeSinceLastRun)
{
@@ -29,14 +28,20 @@ public class DialogueCallbacks(
/// <returns></returns>
public virtual ValueTask<string> GetFriendList(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GetFriendList(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_dialogueController.GetFriendList(sessionID))
);
}
/// <summary>
/// Handle client/chatServer/list
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> GetChatServerList(string url, GetChatServerListRequestData request, string sessionID)
public virtual ValueTask<string> GetChatServerList(
string url,
GetChatServerListRequestData request,
string sessionID
)
{
var chatServer = new List<ChatServer>
{
@@ -50,15 +55,8 @@ public class DialogueCallbacks(
VersionId = "bgkidft87ddd",
Ip = "",
Port = 0,
Chats =
[
new Chat
{
Id = "0",
Members = 0
}
]
}
Chats = [new Chat { Id = "0", Members = 0 }],
},
};
return new ValueTask<string>(_httpResponseUtil.GetBody(chatServer));
@@ -69,9 +67,20 @@ public class DialogueCallbacks(
/// TODO: request properties are not handled
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> GetMailDialogList(string url, GetMailDialogListRequestData request, string sessionID)
public virtual ValueTask<string> GetMailDialogList(
string url,
GetMailDialogListRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GenerateDialogueList(sessionID), 0, null, false));
return new ValueTask<string>(
_httpResponseUtil.GetBody(
_dialogueController.GenerateDialogueList(sessionID),
0,
null,
false
)
);
}
/// <summary>
@@ -81,25 +90,48 @@ public class DialogueCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public virtual ValueTask<string> GetMailDialogView(string url, GetMailDialogViewRequestData request, string sessionID)
public virtual ValueTask<string> GetMailDialogView(
string url,
GetMailDialogViewRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GenerateDialogueView(request, sessionID), 0, null, false));
return new ValueTask<string>(
_httpResponseUtil.GetBody(
_dialogueController.GenerateDialogueView(request, sessionID),
0,
null,
false
)
);
}
/// <summary>
/// Handle client/mail/dialog/info
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> GetMailDialogInfo(string url, GetMailDialogInfoRequestData request, string sessionID)
public virtual ValueTask<string> GetMailDialogInfo(
string url,
GetMailDialogInfoRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GetDialogueInfo(request.DialogId, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(
_dialogueController.GetDialogueInfo(request.DialogId, sessionID)
)
);
}
/// <summary>
/// Handle client/mail/dialog/remove
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> RemoveDialog(string url, RemoveDialogRequestData request, string sessionID)
public virtual ValueTask<string> RemoveDialog(
string url,
RemoveDialogRequestData request,
string sessionID
)
{
_dialogueController.RemoveDialogue(request.DialogId, sessionID);
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
@@ -109,7 +141,11 @@ public class DialogueCallbacks(
/// Handle client/mail/dialog/pin
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> PinDialog(string url, PinDialogRequestData request, string sessionID)
public virtual ValueTask<string> PinDialog(
string url,
PinDialogRequestData request,
string sessionID
)
{
_dialogueController.SetDialoguePin(request.DialogId, true, sessionID);
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
@@ -119,7 +155,11 @@ public class DialogueCallbacks(
/// Handle client/mail/dialog/unpin
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> UnpinDialog(string url, PinDialogRequestData request, string sessionID)
public virtual ValueTask<string> UnpinDialog(
string url,
PinDialogRequestData request,
string sessionID
)
{
_dialogueController.SetDialoguePin(request.DialogId, false, sessionID);
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
@@ -129,7 +169,11 @@ public class DialogueCallbacks(
/// Handle client/mail/dialog/read
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> SetRead(string url, SetDialogReadRequestData request, string sessionID)
public virtual ValueTask<string> SetRead(
string url,
SetDialogReadRequestData request,
string sessionID
)
{
_dialogueController.SetRead(request.Dialogs, sessionID);
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
@@ -139,18 +183,32 @@ public class DialogueCallbacks(
/// Handle client/mail/dialog/getAllAttachments
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> GetAllAttachments(string url, GetAllAttachmentsRequestData request, string sessionID)
public virtual ValueTask<string> GetAllAttachments(
string url,
GetAllAttachmentsRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.GetAllAttachments(request.DialogId, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(
_dialogueController.GetAllAttachments(request.DialogId, sessionID)
)
);
}
/// <summary>
/// Handle client/mail/msg/send
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> SendMessage(string url, SendMessageRequest request, string sessionID)
public virtual ValueTask<string> SendMessage(
string url,
SendMessageRequest request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.SendMessage(sessionID, request)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_dialogueController.SendMessage(sessionID, request))
);
}
/// <summary>
@@ -175,16 +233,26 @@ public class DialogueCallbacks(
/// Handle client/friend/request/send
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> SendFriendRequest(string url, FriendRequestData request, string sessionID)
public virtual ValueTask<string> SendFriendRequest(
string url,
FriendRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_dialogueController.SendFriendRequest(sessionID, request)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_dialogueController.SendFriendRequest(sessionID, request))
);
}
/// <summary>
/// Handle client/friend/request/accept-all
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> AcceptAllFriendRequests(string url, EmptyRequestData _, string sessionID)
public virtual ValueTask<string> AcceptAllFriendRequests(
string url,
EmptyRequestData _,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -193,7 +261,11 @@ public class DialogueCallbacks(
/// Handle client/friend/request/accept
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> AcceptFriendRequest(string url, AcceptFriendRequestData request, string sessionID)
public virtual ValueTask<string> AcceptFriendRequest(
string url,
AcceptFriendRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
@@ -202,7 +274,11 @@ public class DialogueCallbacks(
/// Handle client/friend/request/decline
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> DeclineFriendRequest(string url, DeclineFriendRequestData request, string sessionID)
public virtual ValueTask<string> DeclineFriendRequest(
string url,
DeclineFriendRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
@@ -211,7 +287,11 @@ public class DialogueCallbacks(
/// Handle client/friend/request/cancel
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> CancelFriendRequest(string url, CancelFriendRequestData request, string sessionID)
public virtual ValueTask<string> CancelFriendRequest(
string url,
CancelFriendRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
@@ -220,7 +300,11 @@ public class DialogueCallbacks(
/// Handle client/friend/delete
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> DeleteFriend(string url, DeleteFriendRequest request, string sessionID)
public virtual ValueTask<string> DeleteFriend(
string url,
DeleteFriendRequest request,
string sessionID
)
{
_dialogueController.DeleteFriend(sessionID, request);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -230,7 +314,11 @@ public class DialogueCallbacks(
/// Handle client/friend/ignore/set
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> IgnoreFriend(string url, UIDRequestData request, string sessionID)
public virtual ValueTask<string> IgnoreFriend(
string url,
UIDRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -239,34 +327,58 @@ public class DialogueCallbacks(
/// Handle client/friend/ignore/remove
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> UnIgnoreFriend(string url, UIDRequestData request, string sessionID)
public virtual ValueTask<string> UnIgnoreFriend(
string url,
UIDRequestData request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
public virtual ValueTask<string> ClearMail(string url, ClearMailMessageRequest request, string sessionID)
public virtual ValueTask<string> ClearMail(
string url,
ClearMailMessageRequest request,
string sessionID
)
{
_dialogueController.ClearMessages(sessionID, request);
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
public virtual ValueTask<string> CreateGroupMail(string url, CreateGroupMailRequest request, string sessionID)
public virtual ValueTask<string> CreateGroupMail(
string url,
CreateGroupMailRequest request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.EmptyArrayResponse());
}
public virtual ValueTask<string> ChangeMailGroupOwner(string url, ChangeGroupMailOwnerRequest request, string sessionID)
public virtual ValueTask<string> ChangeMailGroupOwner(
string url,
ChangeGroupMailOwnerRequest request,
string sessionID
)
{
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
public virtual ValueTask<string> AddUserToMail(string url, AddUserGroupMailRequest request, string sessionID)
public virtual ValueTask<string> AddUserToMail(
string url,
AddUserGroupMailRequest request,
string sessionID
)
{
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
public virtual ValueTask<string> RemoveUserFromMail(string url, RemoveUserGroupMailRequest request, string sessionID)
public virtual ValueTask<string> RemoveUserFromMail(
string url,
RemoveUserGroupMailRequest request,
string sessionID
)
{
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
@@ -28,7 +28,11 @@ public class GameCallbacks(
/// Handle client/game/version/validate
/// </summary>
/// <returns></returns>
public ValueTask<string> VersionValidate(string url, VersionValidateRequestData info, string sessionID)
public ValueTask<string> VersionValidate(
string url,
VersionValidateRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -41,12 +45,9 @@ public class GameCallbacks(
{
var startTimestampSec = _timeUtil.GetTimeStamp();
_gameController.GameStart(url, sessionID, startTimestampSec);
return new ValueTask<string>(_httpResponseUtil.GetBody(
new GameStartResponse
{
UtcTime = startTimestampSec
}
));
return new ValueTask<string>(
_httpResponseUtil.GetBody(new GameStartResponse { UtcTime = startTimestampSec })
);
}
/// <summary>
@@ -57,21 +58,22 @@ public class GameCallbacks(
public async ValueTask<string> GameLogout(string url, EmptyRequestData _, string sessionID)
{
await _saveServer.SaveProfileAsync(sessionID);
return _httpResponseUtil.GetBody(
new GameLogoutResponseData
{
Status = "ok"
}
);
return _httpResponseUtil.GetBody(new GameLogoutResponseData { Status = "ok" });
}
/// <summary>
/// Handle client/game/config
/// </summary>
/// <returns></returns>
public ValueTask<string> GetGameConfig(string url, GameEmptyCrcRequestData info, string sessionID)
public ValueTask<string> GetGameConfig(
string url,
GameEmptyCrcRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetGameConfig(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetGameConfig(sessionID))
);
}
/// <summary>
@@ -80,7 +82,9 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> GetGameMode(string url, GameModeRequestData info, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetGameMode(sessionID, info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetGameMode(sessionID, info))
);
}
/// <summary>
@@ -89,7 +93,9 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> GetServer(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetServer(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetServer(sessionID))
);
}
/// <summary>
@@ -98,7 +104,9 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> GetCurrentGroup(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetCurrentGroup(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetCurrentGroup(sessionID))
);
}
/// <summary>
@@ -107,7 +115,9 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> ValidateGameVersion(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetValidGameVersion(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetValidGameVersion(sessionID))
);
}
/// <summary>
@@ -116,7 +126,9 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> GameKeepalive(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetKeepAlive(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetKeepAlive(sessionID))
);
}
/// <summary>
@@ -126,12 +138,9 @@ public class GameCallbacks(
public ValueTask<string> GetVersion(string url, EmptyRequestData _, string sessionID)
{
// change to be a proper type
return new ValueTask<string>(_httpResponseUtil.NoBody(
new
{
Version = _watermark.GetInGameVersionLabel()
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(new { Version = _watermark.GetInGameVersionLabel() })
);
}
/// <summary>
@@ -149,7 +158,9 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> GetRaidTime(string url, GetRaidTimeRequest request, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_gameController.GetRaidTime(sessionID, request)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_gameController.GetRaidTime(sessionID, request))
);
}
/// <summary>
@@ -158,14 +169,20 @@ public class GameCallbacks(
/// <returns></returns>
public ValueTask<string> GetSurvey(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_gameController.GetSurvey(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_gameController.GetSurvey(sessionID))
);
}
/// <summary>
/// Handle client/survey/view
/// </summary>
/// <returns></returns>
public ValueTask<string> GetSurveyView(string url, SendSurveyOpinionRequest request, string sessionID)
public ValueTask<string> GetSurveyView(
string url,
SendSurveyOpinionRequest request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -174,7 +191,11 @@ public class GameCallbacks(
/// Handle client/survey/opinion
/// </summary>
/// <returns></returns>
public ValueTask<string> SendSurveyOpinion(string url, SendSurveyOpinionRequest request, string sessionID)
public ValueTask<string> SendSurveyOpinion(
string url,
SendSurveyOpinionRequest request,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -24,7 +24,11 @@ public class HealthCallbacks(
/// <returns>empty response, no data sent back to client</returns>
public ValueTask<string> HandleWorkoutEffects(string url, WorkoutData info, string sessionID)
{
_healthController.ApplyWorkoutChanges(_profileHelper.GetPmcProfile(sessionID), info, sessionID);
_healthController.ApplyWorkoutChanges(
_profileHelper.GetPmcProfile(sessionID),
info,
sessionID
);
return new ValueTask<string>(_httpResponseUtil.EmptyResponse());
}
@@ -35,7 +39,11 @@ public class HealthCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse OffraidEat(PmcData pmcData, OffraidEatRequestData info, string sessionID)
public ItemEventRouterResponse OffraidEat(
PmcData pmcData,
OffraidEatRequestData info,
string sessionID
)
{
return _healthController.OffRaidEat(pmcData, info, sessionID);
}
@@ -47,7 +55,11 @@ public class HealthCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse OffraidHeal(PmcData pmcData, OffraidHealRequestData info, string sessionID)
public ItemEventRouterResponse OffraidHeal(
PmcData pmcData,
OffraidHealRequestData info,
string sessionID
)
{
return _healthController.OffRaidHeal(pmcData, info, sessionID);
}
@@ -59,7 +71,11 @@ public class HealthCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse HealthTreatment(PmcData pmcData, HealthTreatmentRequestData info, string sessionID)
public ItemEventRouterResponse HealthTreatment(
PmcData pmcData,
HealthTreatmentRequestData info,
string sessionID
)
{
return _healthController.HealthTreatment(pmcData, info, sessionID);
}
@@ -10,10 +10,8 @@ using SPTarkov.Server.Core.Servers;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable(TypePriority = OnUpdateOrder.HideoutCallbacks)]
public class HideoutCallbacks(
HideoutController _hideoutController,
ConfigServer _configServer
) : IOnUpdate
public class HideoutCallbacks(HideoutController _hideoutController, ConfigServer _configServer)
: IOnUpdate
{
private readonly HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
@@ -33,7 +31,12 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutUpgrade event
/// </summary>
public ItemEventRouterResponse Upgrade(PmcData pmcData, HideoutUpgradeRequestData request, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse Upgrade(
PmcData pmcData,
HideoutUpgradeRequestData request,
string sessionID,
ItemEventRouterResponse output
)
{
_hideoutController.StartUpgrade(pmcData, request, sessionID, output);
@@ -43,7 +46,12 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutUpgradeComplete event
/// </summary>
public ItemEventRouterResponse UpgradeComplete(PmcData pmcData, HideoutUpgradeCompleteRequestData request, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse UpgradeComplete(
PmcData pmcData,
HideoutUpgradeCompleteRequestData request,
string sessionID,
ItemEventRouterResponse output
)
{
_hideoutController.UpgradeComplete(pmcData, request, sessionID, output);
@@ -53,7 +61,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutPutItemsInAreaSlots
/// </summary>
public ItemEventRouterResponse PutItemsInAreaSlots(PmcData pmcData, HideoutPutItemInRequestData request, string sessionID)
public ItemEventRouterResponse PutItemsInAreaSlots(
PmcData pmcData,
HideoutPutItemInRequestData request,
string sessionID
)
{
return _hideoutController.PutItemsInAreaSlots(pmcData, request, sessionID);
}
@@ -61,7 +73,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutTakeItemsFromAreaSlots event
/// </summary>
public ItemEventRouterResponse TakeItemsFromAreaSlots(PmcData pmcData, HideoutTakeItemOutRequestData request, string sessionID)
public ItemEventRouterResponse TakeItemsFromAreaSlots(
PmcData pmcData,
HideoutTakeItemOutRequestData request,
string sessionID
)
{
return _hideoutController.TakeItemsFromAreaSlots(pmcData, request, sessionID);
}
@@ -69,7 +85,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutToggleArea event
/// </summary>
public ItemEventRouterResponse ToggleArea(PmcData pmcData, HideoutToggleAreaRequestData request, string sessionID)
public ItemEventRouterResponse ToggleArea(
PmcData pmcData,
HideoutToggleAreaRequestData request,
string sessionID
)
{
return _hideoutController.ToggleArea(pmcData, request, sessionID);
}
@@ -77,7 +97,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutSingleProductionStart event
/// </summary>
public ItemEventRouterResponse SingleProductionStart(PmcData pmcData, HideoutSingleProductionStartRequestData request, string sessionID)
public ItemEventRouterResponse SingleProductionStart(
PmcData pmcData,
HideoutSingleProductionStartRequestData request,
string sessionID
)
{
return _hideoutController.SingleProductionStart(pmcData, request, sessionID);
}
@@ -85,7 +109,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutScavCaseProductionStart event
/// </summary>
public ItemEventRouterResponse ScavCaseProductionStart(PmcData pmcData, HideoutScavCaseStartRequestData request, string sessionID)
public ItemEventRouterResponse ScavCaseProductionStart(
PmcData pmcData,
HideoutScavCaseStartRequestData request,
string sessionID
)
{
return _hideoutController.ScavCaseProductionStart(pmcData, request, sessionID);
}
@@ -93,7 +121,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutContinuousProductionStart
/// </summary>
public ItemEventRouterResponse ContinuousProductionStart(PmcData pmcData, HideoutContinuousProductionStartRequestData request, string sessionID)
public ItemEventRouterResponse ContinuousProductionStart(
PmcData pmcData,
HideoutContinuousProductionStartRequestData request,
string sessionID
)
{
return _hideoutController.ContinuousProductionStart(pmcData, request, sessionID);
}
@@ -101,7 +133,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutTakeProduction event
/// </summary>
public ItemEventRouterResponse TakeProduction(PmcData pmcData, HideoutTakeProductionRequestData request, string sessionID)
public ItemEventRouterResponse TakeProduction(
PmcData pmcData,
HideoutTakeProductionRequestData request,
string sessionID
)
{
return _hideoutController.TakeProduction(pmcData, request, sessionID);
}
@@ -109,7 +145,12 @@ public class HideoutCallbacks(
/// <summary>
/// Handle HideoutQuickTimeEvent
/// </summary>
public ItemEventRouterResponse HandleQTEEvent(PmcData pmcData, HandleQTEEventRequestData request, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse HandleQTEEvent(
PmcData pmcData,
HandleQTEEventRequestData request,
string sessionID,
ItemEventRouterResponse output
)
{
_hideoutController.HandleQTEEventOutcome(sessionID, pmcData, request, output);
@@ -119,8 +160,12 @@ public class HideoutCallbacks(
/// <summary>
/// Handle client/game/profile/items/moving - RecordShootingRangePoints
/// </summary>
public ItemEventRouterResponse RecordShootingRangePoints(PmcData pmcData, RecordShootingRangePoints request, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse RecordShootingRangePoints(
PmcData pmcData,
RecordShootingRangePoints request,
string sessionID,
ItemEventRouterResponse output
)
{
_hideoutController.RecordShootingRangePoints(sessionID, pmcData, request);
@@ -130,7 +175,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle client/game/profile/items/moving - RecordShootingRangePoints
/// </summary>
public ItemEventRouterResponse ImproveArea(PmcData pmcData, HideoutImproveAreaRequestData request, string sessionID)
public ItemEventRouterResponse ImproveArea(
PmcData pmcData,
HideoutImproveAreaRequestData request,
string sessionID
)
{
return _hideoutController.ImproveArea(sessionID, pmcData, request);
}
@@ -138,7 +187,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle client/game/profile/items/moving - HideoutCancelProductionCommand
/// </summary>
public ItemEventRouterResponse CancelProduction(PmcData pmcData, HideoutCancelProductionRequestData request, string sessionID)
public ItemEventRouterResponse CancelProduction(
PmcData pmcData,
HideoutCancelProductionRequestData request,
string sessionID
)
{
return _hideoutController.CancelProduction(sessionID, pmcData, request);
}
@@ -146,7 +199,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle client/game/profile/items/moving - HideoutCircleOfCultistProductionStart
/// </summary>
public ItemEventRouterResponse CicleOfCultistProductionStart(PmcData pmcData, HideoutCircleOfCultistProductionStartRequestData request, string sessionID)
public ItemEventRouterResponse CicleOfCultistProductionStart(
PmcData pmcData,
HideoutCircleOfCultistProductionStartRequestData request,
string sessionID
)
{
return _hideoutController.CicleOfCultistProductionStart(sessionID, pmcData, request);
}
@@ -154,7 +211,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle client/game/profile/items/moving - HideoutDeleteProductionCommand
/// </summary>
public ItemEventRouterResponse HideoutDeleteProductionCommand(PmcData pmcData, HideoutDeleteProductionRequestData request, string sessionID)
public ItemEventRouterResponse HideoutDeleteProductionCommand(
PmcData pmcData,
HideoutDeleteProductionRequestData request,
string sessionID
)
{
return _hideoutController.HideoutDeleteProductionCommand(sessionID, pmcData, request);
}
@@ -162,7 +223,11 @@ public class HideoutCallbacks(
/// <summary>
/// Handle client/game/profile/items/moving - HideoutCustomizationApply
/// </summary>
public ItemEventRouterResponse HideoutCustomizationApplyCommand(PmcData pmcData, HideoutCustomizationApplyRequestData request, string sessionID)
public ItemEventRouterResponse HideoutCustomizationApplyCommand(
PmcData pmcData,
HideoutCustomizationApplyRequestData request,
string sessionID
)
{
return _hideoutController.HideoutCustomizationApply(sessionID, pmcData, request);
}
@@ -171,7 +236,11 @@ public class HideoutCallbacks(
/// Handle client/game/profile/items/moving - hideoutCustomizationSetMannequinPose
/// </summary>
/// <returns></returns>
public ItemEventRouterResponse HideoutCustomizationSetMannequinPose(PmcData pmcData, HideoutCustomizationSetMannequinPoseRequest request, string sessionId)
public ItemEventRouterResponse HideoutCustomizationSetMannequinPose(
PmcData pmcData,
HideoutCustomizationSetMannequinPoseRequest request,
string sessionId
)
{
return _hideoutController.HideoutCustomizationSetMannequinPose(sessionId, pmcData, request);
}
@@ -3,6 +3,7 @@ using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Servers;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable(InjectionType.Singleton, TypePriority = OnLoadOrder.HttpCallbacks)]
public class HttpCallbacks(HttpServer _httpServer) : IOnLoad
{
@@ -7,10 +7,7 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class InraidCallbacks(
InRaidController _inRaidController,
HttpResponseUtil _httpResponseUtil
)
public class InraidCallbacks(InRaidController _inRaidController, HttpResponseUtil _httpResponseUtil)
{
/// <summary>
/// Handle client/location/getLocalloot
@@ -20,7 +17,11 @@ public class InraidCallbacks(
/// <param name="info">register player request</param>
/// <param name="sessionID">Session id</param>
/// <returns>Null http response</returns>
public ValueTask<string> RegisterPlayer(string url, RegisterPlayerRequestData info, string sessionID)
public ValueTask<string> RegisterPlayer(
string url,
RegisterPlayerRequestData info,
string sessionID
)
{
_inRaidController.AddPlayer(sessionID, info);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -45,16 +46,24 @@ public class InraidCallbacks(
/// <returns>JSON as string</returns>
public ValueTask<string> GetRaidMenuSettings()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_inRaidController.GetInRaidConfig().RaidMenuSettings));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_inRaidController.GetInRaidConfig().RaidMenuSettings)
);
}
/// <summary>
/// Handle singleplayer/scav/traitorscavhostile
/// </summary>
/// <returns></returns>
public ValueTask<string> GetTraitorScavHostileChance(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetTraitorScavHostileChance(
string url,
EmptyRequestData _,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_inRaidController.GetTraitorScavHostileChance(url, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_inRaidController.GetTraitorScavHostileChance(url, sessionID))
);
}
/// <summary>
@@ -63,6 +72,8 @@ public class InraidCallbacks(
/// <returns></returns>
public ValueTask<string> GetBossTypes(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_inRaidController.GetBossTypes(url, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_inRaidController.GetBossTypes(url, sessionID))
);
}
}
@@ -17,8 +17,7 @@ public class InsuranceCallbacks(
InsuranceService _insuranceService,
HttpResponseUtil _httpResponseUtil,
ConfigServer _configServer
)
: IOnUpdate
) : IOnUpdate
{
private readonly InsuranceConfig _insuranceConfig = _configServer.GetConfig<InsuranceConfig>();
@@ -41,9 +40,15 @@ public class InsuranceCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> GetInsuranceCost(string url, GetInsuranceCostRequestData info, string sessionID)
public ValueTask<string> GetInsuranceCost(
string url,
GetInsuranceCostRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_insuranceController.Cost(info, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_insuranceController.Cost(info, sessionID))
);
}
/// <summary>
@@ -21,8 +21,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse MoveItem(PmcData pmcData, InventoryMoveRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse MoveItem(
PmcData pmcData,
InventoryMoveRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.MoveItem(pmcData, info, sessionID, output);
return output;
@@ -36,8 +40,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse RemoveItem(PmcData pmcData, InventoryRemoveRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse RemoveItem(
PmcData pmcData,
InventoryRemoveRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.DiscardItem(pmcData, info, sessionID, output);
return output;
@@ -51,8 +59,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse SplitItem(PmcData pmcData, InventorySplitRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse SplitItem(
PmcData pmcData,
InventorySplitRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.SplitItem(pmcData, info, sessionID, output);
return output;
@@ -65,8 +77,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse MergeItem(PmcData pmcData, InventoryMergeRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse MergeItem(
PmcData pmcData,
InventoryMergeRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.MergeItem(pmcData, info, sessionID, output);
return output;
@@ -79,8 +95,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse TransferItem(PmcData pmcData, InventoryTransferRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse TransferItem(
PmcData pmcData,
InventoryTransferRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.TransferItem(pmcData, info, sessionID, output);
return output;
@@ -93,7 +113,11 @@ public class InventoryCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse SwapItem(PmcData pmcData, InventorySwapRequestData info, string sessionID)
public ItemEventRouterResponse SwapItem(
PmcData pmcData,
InventorySwapRequestData info,
string sessionID
)
{
return _inventoryController.SwapItem(pmcData, info, sessionID);
}
@@ -104,7 +128,11 @@ public class InventoryCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse FoldItem(PmcData pmcData, InventoryFoldRequestData info, string sessionID)
public ItemEventRouterResponse FoldItem(
PmcData pmcData,
InventoryFoldRequestData info,
string sessionID
)
{
return _inventoryController.FoldItem(pmcData, info, sessionID);
}
@@ -115,7 +143,11 @@ public class InventoryCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ToggleItem(PmcData pmcData, InventoryToggleRequestData info, string sessionID)
public ItemEventRouterResponse ToggleItem(
PmcData pmcData,
InventoryToggleRequestData info,
string sessionID
)
{
return _inventoryController.ToggleItem(pmcData, info, sessionID);
}
@@ -126,7 +158,11 @@ public class InventoryCallbacks(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData request, string sessionId)
public ItemEventRouterResponse TagItem(
PmcData pmcData,
InventoryTagRequestData request,
string sessionId
)
{
return _inventoryController.TagItem(pmcData, request, sessionId);
}
@@ -138,8 +174,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse BindItem(PmcData pmcData, InventoryBindRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse BindItem(
PmcData pmcData,
InventoryBindRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.BindItem(pmcData, info, sessionID, output);
return output;
@@ -152,8 +192,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse UnBindItem(PmcData pmcData, InventoryBindRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse UnBindItem(
PmcData pmcData,
InventoryBindRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.UnBindItem(pmcData, info, sessionID, output);
return output;
@@ -166,8 +210,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse ExamineItem(PmcData pmcData, InventoryExamineRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse ExamineItem(
PmcData pmcData,
InventoryExamineRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.ExamineItem(pmcData, info, sessionID, output);
return output;
@@ -180,8 +228,11 @@ public class InventoryCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData info,
string sessionID)
public ItemEventRouterResponse ReadEncyclopedia(
PmcData pmcData,
InventoryReadEncyclopediaRequestData info,
string sessionID
)
{
return _inventoryController.ReadEncyclopedia(pmcData, info, sessionID);
}
@@ -194,8 +245,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse SortInventory(PmcData pmcData, InventorySortRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse SortInventory(
PmcData pmcData,
InventorySortRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.SortInventory(pmcData, info, sessionID, output);
return output;
@@ -208,8 +263,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse CreateMapMarker(PmcData pmcData, InventoryCreateMarkerRequestData info,
string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse CreateMapMarker(
PmcData pmcData,
InventoryCreateMarkerRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.CreateMapMarker(pmcData, info, sessionID, output);
return output;
@@ -222,8 +281,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse DeleteMapMarker(PmcData pmcData, InventoryDeleteMarkerRequestData info,
string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse DeleteMapMarker(
PmcData pmcData,
InventoryDeleteMarkerRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.DeleteMapMarker(pmcData, info, sessionID, output);
return output;
@@ -236,8 +299,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse EditMapMarker(
PmcData pmcData,
InventoryEditMarkerRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.EditMapMarker(pmcData, info, sessionID, output);
return output;
@@ -251,9 +318,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse OpenRandomLootContainer(PmcData pmcData, OpenRandomLootContainerRequestData info,
public ItemEventRouterResponse OpenRandomLootContainer(
PmcData pmcData,
OpenRandomLootContainerRequestData info,
string sessionID,
ItemEventRouterResponse output)
ItemEventRouterResponse output
)
{
_inventoryController.OpenRandomLootContainer(pmcData, info, sessionID, output);
return output;
@@ -266,8 +336,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse RedeemProfileReward(
PmcData pmcData,
RedeemProfileRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.RedeemProfileReward(pmcData, info, sessionID);
return output;
@@ -281,8 +355,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse SetFavoriteItem(PmcData pmcData, SetFavoriteItems info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse SetFavoriteItem(
PmcData pmcData,
SetFavoriteItems info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.SetFavoriteItem(pmcData, info, sessionID);
return output;
@@ -297,8 +375,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse FailQuest(PmcData pmcData, FailQuestRequestData info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse FailQuest(
PmcData pmcData,
FailQuestRequestData info,
string sessionID,
ItemEventRouterResponse output
)
{
_questController.FailQuest(pmcData, info, sessionID, output);
return output;
@@ -311,8 +393,12 @@ public class InventoryCallbacks(
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
/// <returns></returns>
public ItemEventRouterResponse PinOrLock(PmcData pmcData, PinOrLockItemRequest info, string sessionID,
ItemEventRouterResponse output)
public ItemEventRouterResponse PinOrLock(
PmcData pmcData,
PinOrLockItemRequest info,
string sessionID,
ItemEventRouterResponse output
)
{
_inventoryController.PinOrLock(pmcData, info, sessionID, output);
return output;
@@ -7,13 +7,24 @@ using SPTarkov.Server.Core.Utils;
namespace SPTarkov.Server.Core.Callbacks;
[Injectable]
public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRouter _itemEventRouter)
public class ItemEventCallbacks(
HttpResponseUtil _httpResponseUtil,
ItemEventRouter _itemEventRouter
)
{
public async ValueTask<string> HandleEvents(string url, ItemEventRouterRequest info, string sessionID)
public async ValueTask<string> HandleEvents(
string url,
ItemEventRouterRequest info,
string sessionID
)
{
var eventResponse = await _itemEventRouter.HandleEvents(info, sessionID);
var result = IsCriticalError(eventResponse.Warnings)
? _httpResponseUtil.GetBody(eventResponse, GetErrorCode(eventResponse.Warnings), eventResponse.Warnings[0].ErrorMessage)
? _httpResponseUtil.GetBody(
eventResponse,
GetErrorCode(eventResponse.Warnings),
eventResponse.Warnings[0].ErrorMessage
)
: _httpResponseUtil.GetBody(eventResponse);
return result;
@@ -34,7 +45,7 @@ public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRou
// List of non-critical error codes, we return true if any error NOT included is passed in
var nonCriticalErrorCodes = new HashSet<BackendErrorCodes>
{
BackendErrorCodes.NotEnoughSpace
BackendErrorCodes.NotEnoughSpace,
};
foreach (var warning in warnings)
@@ -51,6 +62,8 @@ public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRou
public static BackendErrorCodes GetErrorCode(List<Warning> warnings)
{
// Cast int to string to get the error code of 220 for Unknown Error.
return warnings.FirstOrDefault()?.Code is null ? BackendErrorCodes.UnknownError : warnings.FirstOrDefault()?.Code ?? BackendErrorCodes.UnknownError;
return warnings.FirstOrDefault()?.Code is null
? BackendErrorCodes.UnknownError
: warnings.FirstOrDefault()?.Code ?? BackendErrorCodes.UnknownError;
}
}
@@ -68,21 +68,33 @@ public class LauncherCallbacks(
public ValueTask<string> RemoveProfile(string url, RemoveProfileData info, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_saveServer.RemoveProfile(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_saveServer.RemoveProfile(sessionID))
);
}
public ValueTask<string> GetCompatibleTarkovVersion()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.GetCompatibleTarkovVersion()));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_launcherController.GetCompatibleTarkovVersion())
);
}
public ValueTask<string> GetLoadedServerMods()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.GetLoadedServerMods()));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_launcherController.GetLoadedServerMods())
);
}
public ValueTask<string> GetServerModsProfileUsed(string url, EmptyRequestData _, string sessionID)
public ValueTask<string> GetServerModsProfileUsed(
string url,
EmptyRequestData _,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(_launcherController.GetServerModsProfileUsed(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.NoBody(_launcherController.GetServerModsProfileUsed(sessionID))
);
}
}
@@ -15,32 +15,29 @@ public class LauncherV2Callbacks(
{
public ValueTask<string> Ping()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2PingResponse
{
Response = _launcherV2Controller.Ping()
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2PingResponse { Response = _launcherV2Controller.Ping() }
)
);
}
public ValueTask<string> Types()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2TypesResponse
{
Response = _launcherV2Controller.Types()
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2TypesResponse { Response = _launcherV2Controller.Types() }
)
);
}
public ValueTask<string> Login(LoginRequestData info)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2LoginResponse
{
Response = _launcherV2Controller.Login(info)
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2LoginResponse { Response = _launcherV2Controller.Login(info) }
)
);
}
public async ValueTask<string> Register(RegisterData info)
@@ -49,7 +46,7 @@ public class LauncherV2Callbacks(
new LauncherV2RegisterResponse
{
Response = await _launcherV2Controller.Register(info),
Profiles = _profileController.GetMiniProfiles()
Profiles = _profileController.GetMiniProfiles(),
}
);
}
@@ -60,63 +57,67 @@ public class LauncherV2Callbacks(
new LauncherV2PasswordChangeResponse
{
Response = await _launcherV2Controller.PasswordChange(info),
Profiles = _profileController.GetMiniProfiles()
Profiles = _profileController.GetMiniProfiles(),
}
);
}
public ValueTask<string> Remove(LoginRequestData info)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2RemoveResponse
{
Response = _launcherV2Controller.Remove(info),
Profiles = _profileController.GetMiniProfiles()
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2RemoveResponse
{
Response = _launcherV2Controller.Remove(info),
Profiles = _profileController.GetMiniProfiles(),
}
)
);
}
public ValueTask<string> CompatibleVersion()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2VersionResponse
{
Response = new LauncherV2CompatibleVersion
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2VersionResponse
{
SptVersion = _launcherV2Controller.SptVersion(),
EftVersion = _launcherV2Controller.EftVersion()
Response = new LauncherV2CompatibleVersion
{
SptVersion = _launcherV2Controller.SptVersion(),
EftVersion = _launcherV2Controller.EftVersion(),
},
}
}
));
)
);
}
public ValueTask<string> Mods()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2ModsResponse
{
Response = _launcherV2Controller.LoadedMods()
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2ModsResponse { Response = _launcherV2Controller.LoadedMods() }
)
);
}
public ValueTask<string> Profiles()
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2ProfilesResponse
{
Response = _profileController.GetMiniProfiles()
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2ProfilesResponse { Response = _profileController.GetMiniProfiles() }
)
);
}
public ValueTask<string> Profile(string? sessionId)
{
return new ValueTask<string>(_httpResponseUtil.NoBody(
new LauncherV2ProfileResponse
{
Response = _launcherV2Controller.GetProfile(sessionId)
}
));
return new ValueTask<string>(
_httpResponseUtil.NoBody(
new LauncherV2ProfileResponse
{
Response = _launcherV2Controller.GetProfile(sessionId),
}
)
);
}
}
@@ -18,15 +18,23 @@ public class LocationCallbacks(
/// <returns></returns>
public ValueTask<string> GetLocationData(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_locationController.GenerateAll(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_locationController.GenerateAll(sessionID))
);
}
/// <summary>
/// Handle client/airdrop/loot
/// </summary>
/// <returns></returns>
public ValueTask<string> GetAirdropLoot(string url, GetAirdropLootRequest info, string sessionID)
public ValueTask<string> GetAirdropLoot(
string url,
GetAirdropLootRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_locationController.GetAirDropLoot(info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_locationController.GetAirDropLoot(info))
);
}
}
@@ -54,12 +54,9 @@ public class MatchCallbacks(
/// <returns></returns>
public ValueTask<string> GroupCurrent(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(
new MatchGroupCurrentResponse
{
Squad = []
}
));
return new ValueTask<string>(
_httpResponseUtil.GetBody(new MatchGroupCurrentResponse { Squad = [] })
);
}
/// <summary>
@@ -84,7 +81,11 @@ public class MatchCallbacks(
/// Handle client/match/group/invite/send
/// </summary>
/// <returns></returns>
public ValueTask<string> SendGroupInvite(string url, MatchGroupInviteSendRequest info, string sessionID)
public ValueTask<string> SendGroupInvite(
string url,
MatchGroupInviteSendRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody("2427943f23698ay9f2863735"));
}
@@ -95,12 +96,7 @@ public class MatchCallbacks(
/// <returns></returns>
public ValueTask<string> AcceptGroupInvite(string url, RequestIdRequest info, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(
new List<GroupCharacter>
{
new()
}
));
return new ValueTask<string>(_httpResponseUtil.GetBody(new List<GroupCharacter> { new() }));
}
/// <summary>
@@ -134,7 +130,11 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> TransferGroup(string url, MatchGroupTransferRequest info, string sessionID)
public ValueTask<string> TransferGroup(
string url,
MatchGroupTransferRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
@@ -161,7 +161,11 @@ public class MatchCallbacks(
/// Handle client/analytics/event-disconnect
/// </summary>
/// <returns></returns>
public ValueTask<string> EventDisconnect(string url, PutMetricsRequestData info, string sessionID)
public ValueTask<string> EventDisconnect(
string url,
PutMetricsRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -179,9 +183,15 @@ public class MatchCallbacks(
/// Handle match/group/start_game
/// </summary>
/// <returns></returns>
public ValueTask<string> JoinMatch(string url, MatchGroupStartGameRequest info, string sessionID)
public ValueTask<string> JoinMatch(
string url,
MatchGroupStartGameRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.JoinMatch(info, sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_matchController.JoinMatch(info, sessionID))
);
}
/// <summary>
@@ -190,7 +200,9 @@ public class MatchCallbacks(
/// <returns></returns>
public ValueTask<string> GetMetrics(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_databaseService.GetMatch().Metrics));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_databaseService.GetMatch().Metrics)
);
}
/// <summary>
@@ -198,9 +210,15 @@ public class MatchCallbacks(
/// Handle client/match/group/status
/// </summary>
/// <returns></returns>
public ValueTask<string> GetGroupStatus(string url, MatchGroupStatusRequest info, string sessionID)
public ValueTask<string> GetGroupStatus(
string url,
MatchGroupStatusRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.GetGroupStatus(info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_matchController.GetGroupStatus(info))
);
}
/// <summary>
@@ -226,7 +244,11 @@ public class MatchCallbacks(
/// Handle client/match/group/player/remove
/// </summary>
/// <returns></returns>
public ValueTask<string> RemovePlayerFromGroup(string url, MatchGroupPlayerRemoveRequest info, string sessionID)
public ValueTask<string> RemovePlayerFromGroup(
string url,
MatchGroupPlayerRemoveRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(true));
}
@@ -235,16 +257,26 @@ public class MatchCallbacks(
/// Handle client/match/local/start
/// </summary>
/// <returns></returns>
public ValueTask<string> StartLocalRaid(string url, StartLocalRaidRequestData info, string sessionID)
public ValueTask<string> StartLocalRaid(
string url,
StartLocalRaidRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_matchController.StartLocalRaid(sessionID, info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_matchController.StartLocalRaid(sessionID, info))
);
}
/// <summary>
/// Handle client/match/local/end
/// </summary>
/// <returns></returns>
public ValueTask<string> EndLocalRaid(string url, EndLocalRaidRequestData info, string sessionID)
public ValueTask<string> EndLocalRaid(
string url,
EndLocalRaidRequestData info,
string sessionID
)
{
_matchController.EndLocalRaid(sessionID, info);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -254,7 +286,11 @@ public class MatchCallbacks(
/// Handle client/raid/configuration
/// </summary>
/// <returns></returns>
public ValueTask<string> GetRaidConfiguration(string url, GetRaidConfigurationRequestData info, string sessionID)
public ValueTask<string> GetRaidConfiguration(
string url,
GetRaidConfigurationRequestData info,
string sessionID
)
{
_matchController.ConfigureOfflineRaid(info, sessionID);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -267,7 +303,11 @@ public class MatchCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> GetConfigurationByProfile(string url, GetRaidConfigurationRequestData info, string sessionID)
public ValueTask<string> GetConfigurationByProfile(
string url,
GetRaidConfigurationRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
@@ -16,7 +16,11 @@ public class NoteCallbacks(NoteController _noteController)
/// <param name="request">Add note request</param>
/// <param name="sessionID">Session/player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AddNote(PmcData pmcData, NoteActionRequest request, string sessionID)
public ItemEventRouterResponse AddNote(
PmcData pmcData,
NoteActionRequest request,
string sessionID
)
{
return _noteController.AddNote(pmcData, request, sessionID);
}
@@ -28,7 +32,11 @@ public class NoteCallbacks(NoteController _noteController)
/// <param name="request">Edit note request</param>
/// <param name="sessionID">Session/player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse EditNote(PmcData pmcData, NoteActionRequest request, string sessionID)
public ItemEventRouterResponse EditNote(
PmcData pmcData,
NoteActionRequest request,
string sessionID
)
{
return _noteController.EditNote(pmcData, request, sessionID);
}
@@ -40,7 +48,11 @@ public class NoteCallbacks(NoteController _noteController)
/// <param name="request">Delete note request</param>
/// <param name="sessionID">Session/player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse DeleteNote(PmcData pmcData, NoteActionRequest request, string sessionID)
public ItemEventRouterResponse DeleteNote(
PmcData pmcData,
NoteActionRequest request,
string sessionID
)
{
return _noteController.DeleteNote(pmcData, request, sessionID);
}
@@ -32,12 +32,14 @@ public class NotifierCallbacks(
* Take our array of JSON message objects and cast them to JSON strings, so that they can then
* be sent to client as NEWLINE separated strings... yup.
*/
_notifierController.NotifyAsync(tmpSessionID)
.ContinueWith(messages => messages.Result.Select(message => string.Join("\n", jsonUtil.Serialize(message))))
_notifierController
.NotifyAsync(tmpSessionID)
.ContinueWith(messages =>
messages.Result.Select(message => string.Join("\n", jsonUtil.Serialize(message)))
)
.ContinueWith(text => httpServerHelper.SendTextJson(resp, text.Result));
}
/// <summary>
/// TODO: removed from client?
/// Handle push/notifier/get
@@ -55,7 +57,9 @@ public class NotifierCallbacks(
/// <returns></returns>
public ValueTask<string> CreateNotifierChannel(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_notifierController.GetChannel(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_notifierController.GetChannel(sessionID))
);
}
/// <summary>
@@ -64,12 +68,9 @@ public class NotifierCallbacks(
/// <returns></returns>
public ValueTask<string> SelectProfile(string url, UIDRequestData info, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(
new SelectProfileResponse
{
Status = "ok"
}
));
return new ValueTask<string>(
_httpResponseUtil.GetBody(new SelectProfileResponse { Status = "ok" })
);
}
/// <summary>
@@ -21,7 +21,9 @@ public class PrestigeCallbacks(
/// <returns></returns>
public ValueTask<string> GetPrestige(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_prestigeController.GetPrestige(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_prestigeController.GetPrestige(sessionID))
);
}
/// <summary>
@@ -31,7 +33,11 @@ public class PrestigeCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public async ValueTask<string> ObtainPrestige(string url, ObtainPrestigeRequestList info, string sessionID)
public async ValueTask<string> ObtainPrestige(
string url,
ObtainPrestigeRequestList info,
string sessionID
)
{
await _prestigeController.ObtainPrestige(sessionID, info);
@@ -22,15 +22,14 @@ public class ProfileCallbacks(
/// Handle client/game/profile/create
/// </summary>
/// <returns></returns>
public async ValueTask<string> CreateProfile(string url, ProfileCreateRequestData info, string sessionID)
public async ValueTask<string> CreateProfile(
string url,
ProfileCreateRequestData info,
string sessionID
)
{
var id = await _profileController.CreateProfile(info, sessionID);
return _httpResponse.GetBody(
new CreateProfileResponse
{
UserId = id
}
);
return _httpResponse.GetBody(new CreateProfileResponse { UserId = id });
}
/// <summary>
@@ -40,7 +39,9 @@ public class ProfileCallbacks(
/// <returns></returns>
public ValueTask<string> GetProfileData(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponse.GetBody(_profileController.GetCompleteProfile(sessionID)));
return new ValueTask<string>(
_httpResponse.GetBody(_profileController.GetCompleteProfile(sessionID))
);
}
/// <summary>
@@ -51,19 +52,22 @@ public class ProfileCallbacks(
/// <returns></returns>
public ValueTask<string> RegenerateScav(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponse.GetBody(
new List<PmcData>
{
_profileController.GeneratePlayerScav(sessionID)
}
));
return new ValueTask<string>(
_httpResponse.GetBody(
new List<PmcData> { _profileController.GeneratePlayerScav(sessionID) }
)
);
}
/// <summary>
/// Handle client/game/profile/voice/change event
/// </summary>
/// <returns></returns>
public ValueTask<string> ChangeVoice(string url, ProfileChangeVoiceRequestData info, string sessionID)
public ValueTask<string> ChangeVoice(
string url,
ProfileChangeVoiceRequestData info,
string sessionID
)
{
_profileController.ChangeVoice(info, sessionID);
return new ValueTask<string>(_httpResponse.NullResponse());
@@ -74,21 +78,35 @@ public class ProfileCallbacks(
/// Client allows player to adjust their profile name
/// </summary>
/// <returns>Client response as string</returns>
public ValueTask<string> ChangeNickname(string url, ProfileChangeNicknameRequestData info, string sessionId)
public ValueTask<string> ChangeNickname(
string url,
ProfileChangeNicknameRequestData info,
string sessionId
)
{
var output = _profileController.ChangeNickname(info, sessionId);
return output switch
{
NicknameValidationResult.Taken => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotUnique, $"{BackendErrorCodes.NicknameNotUnique} - ")),
NicknameValidationResult.Short => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotValid, $"{BackendErrorCodes.NicknameNotValid} - ")),
_ => new ValueTask<string>(_httpResponse.GetBody<object>(
new
{
status = 0,
NicknameChangeDate = _timeUtil.GetTimeStamp()
}
))
NicknameValidationResult.Taken => new ValueTask<string>(
_httpResponse.GetBody<object?>(
null,
BackendErrorCodes.NicknameNotUnique,
$"{BackendErrorCodes.NicknameNotUnique} - "
)
),
NicknameValidationResult.Short => new ValueTask<string>(
_httpResponse.GetBody<object?>(
null,
BackendErrorCodes.NicknameNotValid,
$"{BackendErrorCodes.NicknameNotValid} - "
)
),
_ => new ValueTask<string>(
_httpResponse.GetBody<object>(
new { status = 0, NicknameChangeDate = _timeUtil.GetTimeStamp() }
)
),
};
}
@@ -96,18 +114,29 @@ public class ProfileCallbacks(
/// Handle client/game/profile/nickname/validate
/// </summary>
/// <returns>Client response as string</returns>
public ValueTask<string> ValidateNickname(string url, ValidateNicknameRequestData info, string sessionId)
public ValueTask<string> ValidateNickname(
string url,
ValidateNicknameRequestData info,
string sessionId
)
{
return _profileController.ValidateNickname(info, sessionId) switch
{
NicknameValidationResult.Taken => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotUnique, $"{BackendErrorCodes.NicknameNotUnique} - ")),
NicknameValidationResult.Short => new ValueTask<string>(_httpResponse.GetBody<object?>(null, BackendErrorCodes.NicknameNotValid, $"{BackendErrorCodes.NicknameNotValid} - ")),
_ => new ValueTask<string>(_httpResponse.GetBody(
new
{
status = "ok"
}
))
NicknameValidationResult.Taken => new ValueTask<string>(
_httpResponse.GetBody<object?>(
null,
BackendErrorCodes.NicknameNotUnique,
$"{BackendErrorCodes.NicknameNotUnique} - "
)
),
NicknameValidationResult.Short => new ValueTask<string>(
_httpResponse.GetBody<object?>(
null,
BackendErrorCodes.NicknameNotValid,
$"{BackendErrorCodes.NicknameNotValid} - "
)
),
_ => new ValueTask<string>(_httpResponse.GetBody(new { status = "ok" })),
};
}
@@ -134,7 +163,9 @@ public class ProfileCallbacks(
/// <returns></returns>
public ValueTask<string> GetProfileStatus(string url, EmptyRequestData _, string sessionId)
{
return new ValueTask<string>(_httpResponse.GetBody(_profileController.GetProfileStatus(sessionId)));
return new ValueTask<string>(
_httpResponse.GetBody(_profileController.GetProfileStatus(sessionId))
);
}
/// <summary>
@@ -142,36 +173,60 @@ public class ProfileCallbacks(
/// Called when viewing another players profile
/// </summary>
/// <returns></returns>
public ValueTask<string> GetOtherProfile(string url, GetOtherProfileRequest request, string sessionID)
public ValueTask<string> GetOtherProfile(
string url,
GetOtherProfileRequest request,
string sessionID
)
{
return new ValueTask<string>(_httpResponse.GetBody(_profileController.GetOtherProfile(sessionID, request)));
return new ValueTask<string>(
_httpResponse.GetBody(_profileController.GetOtherProfile(sessionID, request))
);
}
/// <summary>
/// Handle client/profile/settings
/// </summary>
/// <returns></returns>
public ValueTask<string> GetProfileSettings(string url, GetProfileSettingsRequest info, string sessionID)
public ValueTask<string> GetProfileSettings(
string url,
GetProfileSettingsRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponse.GetBody(_profileController.SetChosenProfileIcon(sessionID, info)));
return new ValueTask<string>(
_httpResponse.GetBody(_profileController.SetChosenProfileIcon(sessionID, info))
);
}
/// <summary>
/// Handle client/game/profile/search
/// </summary>
/// <returns></returns>
public ValueTask<string> SearchProfiles(string url, SearchProfilesRequestData info, string sessionID)
public ValueTask<string> SearchProfiles(
string url,
SearchProfilesRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponse.GetBody(_profileController.SearchProfiles(info, sessionID)));
return new ValueTask<string>(
_httpResponse.GetBody(_profileController.SearchProfiles(info, sessionID))
);
}
/// <summary>
/// Handle launcher/profile/info
/// </summary>
/// <returns></returns>
public ValueTask<string> GetMiniProfile(string url, GetMiniProfileRequestData info, string sessionID)
public ValueTask<string> GetMiniProfile(
string url,
GetMiniProfileRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponse.NoBody(_profileController.GetMiniProfile(sessionID)));
return new ValueTask<string>(
_httpResponse.NoBody(_profileController.GetMiniProfile(sessionID))
);
}
/// <summary>
@@ -21,7 +21,11 @@ public class QuestCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ChangeRepeatableQuest(PmcData pmcData, RepeatableQuestChangeRequest info, string sessionID)
public ItemEventRouterResponse ChangeRepeatableQuest(
PmcData pmcData,
RepeatableQuestChangeRequest info,
string sessionID
)
{
return _repeatableQuestController.ChangeRepeatableQuest(pmcData, info, sessionID);
}
@@ -33,7 +37,11 @@ public class QuestCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse AcceptQuest(PmcData pmcData, AcceptQuestRequestData info, string sessionID)
public ItemEventRouterResponse AcceptQuest(
PmcData pmcData,
AcceptQuestRequestData info,
string sessionID
)
{
if (info.Type == "repeatable")
{
@@ -50,7 +58,11 @@ public class QuestCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse CompleteQuest(PmcData pmcData, CompleteQuestRequestData info, string sessionID)
public ItemEventRouterResponse CompleteQuest(
PmcData pmcData,
CompleteQuestRequestData info,
string sessionID
)
{
return _questController.CompleteQuest(pmcData, info, sessionID);
}
@@ -62,7 +74,11 @@ public class QuestCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse HandoverQuest(PmcData pmcData, HandoverQuestRequestData info, string sessionID)
public ItemEventRouterResponse HandoverQuest(
PmcData pmcData,
HandoverQuestRequestData info,
string sessionID
)
{
return _questController.HandoverQuest(pmcData, info, sessionID);
}
@@ -76,7 +92,9 @@ public class QuestCallbacks(
/// <returns></returns>
public ValueTask<string> ListQuests(string url, ListQuestsRequestData info, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_questController.GetClientQuests(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_questController.GetClientQuests(sessionID))
);
}
/// <summary>
@@ -88,6 +106,10 @@ public class QuestCallbacks(
/// <returns></returns>
public ValueTask<string> ActivityPeriods(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_repeatableQuestController.GetClientRepeatableQuests(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(
_repeatableQuestController.GetClientRepeatableQuests(sessionID)
)
);
}
}
@@ -61,7 +61,9 @@ public class RagfairCallbacks(
/// <returns></returns>
public ValueTask<string> Search(string url, SearchRequestData info, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetOffers(sessionID, info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_ragfairController.GetOffers(sessionID, info))
);
}
/// <summary>
@@ -71,9 +73,15 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> GetMarketPrice(string url, GetMarketPriceRequestData info, string sessionID)
public ValueTask<string> GetMarketPrice(
string url,
GetMarketPriceRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetItemMinAvgMaxFleaPriceValues(info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_ragfairController.GetItemMinAvgMaxFleaPriceValues(info))
);
}
/// <summary>
@@ -83,7 +91,11 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse AddOffer(PmcData pmcData, AddOfferRequestData info, string sessionID)
public ItemEventRouterResponse AddOffer(
PmcData pmcData,
AddOfferRequestData info,
string sessionID
)
{
return _ragfairController.AddPlayerOffer(pmcData, info, sessionID);
}
@@ -95,7 +107,11 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse RemoveOffer(PmcData pmcData, RemoveOfferRequestData info, string sessionID)
public ItemEventRouterResponse RemoveOffer(
PmcData pmcData,
RemoveOfferRequestData info,
string sessionID
)
{
return _ragfairController.FlagOfferForRemoval(info.OfferId, sessionID);
}
@@ -107,7 +123,11 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ExtendOffer(PmcData pmcData, ExtendOfferRequestData info, string sessionID)
public ItemEventRouterResponse ExtendOffer(
PmcData pmcData,
ExtendOfferRequestData info,
string sessionID
)
{
return _ragfairController.ExtendOffer(info, sessionID);
}
@@ -122,7 +142,9 @@ public class RagfairCallbacks(
/// <returns></returns>
public ValueTask<string> GetFleaPrices(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetAllFleaPrices()));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_ragfairController.GetAllFleaPrices())
);
}
/// <summary>
@@ -132,12 +154,20 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> SendReport(string url, SendRagfairReportRequestData info, string sessionID)
public ValueTask<string> SendReport(
string url,
SendRagfairReportRequestData info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.NullResponse());
}
public ValueTask<string> StorePlayerOfferTaxAmount(string url, StorePlayerOfferTaxAmountRequestData info, string sessionID)
public ValueTask<string> StorePlayerOfferTaxAmount(
string url,
StorePlayerOfferTaxAmountRequestData info,
string sessionID
)
{
_ragfairTaxService.StoreClientOfferTaxValue(sessionID, info);
return new ValueTask<string>(_httpResponseUtil.NullResponse());
@@ -150,8 +180,14 @@ public class RagfairCallbacks(
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ValueTask<string> GetFleaOfferById(string url, GetRagfairOfferByIdRequest info, string sessionID)
public ValueTask<string> GetFleaOfferById(
string url,
GetRagfairOfferByIdRequest info,
string sessionID
)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_ragfairController.GetOfferByInternalId(sessionID, info)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_ragfairController.GetOfferByInternalId(sessionID, info))
);
}
}
@@ -17,7 +17,11 @@ public class RepairCallbacks(RepairController _repairController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse TraderRepair(PmcData pmcData, TraderRepairActionDataRequest info, string sessionID)
public ItemEventRouterResponse TraderRepair(
PmcData pmcData,
TraderRepairActionDataRequest info,
string sessionID
)
{
return _repairController.TraderRepair(sessionID, info, pmcData);
}
@@ -30,7 +34,11 @@ public class RepairCallbacks(RepairController _repairController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse Repair(PmcData pmcData, RepairActionDataRequest info, string sessionID)
public ItemEventRouterResponse Repair(
PmcData pmcData,
RepairActionDataRequest info,
string sessionID
)
{
return _repairController.RepairWithKit(sessionID, info, pmcData);
}
@@ -11,8 +11,7 @@ public class SaveCallbacks(
SaveServer _saveServer,
ConfigServer _configServer,
BackupService _backupService
)
: IOnLoad, IOnUpdate
) : IOnLoad, IOnUpdate
{
private readonly CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
@@ -16,7 +16,11 @@ public class TradeCallbacks(TradeController _tradeController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ProcessTrade(PmcData pmcData, ProcessBaseTradeRequestData info, string sessionID)
public ItemEventRouterResponse ProcessTrade(
PmcData pmcData,
ProcessBaseTradeRequestData info,
string sessionID
)
{
return _tradeController.ConfirmTrading(pmcData, info, sessionID);
}
@@ -28,7 +32,11 @@ public class TradeCallbacks(TradeController _tradeController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ProcessRagfairTrade(PmcData pmcData, ProcessRagfairTradeRequestData info, string sessionID)
public ItemEventRouterResponse ProcessRagfairTrade(
PmcData pmcData,
ProcessRagfairTradeRequestData info,
string sessionID
)
{
return _tradeController.ConfirmRagfairTrading(pmcData, info, sessionID);
}
@@ -40,7 +48,11 @@ public class TradeCallbacks(TradeController _tradeController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse SellAllFromSavage(PmcData pmcData, SellScavItemsToFenceRequestData info, string sessionID)
public ItemEventRouterResponse SellAllFromSavage(
PmcData pmcData,
SellScavItemsToFenceRequestData info,
string sessionID
)
{
return _tradeController.SellScavItemsToFence(pmcData, info, sessionID);
}
@@ -35,7 +35,9 @@ public class TraderCallbacks(
/// </summary>
public ValueTask<string> GetTraderSettings(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetAllTraders(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_traderController.GetAllTraders(sessionID))
);
}
/// <summary>
@@ -44,7 +46,9 @@ public class TraderCallbacks(
public ValueTask<string> GetTrader(string url, EmptyRequestData _, string sessionID)
{
var traderID = url.Replace("/client/trading/api/getTrader/", "");
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetTrader(sessionID, traderID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_traderController.GetTrader(sessionID, traderID))
);
}
/// <summary>
@@ -54,7 +58,9 @@ public class TraderCallbacks(
public ValueTask<string> GetAssort(string url, EmptyRequestData _, string sessionID)
{
var traderID = url.Replace("/client/trading/api/getTraderAssort/", "");
return new ValueTask<string>(_httpResponseUtil.GetBody(_traderController.GetAssort(sessionID, traderID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_traderController.GetAssort(sessionID, traderID))
);
}
/// <summary>
@@ -32,6 +32,8 @@ public class WeatherCallbacks(
/// <returns></returns>
public ValueTask<string> GetLocalWeather(string url, EmptyRequestData _, string sessionID)
{
return new ValueTask<string>(_httpResponseUtil.GetBody(_weatherController.GenerateLocal(sessionID)));
return new ValueTask<string>(
_httpResponseUtil.GetBody(_weatherController.GenerateLocal(sessionID))
);
}
}
@@ -16,7 +16,11 @@ public class WishlistCallbacks(WishlistController _wishlistController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse AddToWishlist(PmcData pmcData, AddToWishlistRequest info, string sessionID)
public ItemEventRouterResponse AddToWishlist(
PmcData pmcData,
AddToWishlistRequest info,
string sessionID
)
{
return _wishlistController.AddToWishList(pmcData, info, sessionID);
}
@@ -28,7 +32,11 @@ public class WishlistCallbacks(WishlistController _wishlistController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse RemoveFromWishlist(PmcData pmcData, RemoveFromWishlistRequest info, string sessionID)
public ItemEventRouterResponse RemoveFromWishlist(
PmcData pmcData,
RemoveFromWishlistRequest info,
string sessionID
)
{
return _wishlistController.RemoveFromWishList(pmcData, info, sessionID);
}
@@ -40,7 +48,11 @@ public class WishlistCallbacks(WishlistController _wishlistController)
/// <param name="info"></param>
/// <param name="sessionID">Session/player id</param>
/// <returns></returns>
public ItemEventRouterResponse ChangeWishlistItemCategory(PmcData pmcData, ChangeWishlistItemCategoryRequest info, string sessionID)
public ItemEventRouterResponse ChangeWishlistItemCategory(
PmcData pmcData,
ChangeWishlistItemCategoryRequest info,
string sessionID
)
{
return _wishlistController.ChangeWishListItemCategory(pmcData, info, sessionID);
}
@@ -23,10 +23,7 @@ public class AchievementController(
/// <returns></returns>
public virtual GetAchievementsResponse GetAchievements(string sessionID)
{
return new GetAchievementsResponse
{
Elements = databaseService.GetAchievements()
};
return new GetAchievementsResponse { Elements = databaseService.GetAchievements() };
}
/// <summary>
@@ -37,12 +34,21 @@ public class AchievementController(
public virtual CompletedAchievementsResponse GetAchievementStatics(string sessionId)
{
var stats = new Dictionary<string, int>();
var profiles = profileHelper.GetProfiles()
.Where(kvp => !coreConfig.Features.AchievementProfileIdBlacklist.Contains(kvp.Value.ProfileInfo.ProfileId))
var profiles = profileHelper
.GetProfiles()
.Where(kvp =>
!coreConfig.Features.AchievementProfileIdBlacklist.Contains(
kvp.Value.ProfileInfo.ProfileId
)
)
.ToDictionary();
var achievements = databaseService.GetAchievements();
foreach (var achievementId in achievements.Select(achievement => achievement.Id).Where(achievementId => !string.IsNullOrEmpty(achievementId)))
foreach (
var achievementId in achievements
.Select(achievement => achievement.Id)
.Where(achievementId => !string.IsNullOrEmpty(achievementId))
)
{
var profilesHaveAchievement = 0;
foreach (var (profileId, profile) in profiles)
@@ -63,15 +69,13 @@ public class AchievementController(
var percentage = 0;
if (profiles.Count > 0)
{
percentage = (int)Math.Round((double)profilesHaveAchievement / profiles.Count * 100);
percentage = (int)
Math.Round((double)profilesHaveAchievement / profiles.Count * 100);
}
stats.Add(achievementId, percentage);
}
return new CompletedAchievementsResponse
{
Elements = stats
};
return new CompletedAchievementsResponse { Elements = stats };
}
}
@@ -1,7 +1,7 @@
using System.Diagnostics;
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Constants;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Constants;
using SPTarkov.Server.Core.Generators;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
@@ -50,7 +50,9 @@ public class BotController(
{
if (!_botConfig.PresetBatch.TryGetValue(type, out var limit))
{
_logger.Warning(_localisationService.GetText("bot-bot_preset_count_value_missing", type));
_logger.Warning(
_localisationService.GetText("bot-bot_preset_count_value_missing", type)
);
return 10;
}
@@ -78,23 +80,38 @@ public class BotController(
/// <param name="raidConfig">OPTIONAL - applicationContext Data stored at start of raid</param>
/// <param name="ignoreRaidSettings">OPTIONAL - should raid settings chosen pre-raid be ignored</param>
/// <returns>Difficulty object</returns>
public DifficultyCategories GetBotDifficulty(string sessionId, string type, string diffLevel, bool ignoreRaidSettings = false)
public DifficultyCategories GetBotDifficulty(
string sessionId,
string type,
string diffLevel,
bool ignoreRaidSettings = false
)
{
var difficulty = diffLevel.ToLower();
var raidConfig = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
var raidConfig = _profileActivityService
.GetProfileActivityRaidData(sessionId)
?.RaidConfiguration;
if (!(raidConfig != null || ignoreRaidSettings))
{
_logger.Error(_localisationService.GetText("bot-missing_application_context", "RAID_CONFIGURATION"));
_logger.Error(
_localisationService.GetText(
"bot-missing_application_context",
"RAID_CONFIGURATION"
)
);
}
// Check value chosen in pre-raid difficulty dropdown
// If value is not 'asonline', change requested difficulty to be what was chosen in dropdown
var botDifficultyDropDownValue = raidConfig?.WavesSettings?.BotDifficulty?.ToString().ToLower() ?? "asonline";
var botDifficultyDropDownValue =
raidConfig?.WavesSettings?.BotDifficulty?.ToString().ToLower() ?? "asonline";
if (botDifficultyDropDownValue != "asonline")
{
difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(
botDifficultyDropDownValue
);
}
var botDb = _databaseService.GetBots();
@@ -130,7 +147,9 @@ public class BotController(
result[botTypeLower] = result[Roles.Assault];
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to find bot: {botTypeLower} in db, copying '{Roles.Assault}'");
_logger.Debug(
$"Unable to find bot: {botTypeLower} in db, copying '{Roles.Assault}'"
);
}
continue;
@@ -139,7 +158,9 @@ public class BotController(
if (botDetails?.BotDifficulty is null)
{
// Bot has no difficulty values, skip
_logger.Warning($"Unable to find bot: {botTypeLower} difficulty values in db, skipping");
_logger.Warning(
$"Unable to find bot: {botTypeLower} difficulty values in db, skipping"
);
continue;
}
@@ -153,7 +174,11 @@ public class BotController(
}
// Store all difficulty values in dict keyed by difficulty type e.g. easy/normal/impossible
result[botNameKey].Add(difficultyName, GetBotDifficulty(string.Empty, botNameKey, difficultyName, true));
result[botNameKey]
.Add(
difficultyName,
GetBotDifficulty(string.Empty, botNameKey, difficultyName, true)
);
}
}
@@ -180,7 +205,11 @@ public class BotController(
/// <param name="pmcProfile">Player generating bots</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>List of generated bots</returns>
protected List<BotBase> GenerateBotWaves(GenerateBotsRequestData request, PmcData? pmcProfile, string sessionId)
protected List<BotBase> GenerateBotWaves(
GenerateBotsRequestData request,
PmcData? pmcProfile,
string sessionId
)
{
var generatedBotList = new List<BotBase>();
var raidSettings = GetMostRecentRaidSettings(sessionId);
@@ -191,23 +220,36 @@ public class BotController(
var stopwatch = Stopwatch.StartNew();
// Map conditions to promises for bot generation
Task.WaitAll((request.Conditions ?? [])
.Select(condition => Task.Factory.StartNew(() =>
{
var botWaveGenerationDetails = GetBotGenerationDetailsForWave(
condition,
pmcProfile,
allPmcsHaveSameNameAsPlayer,
raidSettings);
Task.WaitAll(
(request.Conditions ?? [])
.Select(condition =>
Task.Factory.StartNew(() =>
{
var botWaveGenerationDetails = GetBotGenerationDetailsForWave(
condition,
pmcProfile,
allPmcsHaveSameNameAsPlayer,
raidSettings
);
GenerateBotWave(condition, botWaveGenerationDetails, generatedBotList, sessionId);
})).ToArray());
GenerateBotWave(
condition,
botWaveGenerationDetails,
generatedBotList,
sessionId
);
})
)
.ToArray()
);
stopwatch.Stop();
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache()");
_logger.Debug(
$"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache()"
);
}
return generatedBotList;
@@ -225,9 +267,13 @@ public class BotController(
GenerateCondition generateRequest,
BotGenerationDetails botGenerationDetails,
List<BotBase> botList,
string sessionId)
string sessionId
)
{
var isEventBot = generateRequest.Role?.Contains("event", StringComparison.OrdinalIgnoreCase);
var isEventBot = generateRequest.Role?.Contains(
"event",
StringComparison.OrdinalIgnoreCase
);
if (isEventBot.GetValueOrDefault(false))
{
// Add eventRole data + reassign role property to be base type
@@ -241,45 +287,55 @@ public class BotController(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Generating wave of: {botGenerationDetails.BotCountToGenerate} bots of type: {role} {botGenerationDetails.BotDifficulty}");
_logger.Debug(
$"Generating wave of: {botGenerationDetails.BotCountToGenerate} bots of type: {role} {botGenerationDetails.BotDifficulty}"
);
}
Parallel.For(0, botGenerationDetails.BotCountToGenerate.Value, (i) =>
{
BotBase bot = null;
try
Parallel.For(
0,
botGenerationDetails.BotCountToGenerate.Value,
(i) =>
{
bot = _botGenerator.PrepareAndGenerateBot(sessionId, _cloner.Clone(botGenerationDetails));
}
catch (Exception e)
{
_logger.Error(
$"Failed to generate bot: {botGenerationDetails.Role} #{i + 1}: {e.Message} {e.StackTrace}");
return;
}
BotBase bot = null;
// The client expects the Side for PMCs to be `Savage`
// We do this here so it's after we cache the bot in the match details lookup, as when you die, they will have the right side
if (bot.Info.Side is Sides.Bear or Sides.Usec)
{
bot.Info.Side = Sides.Savage;
}
try
{
bot = _botGenerator.PrepareAndGenerateBot(
sessionId,
_cloner.Clone(botGenerationDetails)
);
}
catch (Exception e)
{
_logger.Error(
$"Failed to generate bot: {botGenerationDetails.Role} #{i + 1}: {e.Message} {e.StackTrace}"
);
return;
}
lock (_botListLock)
{
botList.Add(bot);
}
// The client expects the Side for PMCs to be `Savage`
// We do this here so it's after we cache the bot in the match details lookup, as when you die, they will have the right side
if (bot.Info.Side is Sides.Bear or Sides.Usec)
{
bot.Info.Side = Sides.Savage;
}
// Store bot details in cache so post-raid PMC messages can use data
_matchBotDetailsCacheService.CacheBot(bot);
});
lock (_botListLock)
{
botList.Add(bot);
}
// Store bot details in cache so post-raid PMC messages can use data
_matchBotDetailsCacheService.CacheBot(bot);
}
);
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
$"Generated: {botGenerationDetails.BotCountToGenerate} {botGenerationDetails.Role}" +
$"({botGenerationDetails.EventRole ?? botGenerationDetails.Role ?? ""}) {botGenerationDetails.BotDifficulty} bots"
$"Generated: {botGenerationDetails.BotCountToGenerate} {botGenerationDetails.Role}"
+ $"({botGenerationDetails.EventRole ?? botGenerationDetails.Role ?? ""}) {botGenerationDetails.BotDifficulty} bots"
);
}
}
@@ -290,11 +346,15 @@ public class BotController(
/// <returns>GetRaidConfigurationRequestData if it exists</returns>
protected GetRaidConfigurationRequestData? GetMostRecentRaidSettings(string sessionId)
{
var raidConfiguration = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
var raidConfiguration = _profileActivityService
.GetProfileActivityRaidData(sessionId)
?.RaidConfiguration;
if (raidConfiguration is null)
{
_logger.Warning(_localisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
_logger.Warning(
_localisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext")
);
}
return raidConfiguration;
@@ -307,7 +367,10 @@ public class BotController(
/// <returns>MinMax values</returns>
protected MinMax<int> GetPmcLevelRangeForMap(string? location)
{
return _pmcConfig.LocationSpecificPmcLevelOverride!.GetValueOrDefault(location?.ToLower() ?? "", null);
return _pmcConfig.LocationSpecificPmcLevelOverride!.GetValueOrDefault(
location?.ToLower() ?? "",
null
);
}
/// <summary>
@@ -322,24 +385,30 @@ public class BotController(
GenerateCondition condition,
PmcData? pmcProfile,
bool allPmcsHaveSameNameAsPlayer,
GetRaidConfigurationRequestData? raidSettings)
GetRaidConfigurationRequestData? raidSettings
)
{
var generateAsPmc = _botHelper.IsBotPmc(condition.Role);
return new BotGenerationDetails
{
IsPmc = generateAsPmc,
Side = generateAsPmc ? _botHelper.GetPmcSideByRole(condition.Role ?? string.Empty) : "Savage",
Side = generateAsPmc
? _botHelper.GetPmcSideByRole(condition.Role ?? string.Empty)
: "Savage",
Role = condition.Role,
PlayerLevel = pmcProfile?.Info?.Level ?? 1,
PlayerName = pmcProfile?.Info?.Nickname,
BotRelativeLevelDeltaMax = _pmcConfig.BotRelativeLevelDeltaMax,
BotRelativeLevelDeltaMin = _pmcConfig.BotRelativeLevelDeltaMin,
BotCountToGenerate = Math.Max(GetBotPresetGenerationLimit(condition.Role), condition.Limit), // Choose largest between value passed in from request vs what's in bot.config
BotCountToGenerate = Math.Max(
GetBotPresetGenerationLimit(condition.Role),
condition.Limit
), // Choose largest between value passed in from request vs what's in bot.config
BotDifficulty = condition.Difficulty,
LocationSpecificPmcLevelOverride = GetPmcLevelRangeForMap(raidSettings?.Location), // Min/max levels for PMCs to generate within
IsPlayerScav = false,
AllPmcsHaveSameNameAsPlayer = allPmcsHaveSameNameAsPlayer
AllPmcsHaveSameNameAsPlayer = allPmcsHaveSameNameAsPlayer,
};
}
@@ -359,7 +428,10 @@ public class BotController(
if (location == "default")
{
_logger.Warning(
_localisationService.GetText("bot-no_bot_cap_found_for_location", location.ToLower())
_localisationService.GetText(
"bot-no_bot_cap_found_for_location",
location.ToLower()
)
);
}
@@ -376,7 +448,7 @@ public class BotController(
{
PmcType = _pmcConfig.PmcType,
Assault = _botConfig.AssaultBrainType,
PlayerScav = _botConfig.PlayerScavBrainType
PlayerScav = _botConfig.PlayerScavBrainType,
};
}
}
@@ -384,23 +456,11 @@ public class BotController(
public record AiBotBrainTypes
{
[JsonPropertyName("pmc")]
public Dictionary<string, Dictionary<string, Dictionary<string, double>>> PmcType
{
get;
set;
}
public Dictionary<string, Dictionary<string, Dictionary<string, double>>> PmcType { get; set; }
[JsonPropertyName("assault")]
public Dictionary<string, Dictionary<string, int>> Assault
{
get;
set;
}
public Dictionary<string, Dictionary<string, int>> Assault { get; set; }
[JsonPropertyName("playerScav")]
public Dictionary<string, Dictionary<string, int>> PlayerScav
{
get;
set;
}
public Dictionary<string, Dictionary<string, int>> PlayerScav { get; set; }
}
@@ -42,30 +42,37 @@ public class BuildController(
{
EquipmentBuilds = [],
WeaponBuilds = [],
MagazineBuilds = []
MagazineBuilds = [],
};
}
// Ensure the secure container in the default presets match what the player has equipped
var defaultEquipmentPresetsClone = _cloner.Clone(_databaseService.GetTemplates().DefaultEquipmentPresets)
var defaultEquipmentPresetsClone = _cloner
.Clone(_databaseService.GetTemplates().DefaultEquipmentPresets)
.ToList();
// Get players secure container
var playerSecureContainer = profile?.CharacterData?.PmcData?.Inventory?.Items?.FirstOrDefault(x => x.SlotId == secureContainerSlotId
);
var playerSecureContainer =
profile?.CharacterData?.PmcData?.Inventory?.Items?.FirstOrDefault(x =>
x.SlotId == secureContainerSlotId
);
var firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone?
.FirstOrDefault()
?.Items?
.FirstOrDefault(x => x.SlotId == secureContainerSlotId);
var firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone
?.FirstOrDefault()
?.Items?.FirstOrDefault(x => x.SlotId == secureContainerSlotId);
if (playerSecureContainer is not null && playerSecureContainer.Template != firstDefaultItemsSecureContainer?.Template)
// Default equipment presets' secure container tpl doesn't match players secure container tpl
if (
playerSecureContainer is not null
&& playerSecureContainer.Template != firstDefaultItemsSecureContainer?.Template
)
// Default equipment presets' secure container tpl doesn't match players secure container tpl
{
foreach (var defaultPreset in defaultEquipmentPresetsClone)
{
// Find presets secure container
var secureContainer = defaultPreset.Items?.FirstOrDefault(item => item.SlotId == secureContainerSlotId);
var secureContainer = defaultPreset.Items?.FirstOrDefault(item =>
item.SlotId == secureContainerSlotId
);
if (secureContainer is not null)
{
secureContainer.Template = playerSecureContainer.Template;
@@ -102,7 +109,7 @@ public class BuildController(
Id = body.Id,
Name = body.Name,
Root = body.Root,
Items = body.Items
Items = body.Items,
};
var profile = _profileHelper.GetFullProfile(sessionId);
@@ -132,8 +139,9 @@ public class BuildController(
var profile = _profileHelper.GetFullProfile(sessionID);
var pmcData = profile.CharacterData.PmcData;
var existingSavedEquipmentBuilds =
_saveServer.GetProfile(sessionID).UserBuildData.EquipmentBuilds;
var existingSavedEquipmentBuilds = _saveServer
.GetProfile(sessionID)
.UserBuildData.EquipmentBuilds;
// Replace duplicate ID's. The first item is the base item.
// Root ID and the base item ID need to match.
@@ -145,10 +153,11 @@ public class BuildController(
Name = request.Name,
BuildType = EquipmentBuildType.Custom,
Root = request.Items[0].Id,
Items = request.Items
Items = request.Items,
};
var existingBuild = existingSavedEquipmentBuilds?.FirstOrDefault(build => build.Name == request.Name || build.Id == request.Id
var existingBuild = existingSavedEquipmentBuilds?.FirstOrDefault(build =>
build.Name == request.Name || build.Id == request.Id
);
if (existingBuild is not null)
{
@@ -190,7 +199,7 @@ public class BuildController(
Caliber = request.Caliber,
TopCount = request.TopCount,
BottomCount = request.BottomCount,
Items = request.Items
Items = request.Items,
};
var profile = _profileHelper.GetFullProfile(sessionId);
@@ -198,7 +207,9 @@ public class BuildController(
profile.UserBuildData.MagazineBuilds ??= [];
// Check if template with desired name already exists and remove it
var magazineBuildToRemove = profile.UserBuildData.MagazineBuilds.FirstOrDefault(item => item.Name == request.Name);
var magazineBuildToRemove = profile.UserBuildData.MagazineBuilds.FirstOrDefault(item =>
item.Name == request.Name
);
if (magazineBuildToRemove is not null)
{
profile.UserBuildData.MagazineBuilds.Remove(magazineBuildToRemove);
@@ -222,7 +233,9 @@ public class BuildController(
var magazineBuilds = profile.UserBuildData.MagazineBuilds;
// Check for id in weapon array first
var matchingWeaponBuild = weaponBuilds.FirstOrDefault(weaponBuild => weaponBuild.Id == idToRemove);
var matchingWeaponBuild = weaponBuilds.FirstOrDefault(weaponBuild =>
weaponBuild.Id == idToRemove
);
if (matchingWeaponBuild is not null)
{
weaponBuilds.Remove(matchingWeaponBuild);
@@ -231,7 +244,9 @@ public class BuildController(
}
// Id not found in weapons, try equipment
var matchingEquipmentBuild = equipmentBuilds.FirstOrDefault(equipmentBuild => equipmentBuild.Id == idToRemove);
var matchingEquipmentBuild = equipmentBuilds.FirstOrDefault(equipmentBuild =>
equipmentBuild.Id == idToRemove
);
if (matchingEquipmentBuild is not null)
{
equipmentBuilds.Remove(matchingEquipmentBuild);
@@ -240,7 +255,9 @@ public class BuildController(
}
// Id not found in weapons/equipment, try mags
var matchingMagazineBuild = magazineBuilds.FirstOrDefault(magBuild => magBuild.Id == idToRemove);
var matchingMagazineBuild = magazineBuilds.FirstOrDefault(magBuild =>
magBuild.Id == idToRemove
);
if (matchingMagazineBuild is not null)
{
magazineBuilds.Remove(matchingMagazineBuild);
@@ -7,9 +7,7 @@ using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class ClientLogController(
ISptLogger<ClientLogController> _logger
)
public class ClientLogController(ISptLogger<ClientLogController> _logger)
{
/// <summary>
/// Handle /singleplayer/log
@@ -44,16 +44,18 @@ public class CustomizationController(
var suits = _databaseService.GetTrader(traderId).Suits;
var matchingSuits = suits?.Where(s => clothing.ContainsKey(s.SuiteId!)).ToList();
matchingSuits = matchingSuits?.Where(s => clothing[s.SuiteId ?? string.Empty]
?.Properties?.Side?
.Contains(pmcData?.Info?.Side ?? string.Empty) ??
false
matchingSuits = matchingSuits
?.Where(s =>
clothing[s.SuiteId ?? string.Empty]
?.Properties?.Side?.Contains(pmcData?.Info?.Side ?? string.Empty) ?? false
)
.ToList();
if (matchingSuits == null)
{
throw new Exception(_localisationService.GetText("customisation-unable_to_get_trader_suits", traderId));
throw new Exception(
_localisationService.GetText("customisation-unable_to_get_trader_suits", traderId)
);
}
return matchingSuits;
@@ -70,7 +72,8 @@ public class CustomizationController(
public ItemEventRouterResponse BuyCustomisation(
PmcData pmcData,
BuyClothingRequestData buyClothingRequest,
string sessionId)
string sessionId
)
{
var output = _eventOutputHolder.GetOutput(sessionId);
@@ -78,7 +81,10 @@ public class CustomizationController(
if (traderOffer is null)
{
_logger.Error(
_localisationService.GetText("customisation-unable_to_find_suit_by_id", buyClothingRequest.Offer)
_localisationService.GetText(
"customisation-unable_to_find_suit_by_id",
buyClothingRequest.Offer
)
);
return output;
}
@@ -90,11 +96,7 @@ public class CustomizationController(
_logger.Error(
_localisationService.GetText(
"customisation-item_already_purchased",
new
{
itemId = suitDetails?.Id,
itemName = suitDetails?.Name
}
new { itemId = suitDetails?.Id, itemName = suitDetails?.Name }
)
);
@@ -111,7 +113,7 @@ public class CustomizationController(
{
Id = suitId,
Source = CustomisationSource.UNLOCKED_IN_GAME,
Type = CustomisationType.SUITE
Type = CustomisationType.SUITE,
};
profile.CustomisationUnlocks.Add(rewardToStore);
@@ -130,7 +132,9 @@ public class CustomizationController(
var fullProfile = _profileHelper.GetFullProfile(sessionId);
// Check if clothing can be found by id
return fullProfile.CustomisationUnlocks.Exists(customisation => Equals(customisation.Id, suitId));
return fullProfile.CustomisationUnlocks.Exists(customisation =>
Equals(customisation.Id, suitId)
);
}
/// <summary>
@@ -144,7 +148,9 @@ public class CustomizationController(
var foundSuit = GetAllTraderSuits(sessionId).FirstOrDefault(s => s.Id == offerId);
if (foundSuit is null)
{
_logger.Error(_localisationService.GetText("customisation-unable_to_find_suit_with_id", offerId));
_logger.Error(
_localisationService.GetText("customisation-unable_to_find_suit_with_id", offerId)
);
}
return foundSuit;
@@ -157,9 +163,12 @@ public class CustomizationController(
/// <param name="pmcData">Player profile</param>
/// <param name="itemsToPayForClothingWith">Clothing purchased</param>
/// <param name="output">Client response</param>
protected void PayForClothingItems(string sessionId, PmcData pmcData,
protected void PayForClothingItems(
string sessionId,
PmcData pmcData,
List<PaymentItemForClothing>? itemsToPayForClothingWith,
ItemEventRouterResponse output)
ItemEventRouterResponse output
)
{
if (itemsToPayForClothingWith is null || itemsToPayForClothingWith.Count == 0)
{
@@ -175,15 +184,15 @@ public class CustomizationController(
new IdWithCount
{
Count = inventoryItemToProcess.Count.Value,
Id = inventoryItemToProcess.Id
}
Id = inventoryItemToProcess.Id,
},
],
TransactionId = Traders.RAGMAN,
Action = "BuyCustomization",
Type = "",
ItemId = "",
Count = 0,
SchemeId = 0
SchemeId = 0,
};
_paymentService.PayMoney(pmcData, options, sessionId, output);
@@ -202,7 +211,10 @@ public class CustomizationController(
foreach (var trader in traders)
{
if (trader.Value.Base?.CustomizationSeller is not null && trader.Value.Base.CustomizationSeller.Value)
if (
trader.Value.Base?.CustomizationSeller is not null
&& trader.Value.Base.CustomizationSeller.Value
)
{
result.AddRange(GetTraderSuits(trader.Key, sessionId));
}
@@ -226,10 +238,11 @@ public class CustomizationController(
/// </summary>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public List<CustomisationStorage> GetCustomisationStorage(
string sessionId)
public List<CustomisationStorage> GetCustomisationStorage(string sessionId)
{
var customisationResultsClone = _cloner.Clone(_databaseService.GetTemplates().CustomisationStorage);
var customisationResultsClone = _cloner.Clone(
_databaseService.GetTemplates().CustomisationStorage
);
var profile = _profileHelper.GetFullProfile(sessionId);
if (profile is null)
@@ -249,7 +262,11 @@ public class CustomizationController(
/// <param name="request"></param>
/// <param name="pmcData">Players PMC profile</param>
/// <returns></returns>
public ItemEventRouterResponse SetCustomisation(string sessionId, CustomizationSetRequest request, PmcData pmcData)
public ItemEventRouterResponse SetCustomisation(
string sessionId,
CustomizationSetRequest request,
PmcData pmcData
)
{
foreach (var customisation in request.Customizations)
{
@@ -37,13 +37,17 @@ public class DialogueController(
{
if (_dialogueChatBots.Any(cb => cb.GetChatBot().Id == chatBot.GetChatBot().Id))
{
_logger.Error(_localisationService.GetText("dialog-chatbot_id_already_exists", chatBot.GetChatBot().Id));
_logger.Error(
_localisationService.GetText(
"dialog-chatbot_id_already_exists",
chatBot.GetChatBot().Id
)
);
}
_dialogueChatBots.Add(chatBot);
}
/// <summary>
/// Handle onUpdate spt event
/// </summary>
@@ -80,7 +84,7 @@ public class DialogueController(
{
Id = friendProfile.Id,
Aid = friendProfile.Aid,
Info = friendProfile.Info
Info = friendProfile.Info,
}
);
}
@@ -91,7 +95,7 @@ public class DialogueController(
{
Friends = friends,
Ignore = [],
InIgnoreList = []
InIgnoreList = [],
};
}
@@ -144,9 +148,7 @@ public class DialogueController(
/// <param name="dialogueId">Dialog id</param>
/// <param name="sessionId">Session Id</param>
/// <returns>DialogueInfo</returns>
public virtual DialogueInfo? GetDialogueInfo(
string? dialogueId,
string sessionId)
public virtual DialogueInfo? GetDialogueInfo(string? dialogueId, string sessionId)
{
var dialogs = _dialogueHelper.GetDialogsForProfile(sessionId);
var dialogue = dialogs!.GetValueOrDefault(dialogueId);
@@ -164,7 +166,7 @@ public class DialogueController(
New = dialogue?.New,
AttachmentsNew = dialogue?.AttachmentsNew,
Pinned = dialogue?.Pinned,
Users = GetDialogueUsers(dialogue, dialogue?.Type, sessionId)
Users = GetDialogueUsers(dialogue, dialogue?.Type, sessionId),
};
return result;
@@ -180,14 +182,19 @@ public class DialogueController(
public virtual List<UserDialogInfo> GetDialogueUsers(
Dialogue? dialog,
MessageType? messageType,
string sessionId)
string sessionId
)
{
var profile = _saveServer.GetProfile(sessionId);
// User to user messages are special in that they need the player to exist in them, add if they don't
if (messageType == MessageType.UserMessage &&
dialog?.Users is not null &&
dialog.Users.All(userDialog => userDialog.Id != profile.CharacterData?.PmcData?.SessionId))
if (
messageType == MessageType.UserMessage
&& dialog?.Users is not null
&& dialog.Users.All(userDialog =>
userDialog.Id != profile.CharacterData?.PmcData?.SessionId
)
)
{
dialog.Users.Add(
new UserDialogInfo
@@ -200,8 +207,12 @@ public class DialogueController(
Nickname = profile.CharacterData?.PmcData?.Info?.Nickname,
Side = profile.CharacterData?.PmcData?.Info?.Side,
MemberCategory = profile.CharacterData?.PmcData?.Info?.MemberCategory,
SelectedMemberCategory = profile.CharacterData?.PmcData?.Info?.SelectedMemberCategory
}
SelectedMemberCategory = profile
.CharacterData
?.PmcData
?.Info
?.SelectedMemberCategory,
},
}
);
}
@@ -220,7 +231,8 @@ public class DialogueController(
/// <returns>GetMailDialogViewResponseData object</returns>
public virtual GetMailDialogViewResponseData GenerateDialogueView(
GetMailDialogViewRequestData request,
string sessionId)
string sessionId
)
{
var dialogueId = request.DialogId;
var fullProfile = _saveServer.GetProfile(sessionId);
@@ -232,7 +244,7 @@ public class DialogueController(
{
Messages = [],
Profiles = [],
HasMessagesWithRewards = false
HasMessagesWithRewards = false,
};
}
@@ -246,7 +258,7 @@ public class DialogueController(
{
Messages = dialogue.Messages,
Profiles = GetProfilesForMail(fullProfile, dialogue.Users),
HasMessagesWithRewards = MessagesHaveUncollectedRewards(dialogue.Messages!)
HasMessagesWithRewards = MessagesHaveUncollectedRewards(dialogue.Messages!),
};
}
@@ -258,11 +270,16 @@ public class DialogueController(
/// <returns>Dialogue</returns>
protected Dialogue GetDialogByIdFromProfile(
SptProfile profile,
GetMailDialogViewRequestData request)
GetMailDialogViewRequestData request
)
{
if (profile.DialogueRecords is null || profile.DialogueRecords.ContainsKey(request.DialogId!))
if (
profile.DialogueRecords is null
|| profile.DialogueRecords.ContainsKey(request.DialogId!)
)
{
return profile.DialogueRecords?[request.DialogId!] ?? throw new NullReferenceException();
return profile.DialogueRecords?[request.DialogId!]
?? throw new NullReferenceException();
}
profile.DialogueRecords[request.DialogId!] = new Dialogue
@@ -272,7 +289,7 @@ public class DialogueController(
Pinned = false,
Messages = [],
New = 0,
Type = request.Type
Type = request.Type,
};
if (request.Type != MessageType.UserMessage)
@@ -282,7 +299,9 @@ public class DialogueController(
var dialogue = profile.DialogueRecords[request.DialogId!];
dialogue.Users = [];
var chatBot = _dialogueChatBots.FirstOrDefault(cb => cb.GetChatBot().Id == request.DialogId);
var chatBot = _dialogueChatBots.FirstOrDefault(cb =>
cb.GetChatBot().Id == request.DialogId
);
if (chatBot is null)
{
@@ -301,11 +320,14 @@ public class DialogueController(
/// <param name="fullProfile">Player profile</param>
/// <param name="userDialogs">The participants of the mail</param>
/// <returns>UserDialogInfo list</returns>
protected List<UserDialogInfo> GetProfilesForMail(SptProfile fullProfile, List<UserDialogInfo>? userDialogs)
protected List<UserDialogInfo> GetProfilesForMail(
SptProfile fullProfile,
List<UserDialogInfo>? userDialogs
)
{
List<UserDialogInfo> result = [];
if (userDialogs is null)
// Nothing to add
// Nothing to add
{
return result;
}
@@ -330,8 +352,8 @@ public class DialogueController(
Side = pmcProfile?.Info?.Side,
Level = pmcProfile?.Info?.Level,
MemberCategory = pmcProfile?.Info?.MemberCategory,
SelectedMemberCategory = pmcProfile?.Info?.SelectedMemberCategory
}
SelectedMemberCategory = pmcProfile?.Info?.SelectedMemberCategory,
},
}
);
@@ -344,15 +366,16 @@ public class DialogueController(
/// <param name="sessionId">Session id</param>
/// <param name="dialogueId">Dialog id</param>
/// <returns>Count of messages with attachments</returns>
protected int GetUnreadMessagesWithAttachmentsCount(
string sessionId,
string dialogueId)
protected int GetUnreadMessagesWithAttachmentsCount(string sessionId, string dialogueId)
{
var newAttachmentCount = 0;
var activeMessages = GetActiveMessagesFromDialog(sessionId, dialogueId);
foreach (var message in activeMessages)
{
if (message.HasRewards.GetValueOrDefault(false) && !message.RewardCollected.GetValueOrDefault(false))
if (
message.HasRewards.GetValueOrDefault(false)
&& !message.RewardCollected.GetValueOrDefault(false)
)
{
newAttachmentCount++;
}
@@ -372,11 +395,13 @@ public class DialogueController(
var timeNow = _timeUtil.GetTimeStamp();
var dialogs = _dialogueHelper.GetDialogsForProfile(sessionId);
return dialogs[dialogueId].Messages?.Where(message =>
{
var checkTime = message.DateTime + (message.MaxStorageTime ?? 0);
return timeNow < checkTime;
}).ToList() ?? [];
return dialogs[dialogueId]
.Messages?.Where(message =>
{
var checkTime = message.DateTime + (message.MaxStorageTime ?? 0);
return timeNow < checkTime;
})
.ToList() ?? [];
}
/// <summary>
@@ -395,9 +420,7 @@ public class DialogueController(
/// </summary>
/// <param name="dialogueId">id of the dialog to remove</param>
/// <param name="sessionId">Player id</param>
public virtual void RemoveDialogue(
string? dialogueId,
string sessionId)
public virtual void RemoveDialogue(string? dialogueId, string sessionId)
{
var profile = _saveServer.GetProfile(sessionId);
if (!profile.DialogueRecords.ContainsKey(dialogueId))
@@ -405,11 +428,7 @@ public class DialogueController(
_logger.Error(
_localisationService.GetText(
"dialogue-unable_to_find_in_profile",
new
{
sessionId,
dialogueId
}
new { sessionId, dialogueId }
)
);
@@ -433,11 +452,7 @@ public class DialogueController(
_logger.Error(
_localisationService.GetText(
"dialogue-unable_to_find_in_profile",
new
{
sessionId,
dialogueId
}
new { sessionId, dialogueId }
)
);
@@ -461,10 +476,7 @@ public class DialogueController(
_logger.Error(
_localisationService.GetText(
"dialogue-unable_to_find_dialogs_in_profile",
new
{
sessionId
}
new { sessionId }
)
);
@@ -491,9 +503,7 @@ public class DialogueController(
var dialog = dialogs.TryGetValue(dialogueId, out var dialogInfo);
if (!dialog)
{
_logger.Error(
_localisationService.GetText("dialogue-unable_to_find_in_profile")
);
_logger.Error(_localisationService.GetText("dialogue-unable_to_find_in_profile"));
return null;
}
@@ -508,7 +518,7 @@ public class DialogueController(
{
Messages = messagesWithAttachments,
Profiles = [],
HasMessagesWithRewards = MessagesHaveUncollectedRewards(messagesWithAttachments)
HasMessagesWithRewards = MessagesHaveUncollectedRewards(messagesWithAttachments),
};
}
@@ -518,18 +528,15 @@ public class DialogueController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="request"></param>
/// <returns></returns>
public virtual string SendMessage(
string sessionId,
SendMessageRequest request)
public virtual string SendMessage(string sessionId, SendMessageRequest request)
{
_mailSendService.SendPlayerMessageToNpc(sessionId, request.DialogId!, request.Text!);
return (_dialogueChatBots.FirstOrDefault(cb =>
cb.GetChatBot().Id == request.DialogId
)
?.HandleMessage(sessionId, request) ??
request.DialogId) ??
string.Empty;
return (
_dialogueChatBots
.FirstOrDefault(cb => cb.GetChatBot().Id == request.DialogId)
?.HandleMessage(sessionId, request) ?? request.DialogId
) ?? string.Empty;
}
/// <summary>
@@ -592,7 +599,10 @@ public class DialogueController(
/// <param name="sessionID">Session/player id</param>
/// <param name="request">Sent friend request</param>
/// <returns></returns>
public virtual FriendRequestSendResponse SendFriendRequest(string sessionID, FriendRequestData request)
public virtual FriendRequestSendResponse SendFriendRequest(
string sessionID,
FriendRequestData request
)
{
// To avoid needing to jump between profiles, auto-accept all friend requests
var friendProfile = _profileHelper.GetFullProfile(request.To);
@@ -602,7 +612,7 @@ public class DialogueController(
{
Status = BackendErrorCodes.PlayerProfileNotFound,
RequestId = "", // Unused in an error state
RetryAfter = 600
RetryAfter = 600,
};
}
@@ -620,7 +630,9 @@ public class DialogueController(
var notification = new WsFriendsListAccept
{
EventType = NotificationEventType.friendListRequestAccept,
Profile = _profileHelper.GetChatRoomMemberFromPmcProfile(friendProfile.CharacterData.PmcData)
Profile = _profileHelper.GetChatRoomMemberFromPmcProfile(
friendProfile.CharacterData.PmcData
),
};
_notificationSendHelper.SendMessage(sessionID, notification);
},
@@ -633,7 +645,7 @@ public class DialogueController(
{
Status = BackendErrorCodes.None,
RequestId = friendProfile.ProfileInfo.Aid.ToString(),
RetryAfter = 600
RetryAfter = 600,
};
}
@@ -662,7 +674,9 @@ public class DialogueController(
var profile = _saveServer.GetProfile(sessionId);
if (!profile.DialogueRecords.TryGetValue(request.DialogId, out var dialogToClear))
{
_logger.Warning($"unable to clear messages from dialog: {request.DialogId} as it cannot be found in profile: {sessionId}");
_logger.Warning(
$"unable to clear messages from dialog: {request.DialogId} as it cannot be found in profile: {sessionId}"
);
return;
}
@@ -15,7 +15,6 @@ using SPTarkov.Server.Core.Utils.Cloners;
using SPTarkov.Server.Core.Utils.Json;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
@@ -81,7 +80,7 @@ public class GameController(
fullProfile.SptData ??= new Spt
{
//TODO: complete
Version = "Replace_me"
Version = "Replace_me",
};
fullProfile.SptData.Migrations ??= new Dictionary<string, long>();
fullProfile.FriendProfileIds ??= [];
@@ -91,8 +90,14 @@ public class GameController(
return;
}
fullProfile.CharacterData!.PmcData!.WishList ??= new DictionaryOrList<string, int>(new Dictionary<string, int>(), []);
fullProfile.CharacterData.ScavData!.WishList ??= new DictionaryOrList<string, int>(new Dictionary<string, int>(), []);
fullProfile.CharacterData!.PmcData!.WishList ??= new DictionaryOrList<string, int>(
new Dictionary<string, int>(),
[]
);
fullProfile.CharacterData.ScavData!.WishList ??= new DictionaryOrList<string, int>(
new Dictionary<string, int>(),
[]
);
if (fullProfile.DialogueRecords is not null)
{
@@ -101,7 +106,9 @@ public class GameController(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Started game with session {sessionId} {fullProfile.ProfileInfo?.Username}");
_logger.Debug(
$"Started game with session {sessionId} {fullProfile.ProfileInfo?.Username}"
);
}
var pmcProfile = fullProfile.CharacterData.PmcData;
@@ -133,7 +140,12 @@ public class GameController(
_hideoutHelper.UnlockHideoutWallInProfile(pmcProfile);
// Handle if player has been inactive for a long time, catch up on hideout update before the user goes to his hideout
if (!_profileActivityService.ActiveWithinLastMinutes(sessionId, _hideoutConfig.UpdateProfileHideoutWhenActiveWithinMinutes))
if (
!_profileActivityService.ActiveWithinLastMinutes(
sessionId,
_hideoutConfig.UpdateProfileHideoutWhenActiveWithinMinutes
)
)
{
_hideoutHelper.UpdatePlayerHideout(sessionId);
}
@@ -167,10 +179,12 @@ public class GameController(
public GameConfigResponse GetGameConfig(string sessionId)
{
var profile = _profileHelper.GetPmcProfile(sessionId);
var gameTime = profile?.Stats?.Eft?.OverallCounters?.Items?
.FirstOrDefault(c => c.Key!.Contains("LifeTime") && c.Key.Contains("Pmc"))
?.Value ??
0D;
var gameTime =
profile
?.Stats?.Eft?.OverallCounters?.Items?.FirstOrDefault(c =>
c.Key!.Contains("LifeTime") && c.Key.Contains("Pmc")
)
?.Value ?? 0D;
var config = new GameConfigResponse
{
@@ -188,18 +202,14 @@ public class GameController(
Trading = _httpServerHelper.GetBackendUrl(),
Messaging = _httpServerHelper.GetBackendUrl(),
Main = _httpServerHelper.GetBackendUrl(),
RagFair = _httpServerHelper.GetBackendUrl()
RagFair = _httpServerHelper.GetBackendUrl(),
},
UseProtobuf = false,
UtcTime = _timeUtil.GetTimeStamp(),
TotalInGame = gameTime,
SessionMode = "pve",
PurchasedGames = new PurchasedGames
{
IsEftPurchased = true,
IsArenaPurchased = false
},
IsGameSynced = true
PurchasedGames = new PurchasedGames { IsEftPurchased = true, IsArenaPurchased = false },
IsGameSynced = true,
};
return config;
@@ -211,14 +221,12 @@ public class GameController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="requestData"></param>
/// <returns></returns>
public GameModeResponse GetGameMode(
string sessionId,
GameModeRequestData requestData)
public GameModeResponse GetGameMode(string sessionId, GameModeRequestData requestData)
{
return new GameModeResponse
{
GameMode = "pve",
BackendUrl = _httpServerHelper.GetBackendUrl()
BackendUrl = _httpServerHelper.GetBackendUrl(),
};
}
@@ -229,14 +237,7 @@ public class GameController(
/// <returns></returns>
public List<ServerDetails> GetServer(string sessionId)
{
return
[
new ServerDetails
{
Ip = _httpConfig.BackendIp,
Port = _httpConfig.BackendPort
}
];
return [new ServerDetails { Ip = _httpConfig.BackendIp, Port = _httpConfig.BackendPort }];
}
/// <summary>
@@ -246,13 +247,9 @@ public class GameController(
/// <returns></returns>
public CurrentGroupResponse GetCurrentGroup(string sessionId)
{
return new CurrentGroupResponse
{
Squad = []
};
return new CurrentGroupResponse { Squad = [] };
}
/// <summary>
/// Handle client/checkVersion
/// </summary>
@@ -263,7 +260,7 @@ public class GameController(
return new CheckVersionResponse
{
IsValid = true,
LatestVersion = _coreConfig.CompatibleTarkovVersion
LatestVersion = _coreConfig.CompatibleTarkovVersion,
};
}
@@ -275,11 +272,7 @@ public class GameController(
public GameKeepAliveResponse GetKeepAlive(string sessionId)
{
_profileActivityService.SetActivityTimestamp(sessionId);
return new GameKeepAliveResponse
{
Message = "OK",
UtcTime = _timeUtil.GetTimeStamp()
};
return new GameKeepAliveResponse { Message = "OK", UtcTime = _timeUtil.GetTimeStamp() };
}
/// <summary>
@@ -311,7 +304,9 @@ public class GameController(
var botReloadSkill = _profileHelper.GetSkillFromProfile(pmcProfile, SkillTypes.BotReload);
if (botReloadSkill?.Progress > 0)
{
_logger.Warning(_localisationService.GetText("server_start_player_active_botreload_skill"));
_logger.Warning(
_localisationService.GetText("server_start_player_active_botreload_skill")
);
}
}
@@ -337,23 +332,25 @@ public class GameController(
var hpRegenPerHour = 456.6;
// Set new values, whatever is smallest
energyRegenPerHour += pmcProfile.Bonuses!
.Where(bonus => bonus.Type == BonusType.EnergyRegeneration)
energyRegenPerHour += pmcProfile
.Bonuses!.Where(bonus => bonus.Type == BonusType.EnergyRegeneration)
.Aggregate(0d, (sum, bonus) => sum + bonus.Value!.Value);
hydrationRegenPerHour += pmcProfile.Bonuses!
.Where(bonus => bonus.Type == BonusType.HydrationRegeneration)
hydrationRegenPerHour += pmcProfile
.Bonuses!.Where(bonus => bonus.Type == BonusType.HydrationRegeneration)
.Aggregate(0d, (sum, bonus) => sum + bonus.Value!.Value);
hpRegenPerHour += pmcProfile.Bonuses!
.Where(bonus => bonus.Type == BonusType.HealthRegeneration)
hpRegenPerHour += pmcProfile
.Bonuses!.Where(bonus => bonus.Type == BonusType.HealthRegeneration)
.Aggregate(0d, (sum, bonus) => sum + bonus.Value!.Value);
// Player has energy deficit
if (pmcProfile.Health?.Energy?.Current - pmcProfile.Health?.Energy?.Maximum <= _deviation)
{
// Set new value, whatever is smallest
pmcProfile.Health!.Energy!.Current += Math.Round(energyRegenPerHour * (diffSeconds!.Value / 3600));
pmcProfile.Health!.Energy!.Current += Math.Round(
energyRegenPerHour * (diffSeconds!.Value / 3600)
);
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum)
{
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
@@ -361,9 +358,14 @@ public class GameController(
}
// Player has hydration deficit
if (pmcProfile.Health?.Hydration?.Current - pmcProfile.Health?.Hydration?.Maximum <= _deviation)
if (
pmcProfile.Health?.Hydration?.Current - pmcProfile.Health?.Hydration?.Maximum
<= _deviation
)
{
pmcProfile.Health!.Hydration!.Current += Math.Round(hydrationRegenPerHour * (diffSeconds!.Value / 3600));
pmcProfile.Health!.Hydration!.Current += Math.Round(
hydrationRegenPerHour * (diffSeconds!.Value / 3600)
);
if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum)
{
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
@@ -383,10 +385,15 @@ public class GameController(
/// <param name="pmcProfile">Player</param>
/// <param name="hpRegenPerHour"></param>
/// <param name="diffSeconds"></param>
protected void DecreaseBodyPartEffectTimes(PmcData pmcProfile, double hpRegenPerHour, double diffSeconds)
protected void DecreaseBodyPartEffectTimes(
PmcData pmcProfile,
double hpRegenPerHour,
double diffSeconds
)
{
foreach (var bodyPart in pmcProfile.Health!.BodyParts!
.Select(bodyPartKvP => bodyPartKvP.Value))
foreach (
var bodyPart in pmcProfile.Health!.BodyParts!.Select(bodyPartKvP => bodyPartKvP.Value)
)
{
// Check part hp
if (bodyPart.Health!.Current < bodyPart.Health.Maximum)
@@ -399,7 +406,6 @@ public class GameController(
bodyPart.Health.Current = bodyPart.Health.Maximum;
}
if (bodyPart.Effects is null || bodyPart.Effects.Count == 0)
{
continue;
@@ -423,7 +429,7 @@ public class GameController(
// Decrement effect time value by difference between current time and time health was last updated
effectKvP.Value.Time -= diffSeconds;
if (effectKvP.Value.Time < 1)
// Effect time was sub 1, set floor it can be
// Effect time was sub 1, set floor it can be
{
effectKvP.Value.Time = 1;
}
@@ -475,7 +481,9 @@ public class GameController(
{
if (
fullProfile.SptData.Mods.Any(m =>
m.Author == mod.ModMetadata.Author && m.Version == mod.ModMetadata.Version && m.Name == mod.ModMetadata.Name
m.Author == mod.ModMetadata.Author
&& m.Version == mod.ModMetadata.Version
&& m.Name == mod.ModMetadata.Name
)
)
{
@@ -490,7 +498,7 @@ public class GameController(
Version = mod.ModMetadata.Version,
Name = mod.ModMetadata.Name,
Url = mod.ModMetadata.Url,
DateAdded = _timeUtil.GetTimeStamp()
DateAdded = _timeUtil.GetTimeStamp(),
}
);
}
@@ -552,7 +560,9 @@ public class GameController(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Profile made with: {fullProfile.SptData?.Version}");
_logger.Debug($"Server version: {ProgramStatics.SPT_VERSION() ?? _coreConfig.SptVersion} {ProgramStatics.COMMIT()}");
_logger.Debug(
$"Server version: {ProgramStatics.SPT_VERSION() ?? _coreConfig.SptVersion} {ProgramStatics.COMMIT()}"
);
_logger.Debug($"Debug enabled: {ProgramStatics.DEBUG()}");
_logger.Debug($"Mods enabled: {ProgramStatics.MODS()}");
}
@@ -25,7 +25,8 @@ public class HealthController(
LocalisationService _localisationService,
HttpResponseUtil _httpResponseUtil,
HealthHelper _healthHelper,
ICloner _cloner)
ICloner _cloner
)
{
/// <summary>
/// When healing in menu
@@ -37,15 +38,21 @@ public class HealthController(
public ItemEventRouterResponse OffRaidHeal(
PmcData pmcData,
OffraidHealRequestData request,
string sessionID)
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
// Update medkit used (hpresource)
var healingItemToUse = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
var healingItemToUse = pmcData.Inventory.Items.FirstOrDefault(item =>
item.Id == request.Item
);
if (healingItemToUse is null)
{
var errorMessage = _localisationService.GetText("health-healing_item_not_found", request.Item);
var errorMessage = _localisationService.GetText(
"health-healing_item_not_found",
request.Item
);
_logger.Error(errorMessage);
return _httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -61,11 +68,10 @@ public class HealthController(
else
{
// Get max healing from db
var maxHp = _itemHelper.GetItem(healingItemToUse.Template).Value.Properties.MaxHpResource;
healingItemToUse.Upd.MedKit = new UpdMedKit
{
HpResource = maxHp - request.Count
}; // Subtract amout used from max
var maxHp = _itemHelper
.GetItem(healingItemToUse.Template)
.Value.Properties.MaxHpResource;
healingItemToUse.Upd.MedKit = new UpdMedKit { HpResource = maxHp - request.Count }; // Subtract amout used from max
// request.count appears to take into account healing effects removed, e.g. bleeds
// Salewa heals limb for 20 and fixes light bleed = (20+45 = 65)
}
@@ -82,7 +88,9 @@ public class HealthController(
var bodyPartToHeal = pmcData.Health.BodyParts.GetValueOrDefault(request.Part);
if (bodyPartToHeal is null)
{
_logger.Warning($"Player: {sessionID} Tried to heal a non-existent body part: {request.Part}");
_logger.Warning(
$"Player: {sessionID} Tried to heal a non-existent body part: {request.Part}"
);
return output;
}
@@ -99,20 +107,25 @@ public class HealthController(
{
// Check enum has effectType
if (!Enum.TryParse<DamageEffectType>(effectKvP.Key, out var effect))
// Enum doesnt contain this key
// Enum doesnt contain this key
{
continue;
}
// Check if healing item removes the effect on limb
if (!healItemEffectDetails.TryGetValue(effect, out var matchingEffectFromHealingItem))
// Healing item doesn't have matching effect, it doesn't remove the effect
if (
!healItemEffectDetails.TryGetValue(
effect,
out var matchingEffectFromHealingItem
)
)
// Healing item doesn't have matching effect, it doesn't remove the effect
{
continue;
}
// Adjust limb heal amount based on if it's fixing an effect (request.count is TOTAL cost of hp resource on heal item, NOT amount to heal limb)
amountToHealLimb -= (int) (matchingEffectFromHealingItem.Cost ?? 0);
amountToHealLimb -= (int)(matchingEffectFromHealingItem.Cost ?? 0);
bodyPartToHeal.Effects.Remove(effectKvP.Key);
}
}
@@ -140,14 +153,15 @@ public class HealthController(
public ItemEventRouterResponse OffRaidEat(
PmcData pmcData,
OffraidEatRequestData request,
string sessionID)
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
var resourceLeft = 0d;
var itemToConsume = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
if (itemToConsume is null)
// Item not found, very bad
// Item not found, very bad
{
return _httpResponseUtil.AppendErrorToOutput(
output,
@@ -155,7 +169,9 @@ public class HealthController(
);
}
var consumedItemMaxResource = _itemHelper.GetItem(itemToConsume.Template).Value.Properties.MaxResource;
var consumedItemMaxResource = _itemHelper
.GetItem(itemToConsume.Template)
.Value.Properties.MaxResource;
if (consumedItemMaxResource > 1)
{
// Ensure item has a upd object
@@ -165,7 +181,7 @@ public class HealthController(
{
itemToConsume.Upd.FoodDrink = new UpdFoodDrink
{
HpPercent = consumedItemMaxResource - request.Count
HpPercent = consumedItemMaxResource - request.Count,
};
}
else
@@ -192,14 +208,21 @@ public class HealthController(
switch (key)
{
case HealthFactor.Hydration:
ApplyEdibleEffect(pmcData.Health.Hydration, effectProps, foodIsSingleUse, request);
ApplyEdibleEffect(
pmcData.Health.Hydration,
effectProps,
foodIsSingleUse,
request
);
break;
case HealthFactor.Energy:
ApplyEdibleEffect(pmcData.Health.Energy, effectProps, foodIsSingleUse, request);
break;
default:
_logger.Warning($"Unhandled effect after consuming: {itemToConsume.Template}, {key}");
_logger.Warning(
$"Unhandled effect after consuming: {itemToConsume.Template}, {key}"
);
break;
}
}
@@ -214,11 +237,15 @@ public class HealthController(
/// <param name="consumptionDetails">Properties of consumed item</param>
/// <param name="foodIsSingleUse">Single use item</param>
/// <param name="request">Client request</param>
protected void ApplyEdibleEffect(CurrentMinMax bodyValue, EffectsHealthProps consumptionDetails, bool foodIsSingleUse,
OffraidEatRequestData request)
protected void ApplyEdibleEffect(
CurrentMinMax bodyValue,
EffectsHealthProps consumptionDetails,
bool foodIsSingleUse,
OffraidEatRequestData request
)
{
if (foodIsSingleUse)
// Apply whole value from passed in parameter
// Apply whole value from passed in parameter
{
bodyValue.Current += consumptionDetails.Value;
}
@@ -253,7 +280,8 @@ public class HealthController(
public ItemEventRouterResponse HealthTreatment(
PmcData pmcData,
HealthTreatmentRequestData healthTreatmentRequest,
string sessionID)
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
var payMoneyRequest = new ProcessBuyTradeRequestData
@@ -264,7 +292,7 @@ public class HealthController(
Type = "",
ItemId = "",
Count = 0,
SchemeId = 0
SchemeId = 0,
};
_paymentService.PayMoney(pmcData, payMoneyRequest, sessionID, output);
@@ -276,12 +304,12 @@ public class HealthController(
foreach (var bodyPartKvP in healthTreatmentRequest.Difference.BodyParts.GetAllPropsAsDict())
{
// Get body part from request + from pmc profile
var partRequest = (BodyPartEffects) bodyPartKvP.Value;
var partRequest = (BodyPartEffects)bodyPartKvP.Value;
var profilePart = pmcData.Health.BodyParts[bodyPartKvP.Key];
// Bodypart healing is chosen when part request hp is above 0
if (partRequest.Health > 0)
// Heal bodypart
// Heal bodypart
{
profilePart.Health.Current = profilePart.Health.Maximum;
}
@@ -315,10 +343,7 @@ public class HealthController(
/// <param name="pmcData">Player profile</param>
/// <param name="request">Request data</param>
/// <param name="sessionId">session id</param>
public void ApplyWorkoutChanges(
PmcData? pmcData,
WorkoutData request,
string sessionId)
public void ApplyWorkoutChanges(PmcData? pmcData, WorkoutData request, string sessionId)
{
pmcData.Skills.Common = request.Skills.Common;
}
File diff suppressed because it is too large Load Diff
@@ -25,7 +25,7 @@ public class InRaidController(
/// <param name="info">Register player request</param>
public void AddPlayer(string sessionId, RegisterPlayerRequestData info)
{
// _applicationContext.AddValue(ContextVariableType.REGISTER_PLAYER_REQUEST, info);
// _applicationContext.AddValue(ContextVariableType.REGISTER_PLAYER_REQUEST, info);
}
/// <summary>
@@ -91,11 +91,15 @@ public class InsuranceController(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Found {profileInsuranceDetails.Count} insurance packages in profile {sessionId}");
_logger.Debug(
$"Found {profileInsuranceDetails.Count} insurance packages in profile {sessionId}"
);
}
}
return profileInsuranceDetails.Where(insured => insuranceTime >= insured.ScheduledTime).ToList();
return profileInsuranceDetails
.Where(insured => insuranceTime >= insured.ScheduledTime)
.ToList();
}
/// <summary>
@@ -159,17 +163,20 @@ public class InsuranceController(
protected void RemoveInsurancePackageFromProfile(string sessionId, Insurance insPackage)
{
var profile = _saveServer.GetProfile(sessionId);
profile.InsuranceList = profile.InsuranceList.Where(insurance =>
insurance.TraderId != insPackage.TraderId ||
insurance.SystemData?.Date != insPackage.SystemData?.Date ||
insurance.SystemData?.Time != insPackage.SystemData?.Time ||
insurance.SystemData?.Location != insPackage.SystemData?.Location
profile.InsuranceList = profile
.InsuranceList.Where(insurance =>
insurance.TraderId != insPackage.TraderId
|| insurance.SystemData?.Date != insPackage.SystemData?.Date
|| insurance.SystemData?.Time != insPackage.SystemData?.Time
|| insurance.SystemData?.Location != insPackage.SystemData?.Location
)
.ToList();
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Removed processed insurance package. Remaining packages: {profile.InsuranceList.Count}");
_logger.Debug(
$"Removed processed insurance package. Remaining packages: {profile.InsuranceList.Count}"
);
}
}
@@ -186,11 +193,14 @@ public class InsuranceController(
// Populate a Map object of items for quick lookup by their ID and use it to populate a Map of main-parent items
// and each of their attachments. For example, a gun mapped to each of its attachments.
var itemsMap = _itemHelper.GenerateItemsMap(insured.Items);
var parentAttachmentsMap = PopulateParentAttachmentsMap(rootItemParentId, insured, itemsMap);
var parentAttachmentsMap = PopulateParentAttachmentsMap(
rootItemParentId,
insured,
itemsMap
);
// Check to see if any regular items are present.
var hasRegularItems = itemsMap.Values.Any(item => !_itemHelper.IsAttachmentAttached(item)
);
var hasRegularItems = itemsMap.Values.Any(item => !_itemHelper.IsAttachmentAttached(item));
// Process all items that are not attached, attachments; those are handled separately, by value.
if (hasRegularItems)
@@ -229,7 +239,11 @@ public class InsuranceController(
/// <param name="insured">The insurance object containing the items to evaluate</param>
/// <param name="itemsMap">A Dictionary for quick item look-up by item ID</param>
/// <returns>A dictionary containing parent item IDs to arrays of their attachment items</returns>
protected Dictionary<string, List<Item>> PopulateParentAttachmentsMap(string rootItemParentID, Insurance insured, Dictionary<string, Item> itemsMap)
protected Dictionary<string, List<Item>> PopulateParentAttachmentsMap(
string rootItemParentID,
Insurance insured,
Dictionary<string, Item> itemsMap
)
{
var mainParentToAttachmentsMap = new Dictionary<string, List<Item>>();
foreach (var insuredItem in insured.Items)
@@ -247,7 +261,7 @@ public class InsuranceController(
{
insuredItemId = insuredItem.Id,
insuredItemTpl = insuredItem.Template,
parentId = insuredItem.ParentId
parentId = insuredItem.ParentId,
}
)
);
@@ -270,7 +284,7 @@ public class InsuranceController(
new
{
insuredItemId = insuredItem.Id,
insuredItemTpl = insuredItem.Template
insuredItemTpl = insuredItem.Template,
}
)
);
@@ -290,7 +304,7 @@ public class InsuranceController(
{
insuredItemId = insuredItem.Id,
insuredItemTpl = insuredItem.Template,
parentId = insuredItem.ParentId
parentId = insuredItem.ParentId,
}
)
);
@@ -322,8 +336,10 @@ public class InsuranceController(
/// <param name="parentAttachmentsMap">Dictionary containing parent item IDs to arrays of their attachment items</param>
/// <param name="itemsMap">Hashset containing parent item IDs to arrays of their attachment items which are not moddable in-raid</param>
/// <returns></returns>
protected Dictionary<string, List<Item>> RemoveNonModdableAttachments(Dictionary<string, List<Item>> parentAttachmentsMap,
Dictionary<string, Item> itemsMap)
protected Dictionary<string, List<Item>> RemoveNonModdableAttachments(
Dictionary<string, List<Item>> parentAttachmentsMap,
Dictionary<string, Item> itemsMap
)
{
var updatedMap = new Dictionary<string, List<Item>>();
@@ -371,7 +387,11 @@ public class InsuranceController(
/// <param name="insured">Insurance object containing the items to evaluate</param>
/// <param name="toDelete">Hashset to keep track of items marked for deletion</param>
/// <param name="parentAttachmentsMap">Dictionary containing parent item IDs to arrays of their attachment items</param>
protected void ProcessRegularItems(Insurance insured, HashSet<string> toDelete, Dictionary<string, List<Item>> parentAttachmentsMap)
protected void ProcessRegularItems(
Insurance insured,
HashSet<string> toDelete,
Dictionary<string, List<Item>> parentAttachmentsMap
)
{
foreach (var insuredItem in insured.Items)
{
@@ -419,8 +439,12 @@ public class InsuranceController(
/// <param name="itemsMap">Dictionary for quick item look-up by item ID</param>
/// <param name="insuredTraderId">Trader ID from the Insurance object</param>
/// <param name="toDelete">Tracked attachment ids to be removed</param>
protected void ProcessAttachments(Dictionary<string, List<Item>> mainParentToAttachmentsMap, Dictionary<string, Item> itemsMap, string? insuredTraderId,
HashSet<string> toDelete)
protected void ProcessAttachments(
Dictionary<string, List<Item>> mainParentToAttachmentsMap,
Dictionary<string, Item> itemsMap,
string? insuredTraderId,
HashSet<string> toDelete
)
{
foreach (var parentObj in mainParentToAttachmentsMap)
{
@@ -444,7 +468,6 @@ public class InsuranceController(
}
}
/// <summary>
/// Takes an array of attachment items that belong to the same main-parent item, sorts them in descending order by
/// their maximum price. For each attachment, a roll is made to determine if a deletion should be made. Once the
@@ -454,16 +477,26 @@ public class InsuranceController(
/// <param name="attachments">Array of attachment items to sort, filter, and roll</param>
/// <param name="traderId">ID of the trader to that has ensured these items</param>
/// <param name="toDelete">array that accumulates the IDs of the items to be deleted</param>
protected void ProcessAttachmentByParent(List<Item> attachments, string? traderId, HashSet<string> toDelete)
protected void ProcessAttachmentByParent(
List<Item> attachments,
string? traderId,
HashSet<string> toDelete
)
{
// Create dict of item ids + their flea/handbook price (highest is chosen)
var weightedAttachmentByPrice = WeightAttachmentsByPrice(attachments);
// Get how many attachments we want to pull off parent
var countOfAttachmentsToRemove = GetAttachmentCountToRemove(weightedAttachmentByPrice, traderId);
var countOfAttachmentsToRemove = GetAttachmentCountToRemove(
weightedAttachmentByPrice,
traderId
);
// Create prob array and add all attachments with rouble price as the weight
var attachmentsProbabilityArray = new ProbabilityObjectArray<string, double?>(_mathUtil, _cloner);
var attachmentsProbabilityArray = new ProbabilityObjectArray<string, double?>(
_mathUtil,
_cloner
);
foreach (var attachmentTpl in weightedAttachmentByPrice)
{
attachmentsProbabilityArray.Add(
@@ -472,7 +505,10 @@ public class InsuranceController(
}
// Draw x attachments from weighted array to remove from parent, remove from pool after being picked
var attachmentIdsToRemove = attachmentsProbabilityArray.Draw((int) countOfAttachmentsToRemove, false);
var attachmentIdsToRemove = attachmentsProbabilityArray.Draw(
(int)countOfAttachmentsToRemove,
false
);
foreach (var attachmentId in attachmentIdsToRemove)
{
toDelete.Add(attachmentId);
@@ -492,7 +528,11 @@ public class InsuranceController(
/// <param name="attachmentIdsToRemove"></param>
/// <param name="attachments"></param>
/// <param name="attachmentPrices"></param>
protected void LogAttachmentsBeingRemoved(List<string> attachmentIdsToRemove, List<Item> attachments, Dictionary<string, double> attachmentPrices)
protected void LogAttachmentsBeingRemoved(
List<string> attachmentIdsToRemove,
List<Item> attachments,
Dictionary<string, double> attachmentPrices
)
{
var index = 1;
foreach (var attachmentId in attachmentIdsToRemove)
@@ -500,8 +540,8 @@ public class InsuranceController(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
$"Attachment {index} Id: {attachmentId} Tpl: {attachments.FirstOrDefault(x => x.Id == attachmentId)?.Template} - " +
$"Price: {attachmentPrices[attachmentId]}"
$"Attachment {index} Id: {attachmentId} Tpl: {attachments.FirstOrDefault(x => x.Id == attachmentId)?.Template} - "
+ $"Price: {attachmentPrices[attachmentId]}"
);
}
@@ -521,7 +561,10 @@ public class InsuranceController(
// Get a dictionary of item tpls + their rouble price
foreach (var attachment in attachments)
{
var price = _ragfairPriceService.GetDynamicItemPrice(attachment.Template, Money.ROUBLES);
var price = _ragfairPriceService.GetDynamicItemPrice(
attachment.Template,
Money.ROUBLES
);
if (price is not null)
{
result[attachment.Id] = Math.Round(price ?? 0);
@@ -539,7 +582,10 @@ public class InsuranceController(
/// <param name="weightedAttachmentByPrice">Dict of item Tpls and their rouble price</param>
/// <param name="traderId">Trader the attachment is insured against</param>
/// <returns>Attachment count to remove</returns>
protected double GetAttachmentCountToRemove(Dictionary<string, double> weightedAttachmentByPrice, string? traderId)
protected double GetAttachmentCountToRemove(
Dictionary<string, double> weightedAttachmentByPrice,
string? traderId
)
{
const int removeCount = 0;
@@ -550,7 +596,9 @@ public class InsuranceController(
// Get attachments count above or equal to price set in config
return weightedAttachmentByPrice
.Where(attachment => attachment.Value >= _insuranceConfig.MinAttachmentRoublePriceToBeTaken)
.Where(attachment =>
attachment.Value >= _insuranceConfig.MinAttachmentRoublePriceToBeTaken
)
.Count(_ => RollForDelete(traderId) ?? false);
}
@@ -577,8 +625,8 @@ public class InsuranceController(
// Map is labs + insurance is disabled in base.json
if (IsMapLabsAndInsuranceDisabled(insurance))
// Trader has labs-specific messages
// Wipe out returnable items
// Trader has labs-specific messages
// Wipe out returnable items
{
HandleLabsInsurance(traderDialogMessages, insurance);
}
@@ -587,9 +635,14 @@ public class InsuranceController(
HandleLabyrinthInsurance(traderDialogMessages, insurance);
}
else if (insurance.Items?.Count == 0)
// Not labs and no items to return
// Not labs and no items to return
{
if (traderDialogMessages.TryGetValue("insuranceFailed", out var insuranceFailedTemplates))
if (
traderDialogMessages.TryGetValue(
"insuranceFailed",
out var insuranceFailedTemplates
)
)
{
insurance.MessageTemplateId = _randomUtil.GetArrayValue(insuranceFailedTemplates);
}
@@ -615,8 +668,15 @@ public class InsuranceController(
/// <returns></returns>
protected bool IsMapLabsAndInsuranceDisabled(Insurance insurance, string labsId = "laboratory")
{
return string.Equals(insurance.SystemData?.Location, labsId, StringComparison.OrdinalIgnoreCase) &&
!(_databaseService.GetLocation(labsId)?.Base?.Insurance.GetValueOrDefault(false) ?? false);
return string.Equals(
insurance.SystemData?.Location,
labsId,
StringComparison.OrdinalIgnoreCase
)
&& !(
_databaseService.GetLocation(labsId)?.Base?.Insurance.GetValueOrDefault(false)
?? false
);
}
/// <summary>
@@ -625,10 +685,20 @@ public class InsuranceController(
/// <param name="insurance">The insured items to process</param>
/// <param name="labsId">OPTIONAL - id of labs location</param>
/// <returns></returns>
protected bool IsMapLabyrinthAndInsuranceDisabled(Insurance insurance, string labyrinthId = "labyrinth")
protected bool IsMapLabyrinthAndInsuranceDisabled(
Insurance insurance,
string labyrinthId = "labyrinth"
)
{
return string.Equals(insurance.SystemData?.Location, labyrinthId, StringComparison.OrdinalIgnoreCase) &&
!(_databaseService.GetLocation(labyrinthId)?.Base?.Insurance.GetValueOrDefault(false) ?? false);
return string.Equals(
insurance.SystemData?.Location,
labyrinthId,
StringComparison.OrdinalIgnoreCase
)
&& !(
_databaseService.GetLocation(labyrinthId)?.Base?.Insurance.GetValueOrDefault(false)
?? false
);
}
/// <summary>
@@ -636,7 +706,10 @@ public class InsuranceController(
/// </summary>
/// <param name="traderDialogMessages"></param>
/// <param name="insurance"></param>
protected void HandleLabsInsurance(Dictionary<string, List<string>?>? traderDialogMessages, Insurance insurance)
protected void HandleLabsInsurance(
Dictionary<string, List<string>?>? traderDialogMessages,
Insurance insurance
)
{
// Use labs specific messages if available, otherwise use default
var responseMesageIds =
@@ -655,10 +728,13 @@ public class InsuranceController(
/// </summary>
/// <param name="traderDialogMessages"></param>
/// <param name="insurance"></param>
protected void HandleLabyrinthInsurance(Dictionary<string, List<string>?>? traderDialogMessages, Insurance insurance)
protected void HandleLabyrinthInsurance(
Dictionary<string, List<string>?>? traderDialogMessages,
Insurance insurance
)
{
// Use labs specific messages if available, otherwise use default
var responseMessageIds =
var responseMessageIds =
traderDialogMessages["insuranceFailedLabyrinth"]?.Count > 0
? traderDialogMessages["insuranceFailedLabyrinth"]
: traderDialogMessages["insuranceFailed"];
@@ -669,7 +745,6 @@ public class InsuranceController(
insurance.Items = [];
}
/// <summary>
/// Roll for chance of item being 'lost'
/// </summary>
@@ -692,11 +767,15 @@ public class InsuranceController(
var roll = returnChance >= traderReturnChance;
// Log the roll with as much detail as possible.
var itemName = insuredItem is not null ? $"{_itemHelper.GetItemName(insuredItem.Template)}" : "";
var itemName = insuredItem is not null
? $"{_itemHelper.GetItemName(insuredItem.Template)}"
: "";
var status = roll ? "Delete" : "Keep";
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Rolling {itemName} with {trader} - Return {traderReturnChance}% - Roll: {returnChance} - Status: {status}");
_logger.Debug(
$"Rolling {itemName} with {trader} - Return {traderReturnChance}% - Roll: {returnChance} - Status: {status}"
);
}
return roll;
@@ -709,7 +788,11 @@ public class InsuranceController(
/// <param name="request">Insurance request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse object to send to client</returns>
public ItemEventRouterResponse Insure(PmcData pmcData, InsureRequestData request, string sessionId)
public ItemEventRouterResponse Insure(
PmcData pmcData,
InsureRequestData request,
string sessionId
)
{
var output = _eventOutputHolder.GetOutput(sessionId);
var itemsToInsureCount = request.Items.Count;
@@ -729,7 +812,7 @@ public class InsuranceController(
pmcData,
inventoryItemsHash[key],
request.TransactionId
)
),
}
);
}
@@ -742,7 +825,7 @@ public class InsuranceController(
Type = "",
ItemId = "",
Count = 0,
SchemeId = 0
SchemeId = 0,
};
// pay for the item insurance
@@ -757,20 +840,22 @@ public class InsuranceController(
foreach (var key in request.Items)
{
pmcData.InsuredItems.Add(
new InsuredItem
{
TId = request.TransactionId,
ItemId = inventoryItemsHash[key].Id
}
new InsuredItem { TId = request.TransactionId, ItemId = inventoryItemsHash[key].Id }
);
// If Item is Helmet or Body Armour -> Handle insurance of soft inserts
if (_itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(inventoryItemsHash[key].Template))
if (
_itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(inventoryItemsHash[key].Template)
)
{
InsureSoftInserts(inventoryItemsHash[key], pmcData, request);
}
}
_profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Charisma, itemsToInsureCount * 0.01);
_profileHelper.AddSkillPointsToPlayer(
pmcData,
SkillTypes.Charisma,
itemsToInsureCount * 0.01
);
return output;
}
@@ -781,9 +866,15 @@ public class InsuranceController(
/// <param name="itemWithSoftInserts">Armor item to be insured</param>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="request">Insurance request data</param>
public void InsureSoftInserts(Item itemWithSoftInserts, PmcData pmcData, InsureRequestData request)
public void InsureSoftInserts(
Item itemWithSoftInserts,
PmcData pmcData,
InsureRequestData request
)
{
var softInsertSlots = pmcData.Inventory.Items.Where(item => item.ParentId == itemWithSoftInserts.Id && _itemHelper.IsSoftInsertId(item.SlotId.ToLower())
var softInsertSlots = pmcData.Inventory.Items.Where(item =>
item.ParentId == itemWithSoftInserts.Id
&& _itemHelper.IsSoftInsertId(item.SlotId.ToLower())
);
foreach (var softInsertSlot in softInsertSlots)
@@ -794,11 +885,7 @@ public class InsuranceController(
}
pmcData.InsuredItems.Add(
new InsuredItem
{
TId = request.TransactionId,
ItemId = softInsertSlot.Id
}
new InsuredItem { TId = request.TransactionId, ItemId = softInsertSlot.Id }
);
}
}
@@ -830,7 +917,9 @@ public class InsuranceController(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Item with id: {itemId} missing from player inventory, skipping");
_logger.Debug(
$"Item with id: {itemId} missing from player inventory, skipping"
);
}
continue;
@@ -838,7 +927,11 @@ public class InsuranceController(
items.TryAdd(
inventoryItemsHash[itemId].Template,
_insuranceService.GetRoublePriceToInsureItemWithTrader(pmcData, inventoryItemsHash[itemId], trader)
_insuranceService.GetRoublePriceToInsureItemWithTrader(
pmcData,
inventoryItemsHash[itemId],
trader
)
);
}
@@ -48,8 +48,12 @@ public class InventoryController(
/// <param name="moveRequest">Move request data</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void MoveItem(PmcData pmcData, InventoryMoveRequestData moveRequest, string sessionId,
ItemEventRouterResponse output)
public void MoveItem(
PmcData pmcData,
InventoryMoveRequestData moveRequest,
string sessionId,
ItemEventRouterResponse output
)
{
if (output.Warnings?.Count > 0)
{
@@ -57,18 +61,27 @@ public class InventoryController(
}
// Changes made to result apply to character inventory
var ownerInventoryItems = _inventoryHelper.GetOwnerInventoryItems(moveRequest, moveRequest.Item, sessionId);
var ownerInventoryItems = _inventoryHelper.GetOwnerInventoryItems(
moveRequest,
moveRequest.Item,
sessionId
);
if (ownerInventoryItems.SameInventory.GetValueOrDefault(false))
{
// Don't move items from trader to profile, this can happen when editing a traders preset weapons
if (moveRequest.FromOwner?.Type == "Trader" && !ownerInventoryItems.IsMail.GetValueOrDefault(false))
if (
moveRequest.FromOwner?.Type == "Trader"
&& !ownerInventoryItems.IsMail.GetValueOrDefault(false)
)
{
AppendTraderExploitErrorResponse(output);
return;
}
// Check for item in inventory before allowing internal transfer
var originalItemLocation = ownerInventoryItems.From?.FirstOrDefault(item => item.Id == moveRequest.Item);
var originalItemLocation = ownerInventoryItems.From?.FirstOrDefault(item =>
item.Id == moveRequest.Item
);
if (originalItemLocation is null)
{
// Internal item move but item never existed, possible dupe glitch
@@ -91,9 +104,19 @@ public class InventoryController(
}
// Item is moving into or out of place of fame dog tag slot
if (moveRequest.To?.Container != null &&
(moveRequest.To.Container.StartsWith("dogtag", StringComparison.OrdinalIgnoreCase) ||
originalLocationSlotId.StartsWith("dogtag", StringComparison.OrdinalIgnoreCase)))
if (
moveRequest.To?.Container != null
&& (
moveRequest.To.Container.StartsWith(
"dogtag",
StringComparison.OrdinalIgnoreCase
)
|| originalLocationSlotId.StartsWith(
"dogtag",
StringComparison.OrdinalIgnoreCase
)
)
)
{
_hideoutHelper.ApplyPlaceOfFameDogtagBonus(pmcData);
}
@@ -117,7 +140,7 @@ public class InventoryController(
_httpResponseUtil.AppendErrorToOutput(
output,
_localisationService.GetText("inventory-edit_trader_item"),
(BackendErrorCodes) 228
(BackendErrorCodes)228
);
}
@@ -129,13 +152,21 @@ public class InventoryController(
/// <param name="request">Pin/Lock request data</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void PinOrLock(PmcData pmcData, PinOrLockItemRequest request, string sessionId,
ItemEventRouterResponse output)
public void PinOrLock(
PmcData pmcData,
PinOrLockItemRequest request,
string sessionId,
ItemEventRouterResponse output
)
{
var itemToAdjust = pmcData.Inventory!.Items!.FirstOrDefault(item => item.Id == request.Item);
var itemToAdjust = pmcData.Inventory!.Items!.FirstOrDefault(item =>
item.Id == request.Item
);
if (itemToAdjust is null)
{
_logger.Error($"Unable find item: {request.Item} to: {request.State} on player {sessionId}to: ");
_logger.Error(
$"Unable find item: {request.Item} to: {request.State} on player {sessionId}to: "
);
return;
}
@@ -165,7 +196,11 @@ public class InventoryController(
/// <param name="pmcData">Players PMC profile</param>
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
public void RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData request, string sessionId)
public void RedeemProfileReward(
PmcData pmcData,
RedeemProfileRequestData request,
string sessionId
)
{
var fullProfile = _profileHelper.GetFullProfile(sessionId);
foreach (var rewardEvent in request.Events)
@@ -173,49 +208,58 @@ public class InventoryController(
// Hard coded to `SYSTEM` for now
// TODO: make this dynamic
var dialog = fullProfile.DialogueRecords["59e7125688a45068a6249071"];
var mail = dialog.Messages.FirstOrDefault(message => message.Id == rewardEvent.MessageId);
var mailEvent =
mail.ProfileChangeEvents.FirstOrDefault(changeEvent => changeEvent.Id == rewardEvent.EventId);
var mail = dialog.Messages.FirstOrDefault(message =>
message.Id == rewardEvent.MessageId
);
var mailEvent = mail.ProfileChangeEvents.FirstOrDefault(changeEvent =>
changeEvent.Id == rewardEvent.EventId
);
switch (mailEvent.Type)
{
case "TraderSalesSum":
pmcData.TradersInfo[mailEvent.Entity].SalesSum = mailEvent.Value;
_traderHelper.LevelUp(mailEvent.Entity, pmcData);
_logger.Success($"Set trader {mailEvent.Entity}: Sales Sum to: {mailEvent.Value}");
_logger.Success(
$"Set trader {mailEvent.Entity}: Sales Sum to: {mailEvent.Value}"
);
break;
case "TraderStanding":
pmcData.TradersInfo[mailEvent.Entity].Standing = mailEvent.Value;
_traderHelper.LevelUp(mailEvent.Entity, pmcData);
_logger.Success($"Set trader {mailEvent.Entity}: Standing to: {mailEvent.Value}");
_logger.Success(
$"Set trader {mailEvent.Entity}: Standing to: {mailEvent.Value}"
);
break;
case "ProfileLevel":
pmcData.Info.Experience = (int) mailEvent.Value.Value;
pmcData.Info.Experience = (int)mailEvent.Value.Value;
// Will calculate level below
_traderHelper.ValidateTraderStandingsAndPlayerLevelForProfile(sessionId);
_logger.Success($"Set profile xp to: {mailEvent.Value}");
break;
case "SkillPoints":
{
var profileSkill = pmcData.Skills.Common.FirstOrDefault(x =>
x.Id == Enum.Parse<SkillTypes>(mailEvent.Entity)
);
if (profileSkill is null)
{
var profileSkill = pmcData.Skills.Common.FirstOrDefault(x => x.Id == Enum.Parse<SkillTypes>(mailEvent.Entity));
if (profileSkill is null)
{
_logger.Warning($"Unable to find skill with name: {mailEvent.Entity}");
continue;
}
profileSkill.Progress = mailEvent.Value;
_logger.Success($"Set profile skill: {mailEvent.Entity} to: {mailEvent.Value}");
break;
_logger.Warning($"Unable to find skill with name: {mailEvent.Entity}");
continue;
}
profileSkill.Progress = mailEvent.Value;
_logger.Success($"Set profile skill: {mailEvent.Entity} to: {mailEvent.Value}");
break;
}
case "ExamineAllItems":
{
var itemsToInspect = _itemHelper.GetItems().Where(x => x.Type != "Node");
FlagItemsAsInspectedAndRewardXp(itemsToInspect.Select(x => x.Id), fullProfile);
_logger.Success($"Flagged {itemsToInspect.Count()} items as examined");
{
var itemsToInspect = _itemHelper.GetItems().Where(x => x.Type != "Node");
FlagItemsAsInspectedAndRewardXp(itemsToInspect.Select(x => x.Id), fullProfile);
_logger.Success($"Flagged {itemsToInspect.Count()} items as examined");
break;
}
break;
}
case "UnlockTrader":
pmcData.TradersInfo[mailEvent.Entity].Unlocked = true;
_logger.Success($"Trader {mailEvent.Entity} Unlocked");
@@ -228,19 +272,21 @@ public class InventoryController(
break;
case "HideoutAreaLevel":
{
var areaName = mailEvent.Entity;
var newValue = mailEvent.Value;
var hideoutAreaType = Enum.Parse<HideoutAreas>(areaName ?? "NotSet");
var desiredArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == hideoutAreaType
);
if (desiredArea is not null)
{
var areaName = mailEvent.Entity;
var newValue = mailEvent.Value;
var hideoutAreaType = Enum.Parse<HideoutAreas>(areaName ?? "NotSet");
var desiredArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == hideoutAreaType);
if (desiredArea is not null)
{
desiredArea.Level = (int?) newValue;
}
break;
desiredArea.Level = (int?)newValue;
}
break;
}
default:
_logger.Warning($"Unhandled profile reward event: {mailEvent.Type}");
@@ -254,7 +300,10 @@ public class InventoryController(
/// </summary>
/// <param name="itemTpls">Inspected item tpls</param>
/// <param name="fullProfile">Profile to add xp to</param>
protected void FlagItemsAsInspectedAndRewardXp(IEnumerable<string> itemTpls, SptProfile fullProfile)
protected void FlagItemsAsInspectedAndRewardXp(
IEnumerable<string> itemTpls,
SptProfile fullProfile
)
{
foreach (var itemTpl in itemTpls)
{
@@ -262,16 +311,23 @@ public class InventoryController(
if (!item.Key)
{
_logger.Warning(
_localisationService.GetText("inventory-unable_to_inspect_item_not_in_db", itemTpl)
_localisationService.GetText(
"inventory-unable_to_inspect_item_not_in_db",
itemTpl
)
);
return;
}
fullProfile.CharacterData.PmcData.Info.Experience += item.Value.Properties.ExamineExperience;
fullProfile.CharacterData.PmcData.Info.Experience += item.Value
.Properties
.ExamineExperience;
fullProfile.CharacterData.PmcData.Encyclopedia[itemTpl] = false;
fullProfile.CharacterData.ScavData.Info.Experience += item.Value.Properties.ExamineExperience;
fullProfile.CharacterData.ScavData.Info.Experience += item.Value
.Properties
.ExamineExperience;
fullProfile.CharacterData.ScavData.Encyclopedia[itemTpl] = false;
}
@@ -291,8 +347,12 @@ public class InventoryController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void OpenRandomLootContainer(PmcData pmcData, OpenRandomLootContainerRequestData request, string sessionId,
ItemEventRouterResponse output)
public void OpenRandomLootContainer(
PmcData pmcData,
OpenRandomLootContainerRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
// Container player opened in their inventory
var openedItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
@@ -305,7 +365,7 @@ public class InventoryController(
{
ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_VIOLET_OPEN,
ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_BLUE_OPEN,
ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_GREEN_OPEN
ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_GREEN_OPEN,
};
// Temp fix for unlocked weapon crate hideout craft
if (isSealedWeaponBox || unlockedWeaponCrates.Contains(containerDetailsDb.Value.Id))
@@ -320,10 +380,14 @@ public class InventoryController(
}
else
{
var rewardContainerDetails = _inventoryHelper.GetRandomLootContainerRewardDetails(openedItem.Template);
var rewardContainerDetails = _inventoryHelper.GetRandomLootContainerRewardDetails(
openedItem.Template
);
if (rewardContainerDetails?.RewardCount == null)
{
_logger.Error($"Unable to add loot to container: {openedItem.Template}, no rewards found");
_logger.Error(
$"Unable to add loot to container: {openedItem.Template}, no rewards found"
);
}
else
{
@@ -344,7 +408,7 @@ public class InventoryController(
ItemsWithModsToAdd = rewards,
FoundInRaid = foundInRaid,
Callback = null,
UseSortingTable = true
UseSortingTable = true,
};
_inventoryHelper.AddItemsToStash(sessionId, addItemsRequest, pmcData, output);
if (output.Warnings?.Count > 0)
@@ -364,8 +428,12 @@ public class InventoryController(
/// <param name="request">Edit marker request</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData request, string sessionId,
ItemEventRouterResponse output)
public void EditMapMarker(
PmcData pmcData,
InventoryEditMarkerRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
var mapItem = _mapMarkerService.EditMarkerOnMap(pmcData, request);
@@ -380,8 +448,12 @@ public class InventoryController(
/// <param name="request">Delete marker request</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void DeleteMapMarker(PmcData pmcData, InventoryDeleteMarkerRequestData request, string sessionId,
ItemEventRouterResponse output)
public void DeleteMapMarker(
PmcData pmcData,
InventoryDeleteMarkerRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
var mapItem = _mapMarkerService.DeleteMarkerFromMap(pmcData, request);
@@ -389,8 +461,12 @@ public class InventoryController(
output.ProfileChanges[sessionId].Items.ChangedItems.Add(mapItem);
}
public void CreateMapMarker(PmcData pmcData, InventoryCreateMarkerRequestData request, string sessionId,
ItemEventRouterResponse output)
public void CreateMapMarker(
PmcData pmcData,
InventoryCreateMarkerRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
var adjustedMapItem = _mapMarkerService.CreateMarkerOnMap(pmcData, request);
@@ -405,16 +481,25 @@ public class InventoryController(
/// <param name="request">Add marker request</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void SortInventory(PmcData pmcData, InventorySortRequestData request, string sessionId,
ItemEventRouterResponse output)
public void SortInventory(
PmcData pmcData,
InventorySortRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
foreach (var change in request.ChangedItems)
{
var inventoryItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == change.Id);
var inventoryItem = pmcData.Inventory.Items.FirstOrDefault(item =>
item.Id == change.Id
);
if (inventoryItem is null)
{
_logger.Error(
_localisationService.GetText("inventory-unable_to_sort_inventory_restart_game", change.Id)
_localisationService.GetText(
"inventory-unable_to_sort_inventory_restart_game",
change.Id
)
);
continue;
@@ -440,8 +525,11 @@ public class InventoryController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData request,
string sessionId)
public ItemEventRouterResponse ReadEncyclopedia(
PmcData pmcData,
InventoryReadEncyclopediaRequestData request,
string sessionId
)
{
foreach (var id in request.Ids)
{
@@ -458,8 +546,12 @@ public class InventoryController(
/// <param name="request">Examine item request</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void ExamineItem(PmcData pmcData, InventoryExamineRequestData request, string sessionId,
ItemEventRouterResponse output)
public void ExamineItem(
PmcData pmcData,
InventoryExamineRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
string? itemId = null;
if (request.FromOwner is not null)
@@ -470,12 +562,17 @@ public class InventoryController(
}
catch
{
_logger.Error(_localisationService.GetText("inventory-examine_item_does_not_exist", request.Item));
_logger.Error(
_localisationService.GetText(
"inventory-examine_item_does_not_exist",
request.Item
)
);
}
}
if (itemId is null)
// item template
// item template
{
if (_databaseService.GetItems().ContainsKey(request.Item))
{
@@ -514,14 +611,17 @@ public class InventoryController(
}
if (request.FromOwner.Id == Traders.FENCE)
// Get tpl from fence assorts
// Get tpl from fence assorts
{
return _fenceService.GetRawFenceAssorts().Items.FirstOrDefault(x => x.Id == request.Item)?.Template;
return _fenceService
.GetRawFenceAssorts()
.Items.FirstOrDefault(x => x.Id == request.Item)
?.Template;
}
if (request.FromOwner.Type == "Trader")
// Not fence
// get tpl from trader assort
// Not fence
// get tpl from trader assort
{
return _databaseService
.GetTrader(request.FromOwner.Id)
@@ -539,18 +639,23 @@ public class InventoryController(
}
// Try alternate way of getting offer if first approach fails
var offer = _ragfairOfferService.GetOfferByOfferId(request.Item) ??
_ragfairOfferService.GetOfferByOfferId(request.FromOwner.Id);
var offer =
_ragfairOfferService.GetOfferByOfferId(request.Item)
?? _ragfairOfferService.GetOfferByOfferId(request.FromOwner.Id);
// Try find examine item inside offer items array
var matchingItem = offer.Items.FirstOrDefault(offerItem => offerItem.Id == request.Item);
var matchingItem = offer.Items.FirstOrDefault(offerItem =>
offerItem.Id == request.Item
);
if (matchingItem is not null)
{
return matchingItem.Template;
}
// Unable to find item in database or ragfair
_logger.Warning(_localisationService.GetText("inventory-unable_to_find_item", request.Item));
_logger.Warning(
_localisationService.GetText("inventory-unable_to_find_item", request.Item)
);
}
// get hideout item
@@ -565,7 +670,9 @@ public class InventoryController(
// all mail the player has
var mail = _profileHelper.GetFullProfile(sessionId).DialogueRecords;
// per trader/person mail
var dialogue = mail.FirstOrDefault(x => x.Value.Messages.Any(m => m.Id == request.FromOwner.Id));
var dialogue = mail.FirstOrDefault(x =>
x.Value.Messages.Any(m => m.Id == request.FromOwner.Id)
);
// check each message from that trader/person for messages that match the ID we got
var message = dialogue.Value.Messages.FirstOrDefault(m => m.Id == request.FromOwner.Id);
// get the Id given and get the Template ID from that
@@ -590,8 +697,12 @@ public class InventoryController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void UnBindItem(PmcData pmcData, InventoryBindRequestData request, string sessionId,
ItemEventRouterResponse output)
public void UnBindItem(
PmcData pmcData,
InventoryBindRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
// Remove kvp from requested fast panel index
@@ -607,8 +718,12 @@ public class InventoryController(
/// <param name="bindRequest"></param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void BindItem(PmcData pmcData, InventoryBindRequestData bindRequest, string sessionId,
ItemEventRouterResponse output)
public void BindItem(
PmcData pmcData,
InventoryBindRequestData bindRequest,
string sessionId,
ItemEventRouterResponse output
)
{
// Remove link
if (pmcData.Inventory.FastPanel.ContainsKey(bindRequest.Index))
@@ -627,7 +742,11 @@ public class InventoryController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData request, string sessionId)
public ItemEventRouterResponse TagItem(
PmcData pmcData,
InventoryTagRequestData request,
string sessionId
)
{
var itemToTag = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
if (itemToTag is null)
@@ -636,20 +755,13 @@ public class InventoryController(
$"Unable to tag item: {request.Item} as it cannot be found in player {sessionId} inventory"
);
return new ItemEventRouterResponse
{
Warnings = []
};
return new ItemEventRouterResponse { Warnings = [] };
}
// Null guard
itemToTag.Upd ??= new Upd();
itemToTag.Upd.Tag = new UpdTag
{
Color = request.TagColor,
Name = request.TagName
};
itemToTag.Upd.Tag = new UpdTag { Color = request.TagColor, Name = request.TagName };
return _eventOutputHolder.GetOutput(sessionId);
}
@@ -661,7 +773,11 @@ public class InventoryController(
/// <param name="request">Toggle request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse ToggleItem(PmcData pmcData, InventoryToggleRequestData request, string sessionId)
public ItemEventRouterResponse ToggleItem(
PmcData pmcData,
InventoryToggleRequestData request,
string sessionId
)
{
// May need to reassign to scav profile
var playerData = pmcData;
@@ -677,23 +793,22 @@ public class InventoryController(
{
_itemHelper.AddUpdObjectToItem(
itemToToggle,
_localisationService.GetText("inventory-item_to_toggle_missing_upd", itemToToggle.Id)
_localisationService.GetText(
"inventory-item_to_toggle_missing_upd",
itemToToggle.Id
)
);
itemToToggle.Upd.Togglable = new UpdTogglable
{
On = request.Value
};
itemToToggle.Upd.Togglable = new UpdTogglable { On = request.Value };
return _eventOutputHolder.GetOutput(sessionId);
}
_logger.Warning(_localisationService.GetText("inventory-unable_to_toggle_item_not_found", request.Item));
_logger.Warning(
_localisationService.GetText("inventory-unable_to_toggle_item_not_found", request.Item)
);
return new ItemEventRouterResponse
{
Warnings = []
};
return new ItemEventRouterResponse { Warnings = [] };
}
/// <summary>
@@ -703,7 +818,11 @@ public class InventoryController(
/// <param name="request">Fold item request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse FoldItem(PmcData pmcData, InventoryFoldRequestData request, string sessionId)
public ItemEventRouterResponse FoldItem(
PmcData pmcData,
InventoryFoldRequestData request,
string sessionId
)
{
// May need to reassign to scav profile
var playerData = pmcData;
@@ -714,27 +833,26 @@ public class InventoryController(
playerData = _profileHelper.GetScavProfile(sessionId);
}
var itemToFold = playerData.Inventory.Items.FirstOrDefault(item => item?.Id == request.Item);
var itemToFold = playerData.Inventory.Items.FirstOrDefault(item =>
item?.Id == request.Item
);
if (itemToFold is null)
{
// Item not found
_logger.Warning(
_localisationService.GetText("inventory-unable_to_fold_item_not_found_in_inventory", request.Item)
_localisationService.GetText(
"inventory-unable_to_fold_item_not_found_in_inventory",
request.Item
)
);
return new ItemEventRouterResponse
{
Warnings = []
};
return new ItemEventRouterResponse { Warnings = [] };
}
// Item may not have upd object
_itemHelper.AddUpdObjectToItem(itemToFold);
itemToFold.Upd.Foldable = new UpdFoldable
{
Folded = request.Value
};
itemToFold.Upd.Foldable = new UpdFoldable { Folded = request.Value };
return _eventOutputHolder.GetOutput(sessionId);
}
@@ -748,7 +866,11 @@ public class InventoryController(
/// <param name="request">Swap item request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse SwapItem(PmcData pmcData, InventorySwapRequestData request, string sessionId)
public ItemEventRouterResponse SwapItem(
PmcData pmcData,
InventorySwapRequestData request,
string sessionId
)
{
// During post-raid scav transfer, the swap may be in the scav inventory
var playerData = pmcData;
@@ -763,11 +885,7 @@ public class InventoryController(
_logger.Error(
_localisationService.GetText(
"inventory-unable_to_find_item_to_swap",
new
{
item1Id = request.Item,
item2Id = request.Item2
}
new { item1Id = request.Item, item2Id = request.Item2 }
)
);
}
@@ -778,11 +896,7 @@ public class InventoryController(
_logger.Error(
_localisationService.GetText(
"inventory-unable_to_find_item_to_swap",
new
{
item1Id = request.Item2,
item2Id = request.Item
}
new { item1Id = request.Item2, item2Id = request.Item }
)
);
}
@@ -827,11 +941,19 @@ public class InventoryController(
/// <param name="request">Transfer item request</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void TransferItem(PmcData pmcData, InventoryTransferRequestData request, string sessionId,
ItemEventRouterResponse output)
public void TransferItem(
PmcData pmcData,
InventoryTransferRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
// TODO - check GetOwnerInventoryItems() call still works
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(request, request.Item, sessionId);
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(
request,
request.Item,
sessionId
);
var sourceItem = inventoryItems.From.FirstOrDefault(item => item.Id == request.Item);
var destinationItem = inventoryItems.To.FirstOrDefault(item => item.Id == request.With);
@@ -855,27 +977,21 @@ public class InventoryController(
return;
}
sourceItem.Upd ??= new Upd
{
StackObjectsCount = 1
};
sourceItem.Upd ??= new Upd { StackObjectsCount = 1 };
var sourceStackCount = sourceItem.Upd.StackObjectsCount;
if (sourceStackCount > request.Count)
// Source items stack count greater than new desired count
// Source items stack count greater than new desired count
{
sourceItem.Upd.StackObjectsCount = sourceStackCount - request.Count;
}
else
// Moving a full stack onto a smaller stack
// Moving a full stack onto a smaller stack
{
sourceItem.Upd.StackObjectsCount = sourceStackCount - 1;
}
destinationItem.Upd ??= new Upd
{
StackObjectsCount = 1
};
destinationItem.Upd ??= new Upd { StackObjectsCount = 1 };
var destinationStackCount = destinationItem.Upd.StackObjectsCount;
destinationItem.Upd.StackObjectsCount = destinationStackCount + request.Count;
@@ -889,17 +1005,26 @@ public class InventoryController(
/// <param name="request">Merge stacks request</param>
/// <param name="sessionID">Session/Player id</param>
/// <param name="output">Client response</param>
public void MergeItem(PmcData pmcData, InventoryMergeRequestData request, string sessionID,
ItemEventRouterResponse output)
public void MergeItem(
PmcData pmcData,
InventoryMergeRequestData request,
string sessionID,
ItemEventRouterResponse output
)
{
// Changes made to result apply to character inventory
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(request, request.Item, sessionID);
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(
request,
request.Item,
sessionID
);
// Get source item (can be from player or trader or mail)
var sourceItem = inventoryItems.From.FirstOrDefault(x => x.Id == request.Item);
if (sourceItem is null)
{
var errorMessage = $"Unable to merge stacks as source item: {request.With} cannot be found";
var errorMessage =
$"Unable to merge stacks as source item: {request.With} cannot be found";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -911,7 +1036,8 @@ public class InventoryController(
var destinationItem = inventoryItems.To.FirstOrDefault(x => x.Id == request.With);
if (destinationItem is null)
{
var errorMessage = $"Unable to merge stacks as destination item: {request.With} cannot be found";
var errorMessage =
$"Unable to merge stacks as destination item: {request.With} cannot be found";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -920,48 +1046,38 @@ public class InventoryController(
}
if (destinationItem.Upd?.StackObjectsCount is null)
// No stackcount on destination, add one
// No stackcount on destination, add one
{
destinationItem.Upd = new Upd
{
StackObjectsCount = 1
};
destinationItem.Upd = new Upd { StackObjectsCount = 1 };
}
if (sourceItem.Upd is null)
{
sourceItem.Upd = new Upd
{
StackObjectsCount = 1
};
sourceItem.Upd = new Upd { StackObjectsCount = 1 };
}
else if (sourceItem.Upd.StackObjectsCount is null)
// Items pulled out of raid can have no stack count if the stack should be 1
// Items pulled out of raid can have no stack count if the stack should be 1
{
sourceItem.Upd.StackObjectsCount = 1;
}
// Remove FiR status from destination stack when source stack has no FiR but destination does
if (!sourceItem.Upd.SpawnedInSession.GetValueOrDefault(false) &&
destinationItem.Upd.SpawnedInSession.GetValueOrDefault(false))
if (
!sourceItem.Upd.SpawnedInSession.GetValueOrDefault(false)
&& destinationItem.Upd.SpawnedInSession.GetValueOrDefault(false)
)
{
destinationItem.Upd.SpawnedInSession = false;
}
destinationItem.Upd.StackObjectsCount +=
sourceItem.Upd.StackObjectsCount; // Add source stackcount to destination
output.ProfileChanges[sessionID]
.Items.DeletedItems.Add(
new Item
{
Id = sourceItem.Id
}
); // Inform client source item being deleted
destinationItem.Upd.StackObjectsCount += sourceItem.Upd.StackObjectsCount; // Add source stackcount to destination
output.ProfileChanges[sessionID].Items.DeletedItems.Add(new Item { Id = sourceItem.Id }); // Inform client source item being deleted
var indexOfItemToRemove = inventoryItems.From.FindIndex(x => x.Id == sourceItem.Id);
if (indexOfItemToRemove == -1)
{
var errorMessage = $"Unable to find item: {sourceItem.Id} to remove from sender inventory";
var errorMessage =
$"Unable to find item: {sourceItem.Id} to remove from sender inventory";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -979,11 +1095,19 @@ public class InventoryController(
/// <param name="request">Split stack request</param>
/// <param name="sessionID">Session/Player id</param>
/// <param name="output">Client response</param>
public void SplitItem(PmcData pmcData, InventorySplitRequestData request, string sessionID,
ItemEventRouterResponse output)
public void SplitItem(
PmcData pmcData,
InventorySplitRequestData request,
string sessionID,
ItemEventRouterResponse output
)
{
// Changes made to result apply to character inventory
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(request, request.NewItem, sessionID);
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(
request,
request.NewItem,
sessionID
);
// Handle cartridge edge-case
if (request.Container.Location is null && request.Container.ContainerName == "cartridges")
@@ -996,7 +1120,8 @@ public class InventoryController(
var itemToSplit = inventoryItems.From.FirstOrDefault(x => x.Id == request.SplitItem);
if (itemToSplit is null)
{
var errorMessage = $"Unable to split stack as source item: {request.SplitItem} cannot be found";
var errorMessage =
$"Unable to split stack as source item: {request.SplitItem} cannot be found";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -1012,13 +1137,14 @@ public class InventoryController(
itemToSplit.Upd.StackObjectsCount -= request.Count;
// Inform client of change
output.ProfileChanges[sessionID]
output
.ProfileChanges[sessionID]
.Items.NewItems.Add(
new Item
{
Id = request.NewItem,
Template = itemToSplit.Template,
Upd = updatedUpd
Upd = updatedUpd,
}
);
@@ -1031,7 +1157,7 @@ public class InventoryController(
ParentId = request.Container.Id,
SlotId = request.Container.ContainerName,
Location = request.Container.Location,
Upd = updatedUpd
Upd = updatedUpd,
}
);
}
@@ -1044,8 +1170,12 @@ public class InventoryController(
/// <param name="request">Discard item request</param>
/// <param name="sessionId">Session/Player id</param>
/// <param name="output">Client response</param>
public void DiscardItem(PmcData pmcData, InventoryRemoveRequestData request, string sessionId,
ItemEventRouterResponse output)
public void DiscardItem(
PmcData pmcData,
InventoryRemoveRequestData request,
string sessionId,
ItemEventRouterResponse output
)
{
if (request.FromOwner?.Type == "Mail")
{
@@ -1054,9 +1184,10 @@ public class InventoryController(
return;
}
var profileToRemoveItemFrom = request.FromOwner is null || request.FromOwner?.Id == pmcData.Id
? pmcData
: _profileHelper.GetFullProfile(sessionId).CharacterData.ScavData;
var profileToRemoveItemFrom =
request.FromOwner is null || request.FromOwner?.Id == pmcData.Id
? pmcData
: _profileHelper.GetFullProfile(sessionId).CharacterData.ScavData;
_inventoryHelper.RemoveItem(profileToRemoveItemFrom, request.Item, sessionId, output);
}
@@ -37,8 +37,11 @@ public class LauncherController(
public ConnectResponse Connect()
{
// Get all possible profile types + filter out any that are blacklisted
var profileTemplates = _databaseService.GetProfileTemplates()
.Where(profile => !_coreConfig.Features.CreateNewProfileTypesBlacklist.Contains(profile.Key))
var profileTemplates = _databaseService
.GetProfileTemplates()
.Where(profile =>
!_coreConfig.Features.CreateNewProfileTypesBlacklist.Contains(profile.Key)
)
.ToDictionary();
return new ConnectResponse
@@ -46,7 +49,7 @@ public class LauncherController(
BackendUrl = _httpServerHelper.GetBackendUrl(),
Name = _coreConfig.ServerName,
Editions = profileTemplates.Select(x => x.Key).ToList(),
ProfileDescriptions = GetProfileDescriptions(profileTemplates)
ProfileDescriptions = GetProfileDescriptions(profileTemplates),
};
}
@@ -55,7 +58,9 @@ public class LauncherController(
/// </summary>
/// <param name="profileTemplates">Profiles to get descriptions of</param>
/// <returns>Dictionary of profile types with related descriptive text</returns>
protected Dictionary<string, string> GetProfileDescriptions(Dictionary<string, ProfileSides> profileTemplates)
protected Dictionary<string, string> GetProfileDescriptions(
Dictionary<string, ProfileSides> profileTemplates
)
{
var result = new Dictionary<string, string>();
foreach (var (profileKey, profile) in profileTemplates)
@@ -72,7 +77,11 @@ public class LauncherController(
/// <returns></returns>
public Info? Find(string? sessionId)
{
return sessionId is not null && _saveServer.GetProfiles().TryGetValue(sessionId, out var profile) ? profile.ProfileInfo : null;
return
sessionId is not null
&& _saveServer.GetProfiles().TryGetValue(sessionId, out var profile)
? profile.ProfileInfo
: null;
}
/// <summary>
@@ -126,7 +135,7 @@ public class LauncherController(
Username = info.Username,
Password = info.Password,
IsWiped = true,
Edition = info.Edition
Edition = info.Edition,
};
_saveServer.CreateProfile(newProfileDetails);
@@ -229,7 +238,10 @@ public class LauncherController(
/// <returns>Dictionary of mod name and mod details</returns>
public Dictionary<string, AbstractModMetadata> GetLoadedServerMods()
{
return _loadedMods.ToDictionary(sptMod => sptMod.ModMetadata?.Name ?? "UNKNOWN MOD", sptMod => sptMod.ModMetadata);
return _loadedMods.ToDictionary(
sptMod => sptMod.ModMetadata?.Name ?? "UNKNOWN MOD",
sptMod => sptMod.ModMetadata
);
}
/// <summary>
@@ -48,7 +48,10 @@ public class LauncherV2Controller(
foreach (var profileKvP in dbProfiles)
{
result.TryAdd(profileKvP.Key, _localisationService.GetText(profileKvP.Value.DescriptionLocaleKey));
result.TryAdd(
profileKvP.Key,
_localisationService.GetText(profileKvP.Value.DescriptionLocaleKey)
);
}
return result;
@@ -174,7 +177,7 @@ public class LauncherV2Controller(
Username = info.Username,
Password = info.Password,
IsWiped = true,
Edition = info.Edition
Edition = info.Edition,
};
_saveServer.CreateProfile(newProfileDetails);
@@ -204,7 +207,10 @@ public class LauncherV2Controller(
{
foreach (var profile in _saveServer.GetProfiles())
{
if (info.Username == profile.Value.ProfileInfo!.Username && info.Password == profile.Value.ProfileInfo.Password)
if (
info.Username == profile.Value.ProfileInfo!.Username
&& info.Password == profile.Value.ProfileInfo.Password
)
{
return profile.Key;
}
@@ -7,7 +7,6 @@ using SPTarkov.Server.Core.Services;
using SPTarkov.Server.Core.Utils.Cloners;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
@@ -54,7 +53,7 @@ public class LocationController(
return new LocationsGenerateAllResponse
{
Locations = locationResult,
Paths = locationsFromDb.Base!.Paths
Paths = locationsFromDb.Base!.Paths,
};
}
@@ -70,9 +70,9 @@ public class MatchController(
RaidMode = "Online",
Mode = "deathmatch",
ShortId = null,
AdditionalInfo = null
}
]
AdditionalInfo = null,
},
],
};
return output;
@@ -85,11 +85,7 @@ public class MatchController(
/// <returns>MatchGroupStatusResponse</returns>
public MatchGroupStatusResponse GetGroupStatus(MatchGroupStatusRequest request)
{
return new MatchGroupStatusResponse
{
Players = [],
MaxPveCountExceeded = false
};
return new MatchGroupStatusResponse { Players = [], MaxPveCountExceeded = false };
}
/// <summary>
@@ -138,7 +134,10 @@ public class MatchController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="request">Start raid request</param>
/// <returns>StartLocalRaidResponseData</returns>
public StartLocalRaidResponseData StartLocalRaid(string sessionId, StartLocalRaidRequestData request)
public StartLocalRaidResponseData StartLocalRaid(
string sessionId,
StartLocalRaidRequestData request
)
{
return _locationLifecycleService.StartLocalRaid(sessionId, request);
}
@@ -7,9 +7,7 @@ using SPTarkov.Server.Core.Routers;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class NoteController(
EventOutputHolder _eventOutputHolder
)
public class NoteController(EventOutputHolder _eventOutputHolder)
{
/// <summary>
/// </summary>
@@ -20,13 +18,10 @@ public class NoteController(
public ItemEventRouterResponse AddNote(
PmcData pmcData,
NoteActionRequest request,
string sessionId)
string sessionId
)
{
var newNote = new Note
{
Time = request.Note.Time,
Text = request.Note.Text
};
var newNote = new Note { Time = request.Note.Time, Text = request.Note.Text };
pmcData.Notes.DataNotes.Add(newNote);
return _eventOutputHolder.GetOutput(sessionId);
@@ -41,7 +36,8 @@ public class NoteController(
public ItemEventRouterResponse EditNote(
PmcData pmcData,
NoteActionRequest request,
string sessionId)
string sessionId
)
{
var noteToEdit = pmcData.Notes.DataNotes[request.Index!.Value];
noteToEdit.Time = request.Note.Time;
@@ -59,7 +55,8 @@ public class NoteController(
public ItemEventRouterResponse DeleteNote(
PmcData pmcData,
NoteActionRequest request,
string sessionId)
string sessionId
)
{
pmcData.Notes?.DataNotes?.RemoveAt(request.Index!.Value);
@@ -63,7 +63,7 @@ public class NotifierController(
ChannelId = sessionId,
Url = "",
NotifierServer = GetServer(sessionId),
WebSocket = _notifierHelper.GetWebSocketServer(sessionId)
WebSocket = _notifierHelper.GetWebSocketServer(sessionId),
};
}
@@ -33,10 +33,7 @@ public class PresetController(
// Get root items tpl
var tpl = preset.Items.FirstOrDefault()?.Template;
result.TryAdd(tpl, new PresetCacheDetails
{
PresetIds = []
});
result.TryAdd(tpl, new PresetCacheDetails { PresetIds = [] });
result.TryGetValue(tpl, out var details);
details.PresetIds.Add(presetId);
@@ -23,8 +23,7 @@ public class PrestigeController(
/// </summary>
/// <param name="sessionId">Session/Player id</param>
/// <returns>Prestige</returns>
public Prestige GetPrestige(
string sessionId)
public Prestige GetPrestige(string sessionId)
{
return _databaseService.GetTemplates().Prestige;
}
@@ -59,9 +58,7 @@ public class PrestigeController(
/// </list>
/// </summary>
/// <returns></returns>
public async Task ObtainPrestige(
string sessionId,
ObtainPrestigeRequestList request)
public async Task ObtainPrestige(string sessionId, ObtainPrestigeRequestList request)
{
var profile = _profileHelper.GetFullProfile(sessionId);
if (profile is not null)
@@ -69,7 +66,7 @@ public class PrestigeController(
var pendingPrestige = new PendingPrestige
{
PrestigeLevel = profile.CharacterData.PmcData.Info.PrestigeLevel + 1,
Items = request
Items = request,
};
profile.SptData.PendingPrestige = pendingPrestige;
@@ -57,7 +57,9 @@ public class ProfileController(
var profile = _saveServer.GetProfile(sessionId);
if (profile?.CharacterData == null)
{
throw new Exception($"Unable to find character data for id: {sessionId}. Profile may be corrupt");
throw new Exception(
$"Unable to find character data for id: {sessionId}. Profile may be corrupt"
);
}
var pmc = profile.CharacterData.PmcData;
@@ -81,7 +83,7 @@ public class ProfileController(
MaxLevel = maxLvl,
Edition = profile.ProfileInfo?.Edition ?? "",
ProfileId = profile.ProfileInfo?.ProfileId ?? "",
SptData = _profileHelper.GetDefaultSptDataObject()
SptData = _profileHelper.GetDefaultSptDataObject(),
};
}
@@ -93,12 +95,13 @@ public class ProfileController(
Side = pmc.Info.Side,
CurrentLevel = pmc.Info.Level,
CurrentExperience = pmc.Info.Experience ?? 0,
PreviousExperience = currentLevel == 0 ? 0 : _profileHelper.GetExperience(currentLevel.Value),
PreviousExperience =
currentLevel == 0 ? 0 : _profileHelper.GetExperience(currentLevel.Value),
NextLevel = xpToNextLevel,
MaxLevel = maxLvl,
Edition = profile.ProfileInfo?.Edition ?? "",
ProfileId = profile.ProfileInfo?.ProfileId ?? "",
SptData = profile.SptData
SptData = profile.SptData,
};
}
@@ -118,7 +121,10 @@ public class ProfileController(
/// <param name="request">Create profile request</param>
/// <param name="sessionId">Player id</param>
/// <returns>Player id</returns>
public virtual async ValueTask<string> CreateProfile(ProfileCreateRequestData request, string sessionId)
public virtual async ValueTask<string> CreateProfile(
ProfileCreateRequestData request,
string sessionId
)
{
return await _createProfileService.CreateProfile(sessionId, request);
}
@@ -140,7 +146,10 @@ public class ProfileController(
/// <param name="request">Validate nickname request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public virtual NicknameValidationResult ValidateNickname(ValidateNicknameRequestData request, string sessionId)
public virtual NicknameValidationResult ValidateNickname(
ValidateNicknameRequestData request,
string sessionId
)
{
if (request.Nickname?.Length < 3)
{
@@ -162,13 +171,13 @@ public class ProfileController(
/// <param name="request">Change nickname request</param>
/// <param name="sessionId">Player id</param>
/// <returns></returns>
public virtual NicknameValidationResult ChangeNickname(ProfileChangeNicknameRequestData request, string sessionId)
public virtual NicknameValidationResult ChangeNickname(
ProfileChangeNicknameRequestData request,
string sessionId
)
{
var output = ValidateNickname(
new ValidateNicknameRequestData
{
Nickname = request.Nickname
},
new ValidateNicknameRequestData { Nickname = request.Nickname },
sessionId
);
@@ -200,7 +209,10 @@ public class ProfileController(
/// <param name="request">Search profiles request</param>
/// <param name="sessionID">Player id</param>
/// <returns>Found profiles</returns>
public virtual List<SearchFriendResponse> SearchProfiles(SearchProfilesRequestData request, string sessionID)
public virtual List<SearchFriendResponse> SearchProfiles(
SearchProfilesRequestData request,
string sessionID
)
{
var result = new List<SearchFriendResponse>();
@@ -241,7 +253,7 @@ public class ProfileController(
Status = "Free",
Sid = "",
Ip = "",
Port = 0
Port = 0,
},
new ProfileStatusData
{
@@ -250,9 +262,9 @@ public class ProfileController(
Status = "Free",
Sid = "",
Ip = "",
Port = 0
}
]
Port = 0,
},
],
};
return response;
@@ -264,13 +276,21 @@ public class ProfileController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="request">Get other profile request</param>
/// <returns>GetOtherProfileResponse</returns>
public virtual GetOtherProfileResponse GetOtherProfile(string sessionId, GetOtherProfileRequest request)
public virtual GetOtherProfileResponse GetOtherProfile(
string sessionId,
GetOtherProfileRequest request
)
{
// Find the profile by the account ID, fall back to the current player if we can't find the account
var profileToView = _profileHelper.GetFullProfileByAccountId(request.AccountId);
if (profileToView?.CharacterData?.PmcData is null || profileToView.CharacterData.ScavData is null)
if (
profileToView?.CharacterData?.PmcData is null
|| profileToView.CharacterData.ScavData is null
)
{
_logger.Warning($"Unable to get profile: {request.AccountId} to show, falling back to own profile");
_logger.Warning(
$"Unable to get profile: {request.AccountId} to show, falling back to own profile"
);
profileToView = _profileHelper.GetFullProfile(sessionId);
}
@@ -283,12 +303,17 @@ public class ProfileController(
hideoutKeys.Add(profileToViewPmc.Inventory.HideoutCustomizationStashId);
// Find hideout items e.g. posters
var hideoutRootItems = profileToViewPmc.Inventory.Items.Where(x => hideoutKeys.Contains(x.Id));
var hideoutRootItems = profileToViewPmc.Inventory.Items.Where(x =>
hideoutKeys.Contains(x.Id)
);
var itemsToReturn = new List<Item>();
foreach (var rootItems in hideoutRootItems)
{
// Check each root items for children and add
var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(profileToViewPmc.Inventory.Items, rootItems.Id);
var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(
profileToViewPmc.Inventory.Items,
rootItems.Id
);
itemsToReturn.AddRange(itemWithChildren);
}
@@ -301,10 +326,12 @@ public class ProfileController(
Nickname = profileToViewPmc.Info.Nickname,
Side = profileToViewPmc.Info.Side,
Experience = profileToViewPmc.Info.Experience,
MemberCategory = (int)(profileToViewPmc.Info.MemberCategory ?? MemberCategory.Default),
MemberCategory = (int)(
profileToViewPmc.Info.MemberCategory ?? MemberCategory.Default
),
BannedState = profileToViewPmc.Info.BannedState,
BannedUntil = profileToViewPmc.Info.BannedUntil,
RegistrationDate = profileToViewPmc.Info.RegistrationDate
RegistrationDate = profileToViewPmc.Info.RegistrationDate,
},
Customization = new OtherProfileCustomization
{
@@ -312,13 +339,13 @@ public class ProfileController(
Body = profileToViewPmc.Customization.Body,
Feet = profileToViewPmc.Customization.Feet,
Hands = profileToViewPmc.Customization.Hands,
Dogtag = profileToViewPmc.Customization.DogTag
Dogtag = profileToViewPmc.Customization.DogTag,
},
Skills = profileToViewPmc.Skills,
Equipment = new OtherProfileEquipment
{
Id = profileToViewPmc.Inventory.Equipment,
Items = profileToViewPmc.Inventory.Items
Items = profileToViewPmc.Inventory.Items,
},
Achievements = profileToViewPmc.Achievements,
FavoriteItems = _profileHelper.GetOtherProfileFavorites(profileToViewPmc),
@@ -327,21 +354,21 @@ public class ProfileController(
Eft = new OtherProfileSubStats
{
TotalInGameTime = profileToViewPmc.Stats.Eft.TotalInGameTime,
OverAllCounters = profileToViewPmc.Stats.Eft.OverallCounters
}
OverAllCounters = profileToViewPmc.Stats.Eft.OverallCounters,
},
},
ScavStats = new OtherProfileStats
{
Eft = new OtherProfileSubStats
{
TotalInGameTime = profileToViewScav.Stats.Eft.TotalInGameTime,
OverAllCounters = profileToViewScav.Stats.Eft.OverallCounters
}
OverAllCounters = profileToViewScav.Stats.Eft.OverallCounters,
},
},
Hideout = profileToViewPmc.Hideout,
CustomizationStash = profileToViewPmc.Inventory.HideoutCustomizationStashId,
HideoutAreaStashes = profileToViewPmc.Inventory.HideoutAreaStashes,
Items = itemsToReturn
Items = itemsToReturn,
};
return profile;
@@ -14,7 +14,6 @@ using SPTarkov.Server.Core.Utils;
using SPTarkov.Server.Core.Utils.Cloners;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
@@ -63,13 +62,19 @@ public class QuestController(
/// <param name="acceptedQuest">Quest accepted</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AcceptQuest(PmcData pmcData, AcceptQuestRequestData acceptedQuest, string sessionID)
public ItemEventRouterResponse AcceptQuest(
PmcData pmcData,
AcceptQuestRequestData acceptedQuest,
string sessionID
)
{
var acceptQuestResponse = _eventOutputHolder.GetOutput(sessionID);
// Does quest exist in profile
// Restarting a failed quest can mean quest exists in profile
var existingQuestStatus = pmcData.Quests.FirstOrDefault(x => x.QId == acceptedQuest.QuestId);
var existingQuestStatus = pmcData.Quests.FirstOrDefault(x =>
x.QId == acceptedQuest.QuestId
);
if (existingQuestStatus is not null)
{
// Update existing
@@ -81,7 +86,11 @@ public class QuestController(
else
{
// Add new quest to server profile
var newQuest = _questHelper.GetQuestReadyForProfile(pmcData, QuestStatusEnum.Started, acceptedQuest);
var newQuest = _questHelper.GetQuestReadyForProfile(
pmcData,
QuestStatusEnum.Started,
acceptedQuest
);
pmcData.Quests.Add(newQuest);
}
@@ -94,10 +103,10 @@ public class QuestController(
AddTaskConditionCountersToProfile(
questFromDb.Conditions.AvailableForFinish,
pmcData,
acceptedQuest.QuestId);
acceptedQuest.QuestId
);
}
// Get messageId of text to send to player as text message in game
var messageId = _questHelper.GetMessageIdForQuestStart(
questFromDb.StartedMessageText,
@@ -120,7 +129,9 @@ public class QuestController(
MessageType.QuestStart,
messageId,
startedQuestRewardItems.ToList(),
_timeUtil.GetHoursAsSeconds((int) _questHelper.GetMailItemRedeemTimeHoursForProfile(pmcData))
_timeUtil.GetHoursAsSeconds(
(int)_questHelper.GetMailItemRedeemTimeHoursForProfile(pmcData)
)
);
// Having accepted new quest, look for newly unlocked quests and inform client of them
@@ -142,7 +153,11 @@ public class QuestController(
/// <param name="questConditions">Conditions to iterate over and possibly add to profile</param>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="questId">Quest where conditions originated</param>
protected void AddTaskConditionCountersToProfile(List<QuestCondition> questConditions, PmcData pmcData, string questId)
protected void AddTaskConditionCountersToProfile(
List<QuestCondition> questConditions,
PmcData pmcData,
string questId
)
{
foreach (var condition in questConditions)
{
@@ -161,7 +176,7 @@ public class QuestController(
Id = condition.Id,
SourceId = questId,
Type = condition.ConditionType,
Value = 0
Value = 0,
};
break;
}
@@ -178,7 +193,11 @@ public class QuestController(
/// <param name="request">Complete quest request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse CompleteQuest(PmcData pmcData, CompleteQuestRequestData request, string sessionId)
public ItemEventRouterResponse CompleteQuest(
PmcData pmcData,
CompleteQuestRequestData request,
string sessionId
)
{
return _questHelper.CompleteQuest(pmcData, request, sessionId);
}
@@ -191,7 +210,11 @@ public class QuestController(
/// <param name="request">Handover request</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse HandoverQuest(PmcData pmcData, HandoverQuestRequestData request, string sessionID)
public ItemEventRouterResponse HandoverQuest(
PmcData pmcData,
HandoverQuestRequestData request,
string sessionID
)
{
var quest = _questHelper.GetQuestFromDb(request.QuestId, pmcData);
List<string> handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
@@ -202,7 +225,11 @@ public class QuestController(
// Decrement number of items handed in
QuestCondition? handoverRequirements = null;
foreach (var condition in quest.Conditions.AvailableForFinish.Where(condition => condition.Id == request.ConditionId))
foreach (
var condition in quest.Conditions.AvailableForFinish.Where(condition =>
condition.Id == request.ConditionId
)
)
{
// Not a handover quest type, skip
if (!handoverQuestTypes.Contains(condition.ConditionType))
@@ -216,7 +243,7 @@ public class QuestController(
if (pmcData.TaskConditionCounters.TryGetValue("ConditionId", out var counter))
{
handedInCount -= (int) (counter.Value ?? 0);
handedInCount -= (int)(counter.Value ?? 0);
if (handedInCount <= 0)
{
@@ -228,7 +255,7 @@ public class QuestController(
questId = request.QuestId,
conditionId = request.ConditionId,
profileCounter = counter.Value,
value = handedInCount
value = handedInCount,
}
)
);
@@ -242,15 +269,26 @@ public class QuestController(
if (isItemHandoverQuest && handedInCount == 0)
{
return ShowRepeatableQuestInvalidConditionError(request.QuestId, request.ConditionId, output);
return ShowRepeatableQuestInvalidConditionError(
request.QuestId,
request.ConditionId,
output
);
}
var totalItemCountToRemove = 0d;
foreach (var itemHandover in request.Items)
{
var matchingItemInProfile = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == itemHandover.Id);
if (!(matchingItemInProfile is not null && handoverRequirements.Target.List.Contains(matchingItemInProfile.Template)))
// Item handed in by player doesn't match what was requested
var matchingItemInProfile = pmcData.Inventory.Items.FirstOrDefault(item =>
item.Id == itemHandover.Id
);
if (
!(
matchingItemInProfile is not null
&& handoverRequirements.Target.List.Contains(matchingItemInProfile.Template)
)
)
// Item handed in by player doesn't match what was requested
{
return ShowQuestItemHandoverMatchError(
request,
@@ -261,7 +299,10 @@ public class QuestController(
}
// Remove the right quantity of given items
var itemCountToRemove = Math.Min(itemHandover.Count ?? 0, handedInCount - totalItemCountToRemove);
var itemCountToRemove = Math.Min(
itemHandover.Count ?? 0,
handedInCount - totalItemCountToRemove
);
totalItemCountToRemove += itemCountToRemove;
if (itemHandover.Count - itemCountToRemove > 0)
{
@@ -269,7 +310,7 @@ public class QuestController(
_questHelper.ChangeItemStack(
pmcData,
itemHandover.Id,
(int) (itemHandover.Count - itemCountToRemove),
(int)(itemHandover.Count - itemCountToRemove),
sessionID,
output
);
@@ -283,17 +324,16 @@ public class QuestController(
else
{
// Remove item with children
var toRemove = _itemHelper.FindAndReturnChildrenByItems(pmcData.Inventory.Items, itemHandover.Id);
var toRemove = _itemHelper.FindAndReturnChildrenByItems(
pmcData.Inventory.Items,
itemHandover.Id
);
var index = pmcData.Inventory.Items.Count;
// Important: don't tell the client to remove the attachments, it will handle it
output.ProfileChanges[sessionID]
.Items.DeletedItems.Add(
new Item
{
Id = itemHandover.Id
}
);
output
.ProfileChanges[sessionID]
.Items.DeletedItems.Add(new Item { Id = itemHandover.Id });
// Important: loop backward when removing items from the array we're looping on
while (index-- > 0)
@@ -315,7 +355,7 @@ public class QuestController(
childItems.RemoveAt(0); // Remove the parent
// Sort by the current `location` and update
childItems.Sort((a, b) => (int) a.Location > (int) b.Location ? 1 : -1);
childItems.Sort((a, b) => (int)a.Location > (int)b.Location ? 1 : -1);
for (var i = 0; i < childItems.Count; i++)
{
@@ -344,15 +384,15 @@ public class QuestController(
/// <param name="conditionId">Relevant condition id that failed</param>
/// <param name="output">Client response</param>
/// <returns>ItemEventRouterResponse</returns>
protected ItemEventRouterResponse ShowRepeatableQuestInvalidConditionError(string questId, string conditionId, ItemEventRouterResponse output)
protected ItemEventRouterResponse ShowRepeatableQuestInvalidConditionError(
string questId,
string conditionId,
ItemEventRouterResponse output
)
{
var errorMessage = _localisationService.GetText(
"repeatable-quest_handover_failed_condition_invalid",
new
{
questId,
conditionId
}
new { questId, conditionId }
);
_logger.Error(errorMessage);
@@ -367,8 +407,12 @@ public class QuestController(
/// <param name="handoverRequirements">Quest handover requirements</param>
/// <param name="output">Response to send to user</param>
/// <returns>ItemEventRouterResponse</returns>
protected ItemEventRouterResponse ShowQuestItemHandoverMatchError(HandoverQuestRequestData handoverQuestRequest, Item? itemHandedOver,
QuestCondition? handoverRequirements, ItemEventRouterResponse output)
protected ItemEventRouterResponse ShowQuestItemHandoverMatchError(
HandoverQuestRequestData handoverQuestRequest,
Item? itemHandedOver,
QuestCondition? handoverRequirements,
ItemEventRouterResponse output
)
{
var errorMessage = _localisationService.GetText(
"quest-handover_wrong_item",
@@ -376,7 +420,7 @@ public class QuestController(
{
questId = handoverQuestRequest.QuestId,
handedInTpl = itemHandedOver?.Template ?? "UNKNOWN",
requiredTpl = handoverRequirements.Target.List.FirstOrDefault()
requiredTpl = handoverRequirements.Target.List.FirstOrDefault(),
}
);
_logger.Error(errorMessage);
@@ -392,7 +436,12 @@ public class QuestController(
/// <param name="conditionId">Backend counter id to update</param>
/// <param name="questId">Quest id counter is associated with</param>
/// <param name="counterValue">Value to increment the backend counter with</param>
protected void UpdateProfileTaskConditionCounterValue(PmcData pmcData, string conditionId, string questId, double counterValue)
protected void UpdateProfileTaskConditionCounterValue(
PmcData pmcData,
string conditionId,
string questId,
double counterValue
)
{
if (pmcData.TaskConditionCounters.GetValueOrDefault(conditionId) != null)
{
@@ -401,13 +450,16 @@ public class QuestController(
return;
}
pmcData.TaskConditionCounters.Add(conditionId, new TaskConditionCounter
{
Id = conditionId,
SourceId = questId,
Type = "HandoverItem",
Value = counterValue
});
pmcData.TaskConditionCounters.Add(
conditionId,
new TaskConditionCounter
{
Id = conditionId,
SourceId = questId,
Type = "HandoverItem",
Value = counterValue,
}
);
}
/// <summary>
@@ -418,7 +470,12 @@ public class QuestController(
/// <param name="sessionID">Session/Player id</param>
/// <param name="output"></param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse FailQuest(PmcData pmcData, FailQuestRequestData request, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse FailQuest(
PmcData pmcData,
FailQuestRequestData request,
string sessionID,
ItemEventRouterResponse output
)
{
_questHelper.FailQuest(pmcData, request, sessionID, output);
@@ -114,8 +114,9 @@ public class RagfairController
// Check profile is capable of creating offers
var pmcProfile = profile.CharacterData.PmcData;
if (
pmcProfile.RagfairInfo is not null &&
pmcProfile.Info.Level >= _databaseService.GetGlobals().Configuration.RagFair.MinUserLevel
pmcProfile.RagfairInfo is not null
&& pmcProfile.Info.Level
>= _databaseService.GetGlobals().Configuration.RagFair.MinUserLevel
)
{
_ragfairOfferHelper.ProcessOffersOnProfile(sessionId);
@@ -139,19 +140,28 @@ public class RagfairController
{
Offers = [],
OffersCount = searchRequest.Limit,
SelectedCategory = searchRequest.HandbookId
SelectedCategory = searchRequest.HandbookId,
};
// Get all offers ready for sorting/filtering below
result.Offers = GetOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, profile.CharacterData.PmcData);
result.Offers = GetOffersForSearchType(
searchRequest,
itemsToAdd,
traderAssorts,
profile.CharacterData.PmcData
);
// Client requested a category refresh
if (searchRequest.UpdateOfferCount.GetValueOrDefault(false))
{
result.Categories = GetSpecificCategories(profile.CharacterData.PmcData, searchRequest, result.Offers);
result.Categories = GetSpecificCategories(
profile.CharacterData.PmcData,
searchRequest,
result.Offers
);
}
// Adjust index value of offers found to start at 0
// Adjust index value of offers found to start at 0
AddIndexValueToOffers(result.Offers);
// Sort offers
@@ -171,7 +181,11 @@ public class RagfairController
}
// Update trader offers' values, Lock quest-linked offers + adjust offer buy limits
foreach (var traderOffer in result.Offers.Where(offer => _ragfairOfferHelper.OfferIsFromTrader(offer)))
foreach (
var traderOffer in result.Offers.Where(offer =>
_ragfairOfferHelper.OfferIsFromTrader(offer)
)
)
{
// For the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
// to true if the quest is not completed yet
@@ -239,11 +253,7 @@ public class RagfairController
_logger.Warning(
_localisationService.GetText(
"ragfair-unable_to_adjust_stack_count_assort_not_found",
new
{
offerId = offer.Items.First().Id,
traderId = offer.User.Id
}
new { offerId = offer.Items.First().Id, traderId = offer.User.Id }
)
);
@@ -288,7 +298,7 @@ public class RagfairController
// Get specific assort purchase data and set current purchase buy value
traderPurchases.TryGetValue(assortId, out var assortTraderPurchaseData);
offer.BuyRestrictionCurrent = (int?) assortTraderPurchaseData?.PurchaseCount ?? 0;
offer.BuyRestrictionCurrent = (int?)assortTraderPurchaseData?.PurchaseCount ?? 0;
offer.BuyRestrictionMax = offerRootItem.Upd.BuyRestrictionMax;
}
@@ -313,12 +323,16 @@ public class RagfairController
/// <param name="searchRequest">Client search request data</param>
/// <param name="offers">Ragfair offers to get categories for</param>
/// <returns>Record with templates + counts</returns>
protected Dictionary<string, int> GetSpecificCategories(PmcData pmcProfile, SearchRequestData searchRequest,
List<RagfairOffer> offers)
protected Dictionary<string, int> GetSpecificCategories(
PmcData pmcProfile,
SearchRequestData searchRequest,
List<RagfairOffer> offers
)
{
// Linked/required search categories
var playerHasFleaUnlocked =
pmcProfile.Info.Level >= _databaseService.GetGlobals().Configuration.RagFair.MinUserLevel;
pmcProfile.Info.Level
>= _databaseService.GetGlobals().Configuration.RagFair.MinUserLevel;
List<RagfairOffer> offerPool = [];
if (IsLinkedSearch(searchRequest) || IsRequiredSearch(searchRequest))
{
@@ -340,7 +354,11 @@ public class RagfairController
return new Dictionary<string, int>();
}
return _ragfairServer.GetAllActiveCategories(playerHasFleaUnlocked, searchRequest, offerPool);
return _ragfairServer.GetAllActiveCategories(
playerHasFleaUnlocked,
searchRequest,
offerPool
);
}
/// <summary>
@@ -371,14 +389,22 @@ public class RagfairController
/// <param name="traderAssorts">Trader assorts</param>
/// <param name="pmcProfile"></param>
/// <returns>Array of offers</returns>
protected List<RagfairOffer> GetOffersForSearchType(SearchRequestData searchRequest, List<string> itemsToAdd,
protected List<RagfairOffer> GetOffersForSearchType(
SearchRequestData searchRequest,
List<string> itemsToAdd,
Dictionary<string, TraderAssort> traderAssorts,
PmcData pmcProfile)
PmcData pmcProfile
)
{
// Searching for items in preset menu
if (searchRequest.BuildCount > 0)
{
return _ragfairOfferHelper.GetOffersForBuild(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
return _ragfairOfferHelper.GetOffersForBuild(
searchRequest,
itemsToAdd,
traderAssorts,
pmcProfile
);
}
if (searchRequest.NeededSearchId?.Length > 0)
@@ -387,7 +413,12 @@ public class RagfairController
}
// Searching for general items
return _ragfairOfferHelper.GetValidOffers(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
return _ragfairOfferHelper.GetValidOffers(
searchRequest,
itemsToAdd,
traderAssorts,
pmcProfile
);
}
/// <summary>
@@ -396,8 +427,10 @@ public class RagfairController
/// <param name="getPriceRequest">Client request object</param>
/// <param name="ignoreTraderOffers">OPTIONAL - Should trader offers be ignored in the calculation</param>
/// <returns>min/avg/max values for an item based on flea offers available</returns>
public GetItemPriceResult GetItemMinAvgMaxFleaPriceValues(GetMarketPriceRequestData getPriceRequest,
bool ignoreTraderOffers = true)
public GetItemPriceResult GetItemMinAvgMaxFleaPriceValues(
GetMarketPriceRequestData getPriceRequest,
bool ignoreTraderOffers = true
)
{
// Get all items of tpl
var offers = _ragfairOfferService.GetOffersOfType(getPriceRequest.TemplateId);
@@ -415,7 +448,7 @@ public class RagfairController
{
Avg = Math.Round(average),
Min = minMax.Min,
Max = minMax.Max
Max = minMax.Max,
};
}
@@ -431,11 +464,15 @@ public class RagfairController
{
Avg = tplPrice,
Min = tplPrice,
Max = tplPrice
Max = tplPrice,
};
}
protected double GetAveragePriceFromOffers(List<RagfairOffer> offers, MinMax<double> minMax, bool ignoreTraderOffers)
protected double GetAveragePriceFromOffers(
List<RagfairOffer> offers,
MinMax<double> minMax,
bool ignoreTraderOffers
)
{
var sum = 0d;
var totalOfferCount = 0;
@@ -488,14 +525,21 @@ public class RagfairController
/// <param name="offerRequest">Flea list creation offer</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AddPlayerOffer(PmcData pmcData, AddOfferRequestData offerRequest, string sessionID)
public ItemEventRouterResponse AddPlayerOffer(
PmcData pmcData,
AddOfferRequestData offerRequest,
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
var fullProfile = _profileHelper.GetFullProfile(sessionID);
if (!IsValidPlayerOfferRequest(offerRequest))
{
return _httpResponseUtil.AppendErrorToOutput(output, "Unable to add offer, check server for error");
return _httpResponseUtil.AppendErrorToOutput(
output,
"Unable to add offer, check server for error"
);
}
var typeOfOffer = GetOfferType(offerRequest);
@@ -540,7 +584,9 @@ public class RagfairController
if (offerRequest.Requirements is null)
{
_logger.Error(_localisationService.GetText("ragfair-unable_to_place_offer_with_no_requirements"));
_logger.Error(
_localisationService.GetText("ragfair-unable_to_place_offer_with_no_requirements")
);
return false;
}
@@ -588,8 +634,12 @@ public class RagfairController
/// <param name="fullProfile">Full profile of player</param>
/// <param name="output">output Response to send to client</param>
/// <returns>ItemEventRouterResponse</returns>
protected ItemEventRouterResponse CreateMultiOffer(string sessionID, AddOfferRequestData offerRequest,
SptProfile fullProfile, ItemEventRouterResponse output)
protected ItemEventRouterResponse CreateMultiOffer(
string sessionID,
AddOfferRequestData offerRequest,
SptProfile fullProfile,
ItemEventRouterResponse output
)
{
var pmcData = fullProfile.CharacterData.PmcData;
// var itemsToListCount = offerRequest.Items.Count; // Wasn't used to commented out for now // Does not count stack size, only items
@@ -625,17 +675,18 @@ public class RagfairController
var rootOfferItem = offer.Items.First(x => x.Id == firstOfferItemId);
// Average offer price for single item (or whole weapon)
var averages =
GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData
{
TemplateId = offer.Items[0].Template
}
);
var averages = GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData { TemplateId = offer.Items[0].Template }
);
// Check for and apply item price modifer if it exists in config
var averageOfferPrice = averages.Avg;
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(rootOfferItem.Template, out var itemPriceModifer))
if (
_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(
rootOfferItem.Template,
out var itemPriceModifer
)
)
{
averageOfferPrice *= itemPriceModifer;
}
@@ -657,7 +708,7 @@ public class RagfairController
);
// Create array of sell times for items listed
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int) stackCountTotal);
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int)stackCountTotal);
// Subtract flea market fee from stash
if (_ragfairConfig.Sell.Fees)
@@ -667,7 +718,7 @@ public class RagfairController
rootOfferItem,
pmcData,
playerListedPriceInRub,
(int) stackCountTotal,
(int)stackCountTotal,
offerRequest,
output
);
@@ -700,8 +751,12 @@ public class RagfairController
/// <param name="fullProfile">Full profile of player</param>
/// <param name="output">Response to send to client</param>
/// <returns>ItemEventRouterResponse</returns>
protected ItemEventRouterResponse CreatePackOffer(string sessionID, AddOfferRequestData offerRequest,
SptProfile fullProfile, ItemEventRouterResponse output)
protected ItemEventRouterResponse CreatePackOffer(
string sessionID,
AddOfferRequestData offerRequest,
SptProfile fullProfile,
ItemEventRouterResponse output
)
{
var pmcData = fullProfile.CharacterData.PmcData;
// var itemsToListCount = offerRequest.Items.Count; // TODO: Wasn't used so commented out for now // Does not count stack size, only items
@@ -730,22 +785,29 @@ public class RagfairController
firstListingRootItem.Upd.StackObjectsCount = stackCountTotal;
// Create flea object
var offer = CreatePlayerOffer(sessionID, offerRequest.Requirements, firstListingAndChildren, true);
var offer = CreatePlayerOffer(
sessionID,
offerRequest.Requirements,
firstListingAndChildren,
true
);
// This is the item that will be listed on flea, has merged stackObjectCount
var newRootOfferItem = offer.Items[0]; // TODO: add logic like single/multi offers to find root item
// Single price for an item
var averages = GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData
{
TemplateId = firstListingRootItem.Template
}
new GetMarketPriceRequestData { TemplateId = firstListingRootItem.Template }
);
var singleItemPrice = averages.Avg;
// Check for and apply item price modifer if it exists in config
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(newRootOfferItem.Template, out var itemPriceModifer))
if (
_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(
newRootOfferItem.Template,
out var itemPriceModifer
)
)
{
singleItemPrice *= itemPriceModifer;
}
@@ -767,7 +829,11 @@ public class RagfairController
);
// Create array of sell times for items listed + sell all at once as it's a pack
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int) stackCountTotal, true);
offer.SellResults = _ragfairSellHelper.RollForSale(
sellChancePercent,
(int)stackCountTotal,
true
);
// Subtract flea market fee from stash
if (_ragfairConfig.Sell.Fees)
@@ -777,7 +843,7 @@ public class RagfairController
newRootOfferItem,
pmcData,
playerListedPriceInRub,
(int) stackCountTotal,
(int)stackCountTotal,
offerRequest,
output
);
@@ -809,22 +875,30 @@ public class RagfairController
/// <param name="fullProfile">Full profile of player</param>
/// <param name="output">Response to send to client</param>
/// <returns>ItemEventRouterResponse</returns>
protected ItemEventRouterResponse CreateSingleOffer(string sessionID, AddOfferRequestData offerRequest,
protected ItemEventRouterResponse CreateSingleOffer(
string sessionID,
AddOfferRequestData offerRequest,
SptProfile fullProfile,
ItemEventRouterResponse output)
ItemEventRouterResponse output
)
{
var pmcData = fullProfile.CharacterData.PmcData;
// var itemsToListCount = offerRequest.Items.Count; // Wasn't used so commented out for now // Does not count stack size, only items
// Find items to be listed on flea from player inventory
var inventoryItemsToSell = GetItemsToListOnFleaFromInventory(pmcData, offerRequest.Items);
if (inventoryItemsToSell.Items is null || !string.IsNullOrEmpty(inventoryItemsToSell.ErrorMessage))
if (
inventoryItemsToSell.Items is null
|| !string.IsNullOrEmpty(inventoryItemsToSell.ErrorMessage)
)
{
_httpResponseUtil.AppendErrorToOutput(output, inventoryItemsToSell.ErrorMessage);
}
// Total count of items summed using their stack counts
var stackCountTotal = _ragfairOfferHelper.GetTotalStackCountSize(inventoryItemsToSell.Items);
var stackCountTotal = _ragfairOfferHelper.GetTotalStackCountSize(
inventoryItemsToSell.Items
);
// Checks are done, create offer
var playerListedPriceInRub = CalculateRequirementsPriceInRub(offerRequest.Requirements);
@@ -841,17 +915,18 @@ public class RagfairController
var qualityMultiplier = _itemHelper.GetItemQualityModifierForItems(offer.Items, true);
// Average offer price for single item (or whole weapon)
var averages =
GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData
{
TemplateId = offerRootItem.Template
}
);
var averages = GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData { TemplateId = offerRootItem.Template }
);
var averageOfferPriceSingleItem = averages.Avg;
// Check for and apply item price modifer if it exists in config
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(offerRootItem.Template, out var itemPriceModifer))
if (
_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(
offerRootItem.Template,
out var itemPriceModifer
)
)
{
averageOfferPriceSingleItem *= itemPriceModifer;
}
@@ -865,7 +940,7 @@ public class RagfairController
playerListedPriceInRub,
qualityMultiplier
);
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int) stackCountTotal);
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int)stackCountTotal);
// Subtract flea market fee from stash
if (_ragfairConfig.Sell.Fees)
@@ -875,7 +950,7 @@ public class RagfairController
offerRootItem,
pmcData,
playerListedPriceInRub,
(int) stackCountTotal,
(int)stackCountTotal,
offerRequest,
output
);
@@ -916,10 +991,13 @@ public class RagfairController
double requirementsPriceInRub,
int itemStackCount,
AddOfferRequestData offerRequest,
ItemEventRouterResponse output)
ItemEventRouterResponse output
)
{
// Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate)
var storedClientTaxValue = _ragfairTaxService.GetStoredClientOfferTaxValueById(offerRequest.Items[0]);
var storedClientTaxValue = _ragfairTaxService.GetStoredClientOfferTaxValueById(
offerRequest.Items[0]
);
var tax = storedClientTaxValue is not null
? storedClientTaxValue.Fee
: _ragfairTaxService.CalculateTax(
@@ -932,7 +1010,9 @@ public class RagfairController
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Offer tax to charge: {tax}, pulled from client: {storedClientTaxValue.Count is not null}");
_logger.Debug(
$"Offer tax to charge: {tax}, pulled from client: {storedClientTaxValue.Count is not null}"
);
}
// cleanup of cache now we've used the tax value from it
@@ -960,32 +1040,34 @@ public class RagfairController
/// <param name="items">Item(s) to list on flea (with children)</param>
/// <param name="sellInOnePiece">Is this a pack offer</param>
/// <returns>RagfairOffer</returns>
protected RagfairOffer CreatePlayerOffer(string sessionId, List<Requirement> requirements, List<Item> items,
bool sellInOnePiece)
protected RagfairOffer CreatePlayerOffer(
string sessionId,
List<Requirement> requirements,
List<Item> items,
bool sellInOnePiece
)
{
const int loyalLevel = 1;
var formattedItems = items.Select(item =>
{
var isChild = items.Any(subItem => subItem.Id == item.ParentId);
{
var isChild = items.Any(subItem => subItem.Id == item.ParentId);
return new Item
{
Id = item.Id,
Template = item.Template,
ParentId = isChild ? item.ParentId : "hideout",
SlotId = isChild ? item.SlotId : "hideout",
Upd = item.Upd
};
}
);
return new Item
{
Id = item.Id,
Template = item.Template,
ParentId = isChild ? item.ParentId : "hideout",
SlotId = isChild ? item.SlotId : "hideout",
Upd = item.Upd,
};
});
var formattedRequirements = requirements.Select(item => new BarterScheme
{
Template = item.Template,
Count = item.Count,
OnlyFunctional = item.OnlyFunctional
}
);
{
Template = item.Template,
Count = item.Count,
OnlyFunctional = item.OnlyFunctional,
});
return _ragfairOfferGenerator.CreateAndAddFleaOffer(
sessionId,
@@ -993,7 +1075,7 @@ public class RagfairController
formattedItems.ToList(),
formattedRequirements.ToList(),
loyalLevel,
(int?) items.FirstOrDefault()?.Upd?.StackObjectsCount ?? 1,
(int?)items.FirstOrDefault()?.Upd?.StackObjectsCount ?? 1,
sellInOnePiece
);
}
@@ -1006,17 +1088,21 @@ public class RagfairController
protected double CalculateRequirementsPriceInRub(List<Requirement> requirements)
{
return requirements.Sum(requirement =>
{
if (
string.IsNullOrEmpty(requirement.Template)
|| !requirement.Count.HasValue
|| requirement.Count == 0
)
{
if (string.IsNullOrEmpty(requirement.Template) || !requirement.Count.HasValue || requirement.Count == 0)
{
return 0;
}
return _paymentHelper.IsMoneyTpl(requirement.Template)
? _handbookHelper.InRUB(requirement.Count.Value, requirement.Template)
: _itemHelper.GetDynamicItemPrice(requirement.Template).Value * requirement.Count.Value;
return 0;
}
);
return _paymentHelper.IsMoneyTpl(requirement.Template)
? _handbookHelper.InRUB(requirement.Count.Value, requirement.Template)
: _itemHelper.GetDynamicItemPrice(requirement.Template).Value
* requirement.Count.Value;
});
}
/// <summary>
@@ -1025,8 +1111,10 @@ public class RagfairController
/// <param name="pmcData">Players PMC profile</param>
/// <param name="itemIdsFromFleaOfferRequest">Request</param>
/// <returns>GetItemsToListOnFleaFromInventoryResult</returns>
protected GetItemsToListOnFleaFromInventoryResult GetItemsToListOnFleaFromInventory(PmcData pmcData,
List<string> itemIdsFromFleaOfferRequest)
protected GetItemsToListOnFleaFromInventoryResult GetItemsToListOnFleaFromInventory(
PmcData pmcData,
List<string> itemIdsFromFleaOfferRequest
)
{
List<List<Item>> itemsToReturn = [];
var errorMessage = string.Empty;
@@ -1039,40 +1127,38 @@ public class RagfairController
{
errorMessage = _localisationService.GetText(
"ragfair-unable_to_find_item_in_inventory",
new
{
id = itemId
}
new { id = itemId }
);
_logger.Error(errorMessage);
return new GetItemsToListOnFleaFromInventoryResult
{
Items = itemsToReturn,
ErrorMessage = errorMessage
ErrorMessage = errorMessage,
};
}
_itemHelper.FixItemStackCount(rootItem);
itemsToReturn.Add(_itemHelper.FindAndReturnChildrenAsItems(pmcData.Inventory.Items, itemId));
itemsToReturn.Add(
_itemHelper.FindAndReturnChildrenAsItems(pmcData.Inventory.Items, itemId)
);
}
if (itemsToReturn?.Count == 0)
{
errorMessage = _localisationService.GetText("ragfair-unable_to_find_requested_items_in_inventory");
errorMessage = _localisationService.GetText(
"ragfair-unable_to_find_requested_items_in_inventory"
);
_logger.Error(errorMessage);
return new GetItemsToListOnFleaFromInventoryResult
{
ErrorMessage = errorMessage
};
return new GetItemsToListOnFleaFromInventoryResult { ErrorMessage = errorMessage };
}
return new GetItemsToListOnFleaFromInventoryResult
{
Items = itemsToReturn,
ErrorMessage = errorMessage
ErrorMessage = errorMessage,
};
}
@@ -1094,11 +1180,7 @@ public class RagfairController
_logger.Warning(
_localisationService.GetText(
"ragfair-unable_to_remove_offer_not_found_in_profile",
new
{
profileId = sessionId,
offerId
}
new { profileId = sessionId, offerId }
)
);
@@ -1109,13 +1191,7 @@ public class RagfairController
if (playerOffer is null)
{
_logger.Error(
_localisationService.GetText(
"ragfair-offer_not_found_in_profile",
new
{
offerId
}
)
_localisationService.GetText("ragfair-offer_not_found_in_profile", new { offerId })
);
return _httpResponseUtil.AppendErrorToOutput(
@@ -1130,10 +1206,12 @@ public class RagfairController
{
// `expireSeconds` Default is 71 seconds
var newEndTime = _ragfairConfig.Sell.ExpireSeconds + _timeUtil.GetTimeStamp();
playerOffer.EndTime = (long?) Math.Round((double) newEndTime);
playerOffer.EndTime = (long?)Math.Round((double)newEndTime);
}
_logger.Debug($"Flagged player offer: {offerId} for expiry in: {TimeSpan.FromTicks(playerOffer.EndTime.Value).ToString()}");
_logger.Debug(
$"Flagged player offer: {offerId} for expiry in: {TimeSpan.FromTicks(playerOffer.EndTime.Value).ToString()}"
);
return output;
}
@@ -1144,7 +1222,10 @@ public class RagfairController
/// <param name="extendRequest">Extend time request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse ExtendOffer(ExtendOfferRequestData extendRequest, string sessionId)
public ItemEventRouterResponse ExtendOffer(
ExtendOfferRequestData extendRequest,
string sessionId
)
{
var output = _eventOutputHolder.GetOutput(sessionId);
@@ -1158,10 +1239,7 @@ public class RagfairController
_logger.Warning(
_localisationService.GetText(
"ragfair-offer_not_found_in_profile",
new
{
offerId = extendRequest.OfferId
}
new { offerId = extendRequest.OfferId }
)
);
return _httpResponseUtil.AppendErrorToOutput(
@@ -1179,7 +1257,8 @@ public class RagfairController
var sellInOncePiece = playerOffer.SellInOnePiece.GetValueOrDefault(false);
if (!sellInOncePiece)
{
count = (int) playerOffer.Items.Sum(offerItem => offerItem.Upd?.StackObjectsCount ?? 0);
count = (int)
playerOffer.Items.Sum(offerItem => offerItem.Upd?.StackObjectsCount ?? 0);
}
var tax = _ragfairTaxService.CalculateTax(
@@ -1202,7 +1281,7 @@ public class RagfairController
}
// Add extra time to offer
playerOffers[playerOfferIndex].EndTime += (long?) Math.Round((decimal) secondsToAdd);
playerOffers[playerOfferIndex].EndTime += (long?)Math.Round((decimal)secondsToAdd);
return output;
}
@@ -1213,7 +1292,10 @@ public class RagfairController
/// <param name="currency">What currency: RUB, EURO, USD</param>
/// <param name="value">Amount of currency</param>
/// <returns>ProcessBuyTradeRequestData</returns>
protected ProcessBuyTradeRequestData CreateBuyTradeRequestObject(CurrencyType currency, double value)
protected ProcessBuyTradeRequestData CreateBuyTradeRequestObject(
CurrencyType currency,
double value
)
{
return new ProcessBuyTradeRequestData
{
@@ -1224,13 +1306,13 @@ public class RagfairController
new IdWithCount
{
Id = _paymentHelper.GetCurrency(currency),
Count = Math.Round(value)
}
Count = Math.Round(value),
},
],
Type = "",
ItemId = "",
Count = 0,
SchemeId = 0
SchemeId = 0,
};
}
@@ -1258,16 +1340,8 @@ public class RagfairController
public record GetItemsToListOnFleaFromInventoryResult
{
public List<List<Item>>? Items
{
get;
set;
}
public List<List<Item>>? Items { get; set; }
public string? ErrorMessage
{
get;
set;
}
public string? ErrorMessage { get; set; }
}
}
@@ -8,10 +8,7 @@ using SPTarkov.Server.Core.Services;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class RepairController(
EventOutputHolder _eventOutputHolder,
RepairService _repairService
)
public class RepairController(EventOutputHolder _eventOutputHolder, RepairService _repairService)
{
/// <summary>
/// Handle TraderRepair event
@@ -24,14 +21,20 @@ public class RepairController(
public ItemEventRouterResponse TraderRepair(
string sessionID,
TraderRepairActionDataRequest body,
PmcData pmcData)
PmcData pmcData
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
// find the item to repair
foreach (var repairItem in body.RepairItems)
{
var repairDetails = _repairService.RepairItemByTrader(sessionID, pmcData, repairItem, body.TId);
var repairDetails = _repairService.RepairItemByTrader(
sessionID,
pmcData,
repairItem,
body.TId
);
_repairService.PayForRepair(
sessionID,
@@ -68,7 +71,8 @@ public class RepairController(
public ItemEventRouterResponse RepairWithKit(
string sessionId,
RepairActionDataRequest body,
PmcData pmcData)
PmcData pmcData
)
{
var output = _eventOutputHolder.GetOutput(sessionId);
@@ -53,7 +53,11 @@ public class RepeatableQuestController(
/// <param name="acceptedQuest">Repeatable quest accepted</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AcceptRepeatableQuest(PmcData pmcData, AcceptQuestRequestData acceptedQuest, string sessionID)
public ItemEventRouterResponse AcceptRepeatableQuest(
PmcData pmcData,
AcceptQuestRequestData acceptedQuest,
string sessionID
)
{
// Create and store quest status object inside player profile
var newRepeatableQuest = _questHelper.GetQuestReadyForProfile(
@@ -74,11 +78,16 @@ public class RepeatableQuestController(
)
);
throw new Exception(_localisationService.GetText("repeatable-unable_to_accept_quest_see_log"));
throw new Exception(
_localisationService.GetText("repeatable-unable_to_accept_quest_see_log")
);
}
// Some scav quests need to be added to scav profile for them to show up in-raid
if (repeatableQuestProfile.Side == "Scav" && _questTypes.Contains(repeatableQuestProfile.Type.ToString()))
if (
repeatableQuestProfile.Side == "Scav"
&& _questTypes.Contains(repeatableQuestProfile.Type.ToString())
)
{
var fullProfile = _profileHelper.GetFullProfile(sessionID);
@@ -98,8 +107,11 @@ public class RepeatableQuestController(
/// <param name="changeRequest">Change quest request</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse ChangeRepeatableQuest(PmcData pmcData, RepeatableQuestChangeRequest changeRequest,
string sessionID)
public ItemEventRouterResponse ChangeRepeatableQuest(
PmcData pmcData,
RepeatableQuestChangeRequest changeRequest,
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
@@ -112,7 +124,9 @@ public class RepeatableQuestController(
if (repeatables.RepeatableType is null || repeatables.Quest is null)
{
// Unable to find quest being replaced
var message = _localisationService.GetText("quest-unable_to_find_repeatable_to_replace");
var message = _localisationService.GetText(
"quest-unable_to_find_repeatable_to_replace"
);
_logger.Error(message);
return _httpResponseUtil.AppendErrorToOutput(output, message);
@@ -125,8 +139,8 @@ public class RepeatableQuestController(
var replacedQuestTraderId = questToReplace.TraderId;
// Update active quests to exclude the quest we're replacing
repeatablesOfTypeInProfile.ActiveQuests = repeatablesOfTypeInProfile.ActiveQuests.Where(quest => quest.Id != changeRequest.QuestId
)
repeatablesOfTypeInProfile.ActiveQuests = repeatablesOfTypeInProfile
.ActiveQuests.Where(quest => quest.Id != changeRequest.QuestId)
.ToList();
// Save for later cost calculations
@@ -138,7 +152,8 @@ public class RepeatableQuestController(
repeatablesOfTypeInProfile.ChangeRequirement.Remove(changeRequest.QuestId);
// Get config for this repeatable subtype (daily/weekly/scav)
var repeatableConfig = _questConfig.RepeatableQuests.FirstOrDefault(config => config.Name == repeatablesOfTypeInProfile.Name
var repeatableConfig = _questConfig.RepeatableQuests.FirstOrDefault(config =>
config.Name == repeatablesOfTypeInProfile.Name
);
// If the configuration dictates to replace with the same quest type, adjust the available quest types
@@ -148,7 +163,10 @@ public class RepeatableQuestController(
}
// Generate meta-data for what type/level range of quests can be generated for player
var allowedQuestTypes = GenerateQuestPool(repeatableConfig, pmcData.Info.Level.GetValueOrDefault(1));
var allowedQuestTypes = GenerateQuestPool(
repeatableConfig,
pmcData.Info.Level.GetValueOrDefault(1)
);
var newRepeatableQuest = AttemptToGenerateRepeatableQuest(
sessionID,
pmcData,
@@ -186,7 +204,7 @@ public class RepeatableQuestController(
repeatablesOfTypeInProfile.ChangeRequirement[newRepeatableQuest.Id] = new ChangeRequirement
{
ChangeCost = newRepeatableQuest.ChangeCost,
ChangeStandingCost = _randomUtil.GetArrayValue(repeatableConfig.StandingChangeCost)
ChangeStandingCost = _randomUtil.GetArrayValue(repeatableConfig.StandingChangeCost),
};
// Check if we should charge player for replacing quest
@@ -201,12 +219,22 @@ public class RepeatableQuestController(
var traderOfReplacedQuest = pmcData.TradersInfo[replacedQuestTraderId];
traderOfReplacedQuest.Standing -= previousChangeRequirement.ChangeStandingCost;
var charismaBonus = _profileHelper.GetSkillFromProfile(pmcData, SkillTypes.Charisma)?.Progress ?? 0;
var charismaBonus =
_profileHelper.GetSkillFromProfile(pmcData, SkillTypes.Charisma)?.Progress ?? 0;
foreach (var cost in previousChangeRequirement.ChangeCost)
{
// Not free, Charge player + apply charisma bonus to cost of replacement
cost.Count = (int) Math.Truncate(cost.Count.Value * (1 - Math.Truncate(charismaBonus / 100) * 0.001));
_paymentService.AddPaymentToOutput(pmcData, cost.TemplateId, cost.Count.Value, sessionID, output);
cost.Count = (int)
Math.Truncate(
cost.Count.Value * (1 - Math.Truncate(charismaBonus / 100) * 0.001)
);
_paymentService.AddPaymentToOutput(
pmcData,
cost.TemplateId,
cost.Count.Value,
sessionID,
output
);
if (output.Warnings.Count > 0)
{
return output;
@@ -265,8 +293,11 @@ public class RepeatableQuestController(
/// <param name="repeatableSubType">Can be daily / weekly / scav repeatable</param>
/// <param name="repeatableTypeName">Subtype of repeatable quest: daily / weekly / scav</param>
/// <returns>Is the repeatable being replaced for free</returns>
protected bool UseFreeRefreshIfAvailable(SptProfile? fullProfile, PmcDataRepeatableQuest repeatableSubType,
string repeatableTypeName)
protected bool UseFreeRefreshIfAvailable(
SptProfile? fullProfile,
PmcDataRepeatableQuest repeatableSubType,
string repeatableTypeName
)
{
// No free refreshes, exit early
if (repeatableSubType.FreeChangesAvailable <= 0)
@@ -305,12 +336,14 @@ public class RepeatableQuestController(
/// </summary>
/// <param name="repeatablesOfTypeInProfile">repeatables that have the replaced and new quest</param>
/// <param name="replacedQuestId">Id of the replaced quest</param>
protected void CleanUpRepeatableChangeRequirements(PmcDataRepeatableQuest repeatablesOfTypeInProfile,
string replacedQuestId)
protected void CleanUpRepeatableChangeRequirements(
PmcDataRepeatableQuest repeatablesOfTypeInProfile,
string replacedQuestId
)
{
if (repeatablesOfTypeInProfile.ActiveQuests.Count == 1)
// Only one repeatable quest being replaced (e.g. scav_daily), remove everything ready for new quest requirement to be added
// Will assist in cleanup of existing profiles data
// Only one repeatable quest being replaced (e.g. scav_daily), remove everything ready for new quest requirement to be added
// Will assist in cleanup of existing profiles data
{
repeatablesOfTypeInProfile.ChangeRequirement.Clear();
@@ -329,8 +362,12 @@ public class RepeatableQuestController(
/// <param name="questTypePool">What type/level range of quests can be generated for player</param>
/// <param name="repeatableConfig">Config for the quest type to generate</param>
/// <returns></returns>
protected RepeatableQuest? AttemptToGenerateRepeatableQuest(string sessionId, PmcData pmcData,
QuestTypePool questTypePool, RepeatableQuestConfig repeatableConfig)
protected RepeatableQuest? AttemptToGenerateRepeatableQuest(
string sessionId,
PmcData pmcData,
QuestTypePool questTypePool,
RepeatableQuestConfig repeatableConfig
)
{
const int maxAttempts = 10;
RepeatableQuest? newRepeatableQuest = null;
@@ -346,7 +383,7 @@ public class RepeatableQuestController(
);
if (newRepeatableQuest is not null)
// Successfully generated a quest, exit loop
// Successfully generated a quest, exit loop
{
break;
}
@@ -356,7 +393,12 @@ public class RepeatableQuestController(
if (attempts > maxAttempts)
{
_logger.Error(_localisationService.GetText("quest-repeatable_generation_failed_please_report", attempts));
_logger.Error(
_localisationService.GetText(
"quest-repeatable_generation_failed_please_report",
attempts
)
);
}
return newRepeatableQuest;
@@ -370,7 +412,10 @@ public class RepeatableQuestController(
protected void RemoveQuestFromProfile(SptProfile fullProfile, string questToReplaceId)
{
// Find quest we're replacing in pmc profile quests array and remove it
_questHelper.FindAndRemoveQuestFromArrayIfExists(questToReplaceId, fullProfile.CharacterData.PmcData.Quests);
_questHelper.FindAndRemoveQuestFromArrayIfExists(
questToReplaceId,
fullProfile.CharacterData.PmcData.Quests
);
// Look for and remove quest we're replacing in scav profile too
if (fullProfile.CharacterData.ScavData is not null)
@@ -393,10 +438,11 @@ public class RepeatableQuestController(
foreach (var repeatablesInProfile in pmcData.RepeatableQuests)
{
// Check for existing quest in (daily/weekly/scav arrays)
var questToReplace =
repeatablesInProfile.ActiveQuests?.FirstOrDefault(repeatable => repeatable.Id == questId);
var questToReplace = repeatablesInProfile.ActiveQuests?.FirstOrDefault(repeatable =>
repeatable.Id == questId
);
if (questToReplace is null)
// Not found, skip to next repeatable subtype
// Not found, skip to next repeatable subtype
{
continue;
}
@@ -404,7 +450,7 @@ public class RepeatableQuestController(
return new GetRepeatableByIdResult
{
Quest = questToReplace,
RepeatableType = repeatablesInProfile
RepeatableType = repeatablesInProfile,
};
}
@@ -443,12 +489,15 @@ public class RepeatableQuestController(
foreach (var repeatableConfig in _questConfig.RepeatableQuests)
{
// Get daily/weekly data from profile, add empty object if missing
var generatedRepeatables = GetRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
var generatedRepeatables = GetRepeatableQuestSubTypeFromProfile(
repeatableConfig,
pmcData
);
var repeatableTypeLower = repeatableConfig.Name.ToLower();
var canAccessRepeatables = CanProfileAccessRepeatableQuests(repeatableConfig, pmcData);
if (!canAccessRepeatables)
// Don't send any repeatables, even existing ones
// Don't send any repeatables, even existing ones
{
continue;
}
@@ -483,7 +532,10 @@ public class RepeatableQuestController(
ProcessExpiredQuests(generatedRepeatables, pmcData);
// Create dynamic quest pool to avoid generating duplicates
var questTypePool = GenerateQuestPool(repeatableConfig, pmcData.Info.Level.GetValueOrDefault(1));
var questTypePool = GenerateQuestPool(
repeatableConfig,
pmcData.Info.Level.GetValueOrDefault(1)
);
// Add repeatable quests of this loops sub-type (daily/weekly)
for (var i = 0; i < GetQuestCount(repeatableConfig, fullProfile); i++)
@@ -535,7 +587,9 @@ public class RepeatableQuestController(
new ChangeRequirement
{
ChangeCost = quest.ChangeCost,
ChangeStandingCost = _randomUtil.GetArrayValue(repeatableConfig.StandingChangeCost) // Randomise standing loss to replace
ChangeStandingCost = _randomUtil.GetArrayValue(
repeatableConfig.StandingChangeCost
), // Randomise standing loss to replace
}
);
}
@@ -553,7 +607,7 @@ public class RepeatableQuestController(
InactiveQuests = generatedRepeatables.InactiveQuests,
ChangeRequirement = generatedRepeatables.ChangeRequirement,
FreeChanges = generatedRepeatables.FreeChanges,
FreeChangesAvailable = generatedRepeatables.FreeChangesAvailable
FreeChangesAvailable = generatedRepeatables.FreeChangesAvailable,
}
);
}
@@ -567,11 +621,14 @@ public class RepeatableQuestController(
/// <param name="repeatableConfig">daily/weekly config</param>
/// <param name="pmcData">Players PMC profile</param>
/// <returns>PmcDataRepeatableQuest</returns>
protected PmcDataRepeatableQuest GetRepeatableQuestSubTypeFromProfile(RepeatableQuestConfig repeatableConfig,
PmcData pmcData)
protected PmcDataRepeatableQuest GetRepeatableQuestSubTypeFromProfile(
RepeatableQuestConfig repeatableConfig,
PmcData pmcData
)
{
// Get from profile, add if missing
var repeatableQuestDetails = pmcData.RepeatableQuests.FirstOrDefault(repeatable => repeatable.Name == repeatableConfig.Name
var repeatableQuestDetails = pmcData.RepeatableQuests.FirstOrDefault(repeatable =>
repeatable.Name == repeatableConfig.Name
);
var hasAccess = _profileHelper.HasAccessToRepeatableFreeRefreshSystem(pmcData);
@@ -586,7 +643,7 @@ public class RepeatableQuestController(
InactiveQuests = [],
EndTime = 0,
FreeChanges = hasAccess ? repeatableConfig.FreeChanges : 0,
FreeChangesAvailable = hasAccess ? repeatableConfig.FreeChangesAvailable : 0
FreeChangesAvailable = hasAccess ? repeatableConfig.FreeChangesAvailable : 0,
};
// Add base object that holds repeatable data to profile
@@ -610,10 +667,16 @@ public class RepeatableQuestController(
/// <param name="repeatableConfig">Repeatable quest config</param>
/// <param name="pmcData">Players PMC profile</param>
/// <returns>True if profile has access to repeatables</returns>
protected bool CanProfileAccessRepeatableQuests(RepeatableQuestConfig repeatableConfig, PmcData pmcData)
protected bool CanProfileAccessRepeatableQuests(
RepeatableQuestConfig repeatableConfig,
PmcData pmcData
)
{
// PMC and daily quests not unlocked yet
if (repeatableConfig.Side == "Pmc" && !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig))
if (
repeatableConfig.Side == "Pmc"
&& !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)
)
{
return false;
}
@@ -638,7 +701,10 @@ public class RepeatableQuestController(
/// <param name="pmcData">Players PMC profile</param>
/// <param name="repeatableConfig">Config of daily type to check</param>
/// <returns>True if unlocked</returns>
protected static bool PlayerHasDailyPmcQuestsUnlocked(PmcData pmcData, RepeatableQuestConfig repeatableConfig)
protected static bool PlayerHasDailyPmcQuestsUnlocked(
PmcData pmcData,
RepeatableQuestConfig repeatableConfig
)
{
return pmcData.Info.Level >= repeatableConfig.MinPlayerLevel;
}
@@ -650,9 +716,11 @@ public class RepeatableQuestController(
/// <returns>True if unlocked</returns>
protected bool PlayerHasDailyScavQuestsUnlocked(PmcData pmcData)
{
return pmcData?.Hideout?.Areas?.FirstOrDefault(hideoutArea => hideoutArea.Type == HideoutAreas.IntelligenceCenter)
?.Level >=
1;
return pmcData
?.Hideout?.Areas?.FirstOrDefault(hideoutArea =>
hideoutArea.Type == HideoutAreas.IntelligenceCenter
)
?.Level >= 1;
}
/// <summary>
@@ -660,12 +728,17 @@ public class RepeatableQuestController(
/// </summary>
/// <param name="generatedRepeatables">Repeatables to process (daily/weekly)</param>
/// <param name="pmcData">Players PMC profile</param>
protected void ProcessExpiredQuests(PmcDataRepeatableQuest generatedRepeatables, PmcData pmcData)
protected void ProcessExpiredQuests(
PmcDataRepeatableQuest generatedRepeatables,
PmcData pmcData
)
{
var questsToKeep = new List<RepeatableQuest>();
foreach (var activeQuest in generatedRepeatables.ActiveQuests)
{
var questStatusInProfile = pmcData.Quests.FirstOrDefault(quest => quest.QId == activeQuest.Id);
var questStatusInProfile = pmcData.Quests.FirstOrDefault(quest =>
quest.QId == activeQuest.Id
);
if (questStatusInProfile is null)
{
continue;
@@ -723,8 +796,15 @@ public class RepeatableQuestController(
// Add "any" to pickup quest pool
questPool.Pool.Pickup.Locations[ELocationName.any] = ["any"];
var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(_mathUtil, _cloner, eliminationConfig.Targets);
var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(
pmcLevel,
repeatableConfig
);
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
_mathUtil,
_cloner,
eliminationConfig.Targets
);
// Populate Elimination quest targets and their locations
foreach (var target in targetsConfig)
@@ -734,10 +814,7 @@ public class RepeatableQuestController(
{
questPool.Pool.Elimination.Targets.Add(
target.Key,
new TargetLocation
{
Locations = ["any"]
}
new TargetLocation { Locations = ["any"] }
);
continue;
@@ -747,16 +824,16 @@ public class RepeatableQuestController(
var possibleLocations = repeatableConfig.Locations.Keys;
var allowedLocations =
target.Key == "Savage"
? possibleLocations.Where(location => location != ELocationName.laboratory
) // Exclude labs for Savage targets.
? possibleLocations.Where(location => location != ELocationName.laboratory) // Exclude labs for Savage targets.
: possibleLocations;
questPool.Pool.Elimination.Targets.Add(
target.Key,
new TargetLocation
{
Locations = allowedLocations.Select(x => x.ToString()).ToList()
});
Locations = allowedLocations.Select(x => x.ToString()).ToList(),
}
);
}
return questPool;
@@ -776,17 +853,17 @@ public class RepeatableQuestController(
{
Exploration = new ExplorationPool
{
Locations = new Dictionary<ELocationName, List<string>>()
Locations = new Dictionary<ELocationName, List<string>>(),
},
Elimination = new EliminationPool
{
Targets = new Dictionary<string, TargetLocation>()
Targets = new Dictionary<string, TargetLocation>(),
},
Pickup = new ExplorationPool
{
Locations = new Dictionary<ELocationName, List<string>>()
}
}
Locations = new Dictionary<ELocationName, List<string>>(),
},
},
};
}
@@ -805,23 +882,25 @@ public class RepeatableQuestController(
}
// Add elite bonus to daily quests
if (string.Equals(repeatableConfig.Name, "daily", StringComparison.OrdinalIgnoreCase) &&
_profileHelper.HasEliteSkillLevel(SkillTypes.Charisma, fullProfile.CharacterData.PmcData))
// Elite charisma skill gives extra daily quest(s)
if (
string.Equals(repeatableConfig.Name, "daily", StringComparison.OrdinalIgnoreCase)
&& _profileHelper.HasEliteSkillLevel(
SkillTypes.Charisma,
fullProfile.CharacterData.PmcData
)
)
// Elite charisma skill gives extra daily quest(s)
{
questCount += _databaseService
.GetGlobals()
.Configuration
.SkillsSettings
.Charisma
.BonusSettings
.EliteBonusSettings
.RepeatableQuestExtraCount
.GetValueOrDefault(0);
.Configuration.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings.RepeatableQuestExtraCount.GetValueOrDefault(
0
);
}
// Add any extra repeatable quests the profile has unlocked
questCount += (int) fullProfile.SptData.ExtraRepeatableQuests.GetValueOrDefault(repeatableConfig.Id, 0);
questCount += (int)
fullProfile.SptData.ExtraRepeatableQuests.GetValueOrDefault(repeatableConfig.Id, 0);
return questCount;
}
@@ -50,7 +50,8 @@ public class TradeController(
public ItemEventRouterResponse ConfirmTrading(
PmcData pmcData,
ProcessBaseTradeRequestData request,
string sessionID)
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
@@ -58,7 +59,7 @@ public class TradeController(
if (request.Type == "buy_from_trader")
{
var foundInRaid = _traderConfig.PurchasesAreFoundInRaid;
var buyData = (ProcessBuyTradeRequestData) request;
var buyData = (ProcessBuyTradeRequestData)request;
_tradeHelper.BuyItem(pmcData, buyData, sessionID, foundInRaid, output);
return output;
@@ -67,7 +68,7 @@ public class TradeController(
// Selling
if (request.Type == "sell_to_trader")
{
var sellData = (ProcessSellTradeRequestData) request;
var sellData = (ProcessSellTradeRequestData)request;
_tradeHelper.SellItem(pmcData, pmcData, sellData, sessionID, output);
return output;
@@ -76,7 +77,11 @@ public class TradeController(
var errorMessage = $"Unhandled trade event: {request.Type}";
_logger.Error(errorMessage);
return _httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.RagfairUnavailable);
return _httpResponseUtil.AppendErrorToOutput(
output,
errorMessage,
BackendErrorCodes.RagfairUnavailable
);
}
/// <summary>
@@ -89,7 +94,8 @@ public class TradeController(
public ItemEventRouterResponse ConfirmRagfairTrading(
PmcData pmcData,
ProcessRagfairTradeRequestData request,
string sessionID)
string sessionID
)
{
var output = _eventOutputHolder.GetOutput(sessionID);
@@ -111,7 +117,11 @@ public class TradeController(
"ragfair-unable_to_purchase_0_count_item",
_itemHelper.GetItem(fleaOffer.Items[0].Template).Value.Name
);
return _httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.OfferOutOfStock);
return _httpResponseUtil.AppendErrorToOutput(
output,
errorMessage,
BackendErrorCodes.OfferOutOfStock
);
}
if (_ragfairOfferHelper.OfferIsFromTrader(fleaOffer))
@@ -146,18 +156,24 @@ public class TradeController(
PmcData pmcData,
RagfairOffer fleaOffer,
OfferRequest requestOffer,
ItemEventRouterResponse output)
ItemEventRouterResponse output
)
{
// Skip buying items when player doesn't have needed loyalty
if (PlayerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData))
{
var errorMessage = $"Unable to buy item: {fleaOffer.Items[0].Template} from trader: {fleaOffer.User.Id} as loyalty level too low, skipping";
var errorMessage =
$"Unable to buy item: {fleaOffer.Items[0].Template} from trader: {fleaOffer.User.Id} as loyalty level too low, skipping";
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(errorMessage);
}
_httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.RagfairUnavailable);
_httpResponseUtil.AppendErrorToOutput(
output,
errorMessage,
BackendErrorCodes.RagfairUnavailable
);
return;
}
@@ -171,9 +187,15 @@ public class TradeController(
ItemId = fleaOffer.Root,
Count = requestOffer.Count,
SchemeId = 0,
SchemeItems = requestOffer.Items
SchemeItems = requestOffer.Items,
};
_tradeHelper.BuyItem(pmcData, buyData, sessionId, _traderConfig.PurchasesAreFoundInRaid, output);
_tradeHelper.BuyItem(
pmcData,
buyData,
sessionId,
_traderConfig.PurchasesAreFoundInRaid,
output
);
// Remove/lower offer quantity of item purchased from trader flea offer
_ragfairServer.ReduceOfferQuantity(fleaOffer.Id, requestOffer.Count ?? 0);
@@ -192,7 +214,8 @@ public class TradeController(
PmcData pmcData,
RagfairOffer fleaOffer,
OfferRequest requestOffer,
ItemEventRouterResponse output)
ItemEventRouterResponse output
)
{
var buyData = new ProcessBuyTradeRequestData
{
@@ -202,11 +225,17 @@ public class TradeController(
ItemId = fleaOffer.Id, // Store ragfair offerId in buyRequestData.item_id
Count = requestOffer.Count,
SchemeId = 0,
SchemeItems = requestOffer.Items
SchemeItems = requestOffer.Items,
};
// buyItem() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use
_tradeHelper.BuyItem(pmcData, buyData, sessionId, _ragfairConfig.Dynamic.PurchasesAreFoundInRaid, output);
_tradeHelper.BuyItem(
pmcData,
buyData,
sessionId,
_ragfairConfig.Dynamic.PurchasesAreFoundInRaid,
output
);
if (output.Warnings?.Count > 0)
{
return;
@@ -234,9 +263,7 @@ public class TradeController(
/// <param name="offerId">id of the offer</param>
/// <param name="offerOwnerId">Owner id</param>
/// <returns>true if offer was made by a player</returns>
protected bool IsPlayerOffer(
string offerId,
string? offerOwnerId)
protected bool IsPlayerOffer(string offerId, string? offerOwnerId)
{
// No ownerId, not player offer
if (offerOwnerId is null)
@@ -246,7 +273,7 @@ public class TradeController(
var offerCreatorProfile = _profileHelper.GetPmcProfile(offerOwnerId);
if (offerCreatorProfile is null || offerCreatorProfile.RagfairInfo.Offers?.Count == 0)
// No profile or no offers
// No profile or no offers
{
return false;
}
@@ -261,9 +288,7 @@ public class TradeController(
/// <param name="fleaOffer">Flea offer being bought</param>
/// <param name="pmcData">Player profile</param>
/// <returns>True if player can buy offer</returns>
protected bool PlayerLacksTraderLoyaltyLevelToBuyOffer(
RagfairOffer fleaOffer,
PmcData pmcData)
protected bool PlayerLacksTraderLoyaltyLevelToBuyOffer(RagfairOffer fleaOffer, PmcData pmcData)
{
return fleaOffer.LoyaltyLevel > pmcData.TradersInfo[fleaOffer.User.Id].LoyaltyLevel;
}
@@ -278,11 +303,12 @@ public class TradeController(
public ItemEventRouterResponse SellScavItemsToFence(
PmcData pmcData,
SellScavItemsToFenceRequestData request,
string sessionId)
string sessionId
)
{
var output = _eventOutputHolder.GetOutput(sessionId);
MailMoneyToPlayer(sessionId, (int) request.TotalValue, Traders.FENCE);
MailMoneyToPlayer(sessionId, (int)request.TotalValue, Traders.FENCE);
return output;
}
@@ -293,10 +319,7 @@ public class TradeController(
/// <param name="sessionId">Session id</param>
/// <param name="roublesToSend">amount of roubles to send</param>
/// <param name="trader">Trader to sell items to</param>
protected void MailMoneyToPlayer(
string sessionId,
int roublesToSend,
string trader)
protected void MailMoneyToPlayer(string sessionId, int roublesToSend, string trader)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
@@ -308,10 +331,7 @@ public class TradeController(
{
Id = _hashUtil.Generate(),
Template = Money.ROUBLES,
Upd = new Upd
{
StackObjectsCount = roublesToSend
}
Upd = new Upd { StackObjectsCount = roublesToSend },
};
// Ensure money is properly split to follow its max stack size limit
@@ -322,7 +342,11 @@ public class TradeController(
sessionId,
trader,
MessageType.MessageWithItems,
_randomUtil.GetArrayValue(_databaseService.GetTrader(trader).Dialogue.TryGetValue("soldItems", out var items) ? items : new List<string>()),
_randomUtil.GetArrayValue(
_databaseService.GetTrader(trader).Dialogue.TryGetValue("soldItems", out var items)
? items
: new List<string>()
),
curencyReward.SelectMany(x => x).ToList(),
_timeUtil.GetHoursAsSeconds(72)
);
@@ -340,7 +364,8 @@ public class TradeController(
string parentItemId,
List<Item> items,
Dictionary<string, int?> handbookPrices,
TraderBase traderDetails)
TraderBase traderDetails
)
{
var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(items, parentItemId);
@@ -348,14 +373,25 @@ public class TradeController(
foreach (var itemToSell in itemWithChildren)
{
var itemDetails = _itemHelper.GetItem(itemToSell.Template);
if (!(itemDetails.Key && _itemHelper.IsOfBaseclasses(itemDetails.Value.Id, traderDetails.ItemsBuy.Category)))
// Skip if tpl isn't item OR item doesn't fulfil match traders buy categories
if (
!(
itemDetails.Key
&& _itemHelper.IsOfBaseclasses(
itemDetails.Value.Id,
traderDetails.ItemsBuy.Category
)
)
)
// Skip if tpl isn't item OR item doesn't fulfil match traders buy categories
{
continue;
}
// Get price of item multiplied by how many are in stack
totalPrice += (int) ((handbookPrices[itemToSell.Template] ?? 0) * (itemToSell.Upd?.StackObjectsCount ?? 1));
totalPrice += (int)(
(handbookPrices[itemToSell.Template] ?? 0)
* (itemToSell.Upd?.StackObjectsCount ?? 1)
);
}
return totalPrice;
@@ -67,8 +67,9 @@ public class TraderController(
_traderPurchasePersisterService.RemoveStalePurchasesFromProfiles(traderId);
// Set to next hour on clock or current time + 60 minutes
trader.Base.NextResupply =
traderResetStartsWithServer ? (int) _traderHelper.GetNextUpdateTimestamp(trader.Base.Id) : (int) nextHourTimestamp;
trader.Base.NextResupply = traderResetStartsWithServer
? (int)_traderHelper.GetNextUpdateTimestamp(trader.Base.Id)
: (int)nextHourTimestamp;
}
}
@@ -85,10 +86,7 @@ public class TraderController(
var barterSchemeItem = kvp.Value?.FirstOrDefault()?.FirstOrDefault();
if (barterSchemeItem != null && _paymentHelper.IsMoneyTpl(barterSchemeItem.Template))
{
barterSchemeItem.Count += Math.Round(
barterSchemeItem?.Count * multiplier ?? 0D,
2
);
barterSchemeItem.Count += Math.Round(barterSchemeItem?.Count * multiplier ?? 0D, 2);
}
}
}
@@ -108,14 +106,14 @@ public class TraderController(
case Traders.LIGHTHOUSEKEEPER:
continue;
case Traders.FENCE:
{
if (_fenceService.NeedsPartialRefresh())
{
if (_fenceService.NeedsPartialRefresh())
{
_fenceService.GenerateFenceAssorts();
}
continue;
_fenceService.GenerateFenceAssorts();
}
continue;
}
}
// Trader needs to be refreshed
@@ -203,8 +201,8 @@ public class TraderController(
{
{ "5449016a4bdc2d6f028b456f", handbookPrices[Money.ROUBLES] },
{ "569668774bdc2da2298b4568", handbookPrices[Money.EUROS] },
{ "5696686a4bdc2da3298b456a", handbookPrices[Money.DOLLARS] }
}
{ "5696686a4bdc2da3298b456a", handbookPrices[Money.DOLLARS] },
},
};
}
}
@@ -23,7 +23,6 @@ public class WeatherController(
{
protected WeatherConfig _weatherConfig = _configServer.GetConfig<WeatherConfig>();
/// <summary>
/// Handle client/weather
/// </summary>
@@ -36,7 +35,7 @@ public class WeatherController(
Time = "",
Date = "",
Weather = null,
Season = Season.AUTUMN
Season = Season.AUTUMN,
};
_weatherGenerator.CalculateGameTime(result);
@@ -55,7 +54,7 @@ public class WeatherController(
var result = new GetLocalWeatherResponseData
{
Season = _seasonalEventService.GetActiveWeatherSeason(),
Weather = []
Weather = [],
};
result.Weather.AddRange(_raidWeatherService.GetUpcomingWeather());
@@ -7,9 +7,7 @@ using SPTarkov.Server.Core.Routers;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class WishlistController(
EventOutputHolder _eventOutputHolder
)
public class WishlistController(EventOutputHolder _eventOutputHolder)
{
/// <summary>
/// Handle AddToWishList
@@ -21,7 +19,8 @@ public class WishlistController(
public ItemEventRouterResponse AddToWishList(
PmcData pmcData,
AddToWishlistRequest request,
string sessionId)
string sessionId
)
{
foreach (var item in request.Items)
{
@@ -41,7 +40,8 @@ public class WishlistController(
public ItemEventRouterResponse RemoveFromWishList(
PmcData pmcData,
RemoveFromWishlistRequest request,
string sessionId)
string sessionId
)
{
foreach (var itemId in request.Items)
{
@@ -61,7 +61,8 @@ public class WishlistController(
public ItemEventRouterResponse ChangeWishListItemCategory(
PmcData pmcData,
ChangeWishlistItemCategoryRequest request,
string sessionId)
string sessionId
)
{
pmcData.WishList.Dictionary[request.Item] = request.Category.Value;
+20 -12
View File
@@ -32,14 +32,10 @@ public abstract class Router
{
if (partialMatch)
{
return GetInternalHandledRoutes()
.Where(r => r.dynamic)
.Any(r => url.Contains(r.route));
return GetInternalHandledRoutes().Where(r => r.dynamic).Any(r => url.Contains(r.route));
}
return GetInternalHandledRoutes()
.Where(r => !r.dynamic)
.Any(r => r.route == url);
return GetInternalHandledRoutes().Where(r => !r.dynamic).Any(r => r.route == url);
}
}
@@ -54,14 +50,19 @@ public abstract class StaticRouter : Router
_jsonUtil = jsonUtil;
}
public async ValueTask<object> HandleStatic(string url, string? body, string sessionID, string output)
public async ValueTask<object> HandleStatic(
string url,
string? body,
string sessionID,
string output
)
{
var action = _actions.Single(route => route.url == url);
var type = action.bodyType;
IRequestData? info = null;
if (type != null && !string.IsNullOrEmpty(body))
{
info = (IRequestData?) _jsonUtil.Deserialize(body, type);
info = (IRequestData?)_jsonUtil.Deserialize(body, type);
}
return await action.action(url, info, sessionID, output);
@@ -84,14 +85,19 @@ public abstract class DynamicRouter : Router
_jsonUtil = jsonUtil;
}
public async ValueTask<object> HandleDynamic(string url, string? body, string sessionID, string output)
public async ValueTask<object> HandleDynamic(
string url,
string? body,
string sessionID,
string output
)
{
var action = actions.First(r => url.Contains(r.url));
var type = action.bodyType;
IRequestData? info = null;
if (type != null && !string.IsNullOrEmpty(body))
{
info = (IRequestData?) _jsonUtil.Deserialize(body, type);
info = (IRequestData?)_jsonUtil.Deserialize(body, type);
}
return await action.action(url, info, sessionID, output);
@@ -107,11 +113,13 @@ public abstract class DynamicRouter : Router
// So instead I added the definition
public abstract class ItemEventRouterDefinition : Router
{
public abstract ValueTask<ItemEventRouterResponse> HandleItemEvent(string url,
public abstract ValueTask<ItemEventRouterResponse> HandleItemEvent(
string url,
PmcData pmcData,
BaseInteractionRequestData body,
string sessionID,
ItemEventRouterResponse output);
ItemEventRouterResponse output
);
}
public abstract class SaveLoadRouter : Router
@@ -1,18 +1,14 @@
namespace SPTarkov.Server.Core.DI
{
/// <summary>
/// A service locator designed specifically for Harmony patches and other
/// A service locator designed specifically for Harmony patches and other
/// parts of the application that do not have direct access to the Dependency Injection (DI) system.
///
/// This should not be used at all when having direct access to DI.
/// </summary>
public static class ServiceLocator
{
public static IServiceProvider ServiceProvider
{
get;
private set;
}
public static IServiceProvider ServiceProvider { get; private set; }
internal static void SetServiceProvider(IServiceProvider provider)
{
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
using SPTarkov.Server.Core.Constants;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Constants;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
@@ -16,7 +16,6 @@ using BodyPart = SPTarkov.Server.Core.Models.Eft.Common.Tables.BodyPart;
using BodyParts = SPTarkov.Server.Core.Constants.BodyParts;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Generators;
[Injectable]
@@ -50,7 +49,13 @@ public class BotGenerator(
/// <param name="botTemplate">base bot template to use (e.g. assault/pmcbot)</param>
/// <param name="profile">profile of player generating pscav</param>
/// <returns>BotBase</returns>
public PmcData GeneratePlayerScav(string sessionId, string role, string difficulty, BotType botTemplate, PmcData profile)
public PmcData GeneratePlayerScav(
string sessionId,
string role,
string difficulty,
BotType botTemplate,
PmcData profile
)
{
var bot = GetCloneOfBotBase();
bot.Info.Settings.BotDifficulty = difficulty;
@@ -66,7 +71,7 @@ public class BotGenerator(
BotRelativeLevelDeltaMin = 0,
BotCountToGenerate = 1,
BotDifficulty = difficulty,
IsPlayerScav = true
IsPlayerScav = true,
};
bot = GenerateBot(sessionId, bot, botTemplate, botGenDetails);
@@ -105,7 +110,7 @@ public class BotGenerator(
WishList = bot.WishList,
MoneyTransferLimitData = bot.MoneyTransferLimitData,
IsPmc = bot.IsPmc,
Prestige = new Dictionary<string, long>()
Prestige = new Dictionary<string, long>(),
};
}
@@ -115,7 +120,10 @@ public class BotGenerator(
/// <param name="sessionId">Session id</param>
/// <param name="botGenerationDetails">details on how to generate bots</param>
/// <returns>constructed bot</returns>
public BotBase PrepareAndGenerateBot(string sessionId, BotGenerationDetails? botGenerationDetails)
public BotBase PrepareAndGenerateBot(
string sessionId,
BotGenerationDetails? botGenerationDetails
)
{
var preparedBotBase = GetPreparedBotBase(
botGenerationDetails.EventRole ?? botGenerationDetails.Role, // Use eventRole if provided
@@ -124,13 +132,16 @@ public class BotGenerator(
);
// Get raw json data for bot (Cloned)
var botRole = botGenerationDetails.IsPmc ?? false
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
: botGenerationDetails.Role;
var botRole =
botGenerationDetails.IsPmc ?? false
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
: botGenerationDetails.Role;
var botJsonTemplateClone = _cloner.Clone(_botHelper.GetBotTemplate(botRole));
if (botJsonTemplateClone is null)
{
_logger.Error($"Unable to retrieve: {botRole} bot template, cannot generate bot of this type");
_logger.Error(
$"Unable to retrieve: {botRole} bot template, cannot generate bot of this type"
);
}
return GenerateBot(sessionId, preparedBotBase, botJsonTemplateClone, botGenerationDetails);
@@ -174,7 +185,8 @@ public class BotGenerator(
string sessionId,
BotBase bot,
BotType botJsonTemplate,
BotGenerationDetails botGenerationDetails)
BotGenerationDetails botGenerationDetails
)
{
var botRoleLowercase = botGenerationDetails.Role.ToLower();
var botLevel = _botLevelGenerator.GenerateBotLevel(
@@ -207,14 +219,17 @@ public class BotGenerator(
: string.Empty;
// Only run when generating a 'fake' playerscav, not actual player scav
if (!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false) && ShouldSimulatePlayerScav(botRoleLowercase))
if (
!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
&& ShouldSimulatePlayerScav(botRoleLowercase)
)
{
_botNameService.AddRandomPmcNameToBotMainProfileNicknameProperty(bot);
SetRandomisedGameVersionAndCategory(bot.Info);
}
if (!_seasonalEventService.ChristmasEventEnabled())
// Process all bots EXCEPT gifter, he needs christmas items
// Process all bots EXCEPT gifter, he needs christmas items
{
if (botGenerationDetails.Role != "gifter")
{
@@ -228,7 +243,12 @@ public class BotGenerator(
RemoveBlacklistedLootFromBotTemplate(botJsonTemplate.BotInventory);
// Remove hideout data if bot is not a PMC or pscav - match what live sends
if (!(botGenerationDetails.IsPmc.GetValueOrDefault(false) || botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)))
if (
!(
botGenerationDetails.IsPmc.GetValueOrDefault(false)
|| botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
)
)
{
bot.Hideout = null;
}
@@ -250,9 +270,15 @@ public class BotGenerator(
botGenerationDetails.BotDifficulty,
botGenerationDetails.Role
);
bot.Info.Settings.UseSimpleAnimator = botJsonTemplate.BotExperience.UseSimpleAnimator ?? false;
bot.Info.Voice = _weightedRandomHelper.GetWeightedValue(botJsonTemplate.BotAppearance.Voice);
bot.Health = GenerateHealth(botJsonTemplate.BotHealth, botGenerationDetails.IsPlayerScav.GetValueOrDefault(false));
bot.Info.Settings.UseSimpleAnimator =
botJsonTemplate.BotExperience.UseSimpleAnimator ?? false;
bot.Info.Voice = _weightedRandomHelper.GetWeightedValue(
botJsonTemplate.BotAppearance.Voice
);
bot.Health = GenerateHealth(
botJsonTemplate.BotHealth,
botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
);
bot.Skills = GenerateSkills(botJsonTemplate.BotSkills);
bot.Info.PrestigeLevel = 0;
@@ -308,7 +334,8 @@ public class BotGenerator(
/// <returns>True if name should be simulated pscav</returns>
public bool ShouldSimulatePlayerScav(string botRole)
{
return botRole == Roles.Assault && _randomUtil.GetChance100(_botConfig.ChanceAssaultScavHasPlayerScavName);
return botRole == Roles.Assault
&& _randomUtil.GetChance100(_botConfig.ChanceAssaultScavHasPlayerScavName);
}
/// <summary>
@@ -318,13 +345,19 @@ public class BotGenerator(
/// <param name="botDifficulty">the killed bots difficulty</param>
/// <param name="role">Role of bot (optional, used for error logging)</param>
/// <returns>Experience for kill</returns>
public int GetExperienceRewardForKillByDifficulty(Dictionary<string, MinMax<int>> experiences, string botDifficulty, string role)
public int GetExperienceRewardForKillByDifficulty(
Dictionary<string, MinMax<int>> experiences,
string botDifficulty,
string role
)
{
if (!experiences.TryGetValue(botDifficulty.ToLower(), out var result))
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to find experience: {botDifficulty} for {role} bot, falling back to `normal`");
_logger.Debug(
$"Unable to find experience: {botDifficulty} for {role} bot, falling back to `normal`"
);
}
return _randomUtil.GetInt(experiences["normal"].Min, experiences["normal"].Max);
@@ -332,7 +365,6 @@ public class BotGenerator(
// Some bots have -1/-1, shortcut result
if (result.Max == -1)
{
return -1;
@@ -348,11 +380,17 @@ public class BotGenerator(
/// <param name="botDifficulty">Difficulty of bot to look up</param>
/// <param name="role">Role of bot (optional, used for error logging)</param>
/// <returns>Standing change value</returns>
public double GetStandingChangeForKillByDifficulty(Dictionary<string, double> standingsForKill, string botDifficulty, string role)
public double GetStandingChangeForKillByDifficulty(
Dictionary<string, double> standingsForKill,
string botDifficulty,
string role
)
{
if (!standingsForKill.TryGetValue(botDifficulty.ToLower(), out var result))
{
_logger.Warning($"Unable to find standing for kill value for: {role} {botDifficulty}, falling back to `normal`");
_logger.Warning(
$"Unable to find standing for kill value for: {role} {botDifficulty}, falling back to `normal`"
);
return standingsForKill["normal"];
}
@@ -367,11 +405,17 @@ public class BotGenerator(
/// <param name="botDifficulty">Difficulty of bot to look up</param>
/// <param name="role">Role of bot (optional, used for error logging)</param>
/// <returns>Standing change value</returns>
public double GetAggressorBonusByDifficulty(Dictionary<string, double> aggressorBonuses, string botDifficulty, string role)
public double GetAggressorBonusByDifficulty(
Dictionary<string, double> aggressorBonuses,
string botDifficulty,
string role
)
{
if (!aggressorBonuses.TryGetValue(botDifficulty.ToLower(), out var result))
{
_logger.Warning($"Unable to find aggressor bonus for kill value for: {role} {botDifficulty}, falling back to `normal`");
_logger.Warning(
$"Unable to find aggressor bonus for kill value for: {role} {botDifficulty}, falling back to `normal`"
);
return aggressorBonuses["normal"];
}
@@ -384,7 +428,10 @@ public class BotGenerator(
/// </summary>
/// <param name="botJsonTemplate">Bot data to adjust</param>
/// <param name="botGenerationDetails">Generation details of bot</param>
public void FilterBlacklistedGear(BotType botJsonTemplate, BotGenerationDetails botGenerationDetails)
public void FilterBlacklistedGear(
BotType botJsonTemplate,
BotGenerationDetails botGenerationDetails
)
{
var blacklist = _botEquipmentFilterService.GetBotEquipmentBlacklist(
_botGeneratorHelper.GetBotEquipmentRole(botGenerationDetails.Role),
@@ -392,7 +439,7 @@ public class BotGenerator(
);
if (blacklist?.Gear is null)
// Nothing to filter by
// Nothing to filter by
{
return;
}
@@ -402,7 +449,7 @@ public class BotGenerator(
var equipmentDict = botJsonTemplate.BotInventory.Equipment[equipmentSlot];
foreach (var blacklistedTpl in blacklistedTpls)
// Set weighting to 0, will never be picked
// Set weighting to 0, will never be picked
{
equipmentDict[blacklistedTpl] = 0;
}
@@ -433,9 +480,10 @@ public class BotGenerator(
// Remove blacklisted loot from loot containers
foreach (var lootContainerKey in lootContainersToFilter)
{
var propInfo = props
.FirstOrDefault(x => string.Equals(x.Name, lootContainerKey, StringComparison.CurrentCultureIgnoreCase));
var prop = (Dictionary<string, double>?) propInfo.GetValue(botInventory.Items);
var propInfo = props.FirstOrDefault(x =>
string.Equals(x.Name, lootContainerKey, StringComparison.CurrentCultureIgnoreCase)
);
var prop = (Dictionary<string, double>?)propInfo.GetValue(botInventory.Items);
// No container, skip
if (prop is null)
@@ -444,9 +492,10 @@ public class BotGenerator(
}
var newProp = prop.Where(tpl =>
{
return !_itemFilterService.IsLootableItemBlacklisted(tpl.Key);
}).ToDictionary();
{
return !_itemFilterService.IsLootableItemBlacklisted(tpl.Key);
})
.ToDictionary();
propInfo.SetValue(botInventory.Items, newProp);
}
}
@@ -457,7 +506,11 @@ public class BotGenerator(
/// <param name="bot">Bot to adjust</param>
/// <param name="appearance">Appearance settings to choose from</param>
/// <param name="botGenerationDetails">Generation details</param>
public void SetBotAppearance(BotBase bot, Appearance appearance, BotGenerationDetails botGenerationDetails)
public void SetBotAppearance(
BotBase bot,
Appearance appearance,
BotGenerationDetails botGenerationDetails
)
{
// Choose random values by weight
bot.Customization.Head = _weightedRandomHelper.GetWeightedValue<string>(appearance.Head);
@@ -468,10 +521,13 @@ public class BotGenerator(
var chosenBodyTemplate = _databaseService.GetCustomization()[bot.Customization.Body];
// Some bodies have matching hands, look up body to see if this is the case
var chosenBody = bodyGlobalDictDb.FirstOrDefault(c => c.Key == chosenBodyTemplate?.Name.Trim());
bot.Customization.Hands = chosenBody.Value?.IsNotRandom ?? false
? chosenBody.Value.Hands // Has fixed hands for chosen body, update to match
: _weightedRandomHelper.GetWeightedValue<string>(appearance.Hands); // Hands can be random, choose any from weighted dict
var chosenBody = bodyGlobalDictDb.FirstOrDefault(c =>
c.Key == chosenBodyTemplate?.Name.Trim()
);
bot.Customization.Hands =
chosenBody.Value?.IsNotRandom ?? false
? chosenBody.Value.Hands // Has fixed hands for chosen body, update to match
: _weightedRandomHelper.GetWeightedValue<string>(appearance.Hands); // Hands can be random, choose any from weighted dict
}
/// <summary>
@@ -491,93 +547,121 @@ public class BotGenerator(
Hydration = new CurrentMinMax
{
Current = _randomUtil.GetDouble(healthObj.Hydration.Min, healthObj.Hydration.Max),
Maximum = healthObj.Hydration.Max
Maximum = healthObj.Hydration.Max,
},
Energy = new CurrentMinMax
{
Current = _randomUtil.GetDouble(healthObj.Energy.Min, healthObj.Energy.Max),
Maximum = healthObj.Energy.Max
Maximum = healthObj.Energy.Max,
},
Temperature = new CurrentMinMax
{
Current = _randomUtil.GetDouble(healthObj.Temperature.Min, healthObj.Temperature.Max),
Maximum = healthObj.Temperature.Max
Current = _randomUtil.GetDouble(
healthObj.Temperature.Min,
healthObj.Temperature.Max
),
Maximum = healthObj.Temperature.Max,
},
BodyParts = new Dictionary<string, BodyPartHealth>
{
{
BodyParts.Head, new BodyPartHealth
BodyParts.Head,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.Head.Min, bodyParts.Head.Max),
Maximum = Math.Round(bodyParts.Head.Max)
}
Maximum = Math.Round(bodyParts.Head.Max),
},
}
},
{
BodyParts.Chest, new BodyPartHealth
BodyParts.Chest,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.Chest.Min, bodyParts.Chest.Max),
Maximum = Math.Round(bodyParts.Chest.Max)
}
Current = _randomUtil.GetDouble(
bodyParts.Chest.Min,
bodyParts.Chest.Max
),
Maximum = Math.Round(bodyParts.Chest.Max),
},
}
},
{
BodyParts.Stomach, new BodyPartHealth
BodyParts.Stomach,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.Stomach.Min, bodyParts.Stomach.Max),
Maximum = Math.Round(bodyParts.Stomach.Max)
}
Current = _randomUtil.GetDouble(
bodyParts.Stomach.Min,
bodyParts.Stomach.Max
),
Maximum = Math.Round(bodyParts.Stomach.Max),
},
}
},
{
BodyParts.LeftArm, new BodyPartHealth
BodyParts.LeftArm,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.LeftArm.Min, bodyParts.LeftArm.Max),
Maximum = Math.Round(bodyParts.LeftArm.Max)
}
Current = _randomUtil.GetDouble(
bodyParts.LeftArm.Min,
bodyParts.LeftArm.Max
),
Maximum = Math.Round(bodyParts.LeftArm.Max),
},
}
},
{
BodyParts.RightArm, new BodyPartHealth
BodyParts.RightArm,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.RightArm.Min, bodyParts.RightArm.Max),
Maximum = Math.Round(bodyParts.RightArm.Max)
}
Current = _randomUtil.GetDouble(
bodyParts.RightArm.Min,
bodyParts.RightArm.Max
),
Maximum = Math.Round(bodyParts.RightArm.Max),
},
}
},
{
BodyParts.LeftLeg, new BodyPartHealth
BodyParts.LeftLeg,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.LeftLeg.Min, bodyParts.LeftLeg.Max),
Maximum = Math.Round(bodyParts.LeftLeg.Max)
}
Current = _randomUtil.GetDouble(
bodyParts.LeftLeg.Min,
bodyParts.LeftLeg.Max
),
Maximum = Math.Round(bodyParts.LeftLeg.Max),
},
}
},
{
BodyParts.RightLeg, new BodyPartHealth
BodyParts.RightLeg,
new BodyPartHealth
{
Health = new CurrentMinMax
{
Current = _randomUtil.GetDouble(bodyParts.RightLeg.Min, bodyParts.RightLeg.Max),
Maximum = Math.Round(bodyParts.RightLeg.Max)
}
Current = _randomUtil.GetDouble(
bodyParts.RightLeg.Min,
bodyParts.RightLeg.Max
),
Maximum = Math.Round(bodyParts.RightLeg.Max),
},
}
}
},
},
UpdateTime = 0, // 0 for player-scav too
Immortal = false
Immortal = false,
};
return health;
@@ -602,9 +686,13 @@ public class BotGenerator(
{
double? hpTotal = 0;
foreach (var prop in props.Where(property => !property.Name.Equals("extensiondata", StringComparison.OrdinalIgnoreCase)))
foreach (
var prop in props.Where(property =>
!property.Name.Equals("extensiondata", StringComparison.OrdinalIgnoreCase)
)
)
{
var value = (MinMax<double>) prop.GetValue(bodyPart);
var value = (MinMax<double>)prop.GetValue(bodyPart);
hpTotal += value.Max;
}
@@ -630,7 +718,7 @@ public class BotGenerator(
{
Common = GetCommonSkillsWithRandomisedProgressValue(botSkills.Common),
Mastering = GetMasteringSkillsWithRandomisedProgressValue(botSkills.Mastering),
Points = 0
Points = 0,
};
return skillsToReturn;
@@ -641,7 +729,9 @@ public class BotGenerator(
/// </summary>
/// <param name="skills">Skills to randomise</param>
/// <returns>Skills with randomised progress values as a collection</returns>
public List<CommonSkill> GetCommonSkillsWithRandomisedProgressValue(Dictionary<string, MinMax<double>>? skills)
public List<CommonSkill> GetCommonSkillsWithRandomisedProgressValue(
Dictionary<string, MinMax<double>>? skills
)
{
if (skills is null)
{
@@ -650,23 +740,22 @@ public class BotGenerator(
return skills
.Select(kvp =>
{
// Get skill from dict, skip if not found
var skill = kvp.Value;
if (skill == null)
{
// Get skill from dict, skip if not found
var skill = kvp.Value;
if (skill == null)
{
return null;
}
return new CommonSkill
{
Id = Enum.Parse<SkillTypes>(kvp.Key),
Progress = _randomUtil.GetDouble(skill.Min, skill.Max),
PointsEarnedDuringSession = 0,
LastAccess = 0
};
return null;
}
)
return new CommonSkill
{
Id = Enum.Parse<SkillTypes>(kvp.Key),
Progress = _randomUtil.GetDouble(skill.Min, skill.Max),
PointsEarnedDuringSession = 0,
LastAccess = 0,
};
})
.Where(baseSkill => baseSkill != null)
.ToList();
}
@@ -676,7 +765,9 @@ public class BotGenerator(
/// </summary>
/// <param name="skills">Skills to randomise</param>
/// <returns>Skills with randomised progress values as a collection</returns>
public List<MasterySkill> GetMasteringSkillsWithRandomisedProgressValue(Dictionary<string, MinMax<double>>? skills)
public List<MasterySkill> GetMasteringSkillsWithRandomisedProgressValue(
Dictionary<string, MinMax<double>>? skills
)
{
if (skills is null)
{
@@ -685,22 +776,21 @@ public class BotGenerator(
return skills
.Select(kvp =>
{
// Get skill from dict, skip if not found
var skill = kvp.Value;
if (skill == null)
{
// Get skill from dict, skip if not found
var skill = kvp.Value;
if (skill == null)
{
return null;
}
// All skills have id and progress props
return new MasterySkill
{
Id = kvp.Key,
Progress = _randomUtil.GetDouble(skill.Min, skill.Max)
};
return null;
}
)
// All skills have id and progress props
return new MasterySkill
{
Id = kvp.Key,
Progress = _randomUtil.GetDouble(skill.Min, skill.Max),
};
})
.Where(baseSkill => baseSkill != null)
.ToList();
}
@@ -716,7 +806,9 @@ public class BotGenerator(
var botId = _hashUtil.Generate();
bot.Id = botId;
bot.Aid = botGenerationDetails.IsPmc.GetValueOrDefault(false) ? _hashUtil.GenerateAccountId() : 0;
bot.Aid = botGenerationDetails.IsPmc.GetValueOrDefault(false)
? _hashUtil.GenerateAccountId()
: 0;
}
/// <summary>
@@ -788,7 +880,9 @@ public class BotGenerator(
break;
default:
// Everyone else gets a weighted randomised category
botInfo.MemberCategory = _weightedRandomHelper.GetWeightedValue(_pmcConfig.AccountTypeWeight);
botInfo.MemberCategory = _weightedRandomHelper.GetWeightedValue(
_pmcConfig.AccountTypeWeight
);
break;
}
@@ -811,10 +905,7 @@ public class BotGenerator(
Template = GetDogtagTplByGameVersionAndSide(bot.Info.Side, bot.Info.GameVersion),
ParentId = bot.Inventory.Equipment,
SlotId = Slots.Dogtag,
Upd = new Upd
{
SpawnedInSession = true
}
Upd = new Upd { SpawnedInSession = true },
};
bot.Inventory.Items.Add(inventoryItem);
@@ -47,12 +47,16 @@ public class BotInventoryGenerator(
EquipmentSlots.TacticalVest,
EquipmentSlots.FaceCover,
EquipmentSlots.Headwear,
EquipmentSlots.Earpiece
EquipmentSlots.Earpiece,
];
private readonly BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
private readonly HashSet<string> _slotsToCheck = [EquipmentSlots.Pockets.ToString(), EquipmentSlots.SecuredContainer.ToString()];
private readonly HashSet<string> _slotsToCheck =
[
EquipmentSlots.Pockets.ToString(),
EquipmentSlots.SecuredContainer.ToString(),
];
/// <summary>
/// Add equipment/weapons/loot to bot
@@ -64,7 +68,14 @@ public class BotInventoryGenerator(
/// <param name="botLevel">Level of bot being generated</param>
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
/// <returns>PmcInventory object with equipment/weapons/loot</returns>
public BotBaseInventory GenerateInventory(string sessionId, BotType botJsonTemplate, string botRole, bool isPmc, int botLevel, string chosenGameVersion)
public BotBaseInventory GenerateInventory(
string sessionId,
BotType botJsonTemplate,
string botRole,
bool isPmc,
int botLevel,
string chosenGameVersion
)
{
var templateInventory = botJsonTemplate.BotInventory;
var wornItemChances = botJsonTemplate.BotChances;
@@ -74,7 +85,9 @@ public class BotInventoryGenerator(
var botInventory = GenerateInventoryBase();
// Get generated raid details bot will be spawned in
var raidConfig = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
var raidConfig = _profileActivityService
.GetProfileActivityRaidData(sessionId)
?.RaidConfiguration;
GenerateAndAddEquipmentToBot(
sessionId,
@@ -101,7 +114,14 @@ public class BotInventoryGenerator(
);
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
_botLootGenerator.GenerateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
_botLootGenerator.GenerateLoot(
sessionId,
botJsonTemplate,
isPmc,
botRole,
botInventory,
botLevel
);
return botInventory;
}
@@ -123,36 +143,16 @@ public class BotInventoryGenerator(
{
Items =
[
new Item
{
Id = equipmentId,
Template = ItemTpl.INVENTORY_DEFAULT
},
new Item
{
Id = stashId,
Template = ItemTpl.STASH_STANDARD_STASH_10X30
},
new Item
{
Id = questRaidItemsId,
Template = ItemTpl.STASH_QUESTRAID
},
new Item
{
Id = questStashItemsId,
Template = ItemTpl.STASH_QUESTOFFLINE
},
new Item
{
Id = sortingTableId,
Template = ItemTpl.SORTINGTABLE_SORTING_TABLE
},
new Item { Id = equipmentId, Template = ItemTpl.INVENTORY_DEFAULT },
new Item { Id = stashId, Template = ItemTpl.STASH_STANDARD_STASH_10X30 },
new Item { Id = questRaidItemsId, Template = ItemTpl.STASH_QUESTRAID },
new Item { Id = questStashItemsId, Template = ItemTpl.STASH_QUESTOFFLINE },
new Item { Id = sortingTableId, Template = ItemTpl.SORTINGTABLE_SORTING_TABLE },
new Item
{
Id = hideoutCustomizationStashId,
Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION
}
Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION,
},
],
Equipment = equipmentId,
Stash = stashId,
@@ -162,7 +162,7 @@ public class BotInventoryGenerator(
HideoutAreaStashes = new Dictionary<string, string>(),
FastPanel = new Dictionary<string, string>(),
FavoriteItems = [],
HideoutCustomizationStashId = hideoutCustomizationStashId
HideoutCustomizationStashId = hideoutCustomizationStashId,
};
}
@@ -177,24 +177,42 @@ public class BotInventoryGenerator(
/// <param name="botLevel">Level of bot</param>
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
/// <param name="raidConfig">RadiConfig</param>
public void GenerateAndAddEquipmentToBot(string sessionId, BotTypeInventory templateInventory, Chances wornItemChances, string botRole,
BotBaseInventory botInventory, int botLevel, string chosenGameVersion, bool isPmc, GetRaidConfigurationRequestData? raidConfig)
public void GenerateAndAddEquipmentToBot(
string sessionId,
BotTypeInventory templateInventory,
Chances wornItemChances,
string botRole,
BotBaseInventory botInventory,
int botLevel,
string chosenGameVersion,
bool isPmc,
GetRaidConfigurationRequestData? raidConfig
)
{
_botConfig.Equipment.TryGetValue(_botGeneratorHelper.GetBotEquipmentRole(botRole), out var botEquipConfig);
_botConfig.Equipment.TryGetValue(
_botGeneratorHelper.GetBotEquipmentRole(botRole),
out var botEquipConfig
);
var randomistionDetails = _botHelper.GetBotRandomizationDetails(botLevel, botEquipConfig);
// Apply nighttime changes if its nighttime + there's changes to make
if (
randomistionDetails?.NighttimeChanges is not null &&
raidConfig is not null &&
_weatherHelper.IsNightTime(raidConfig.TimeVariant, raidConfig.Location)
randomistionDetails?.NighttimeChanges is not null
&& raidConfig is not null
&& _weatherHelper.IsNightTime(raidConfig.TimeVariant, raidConfig.Location)
)
{
foreach (var equipmentSlotKvP in randomistionDetails.NighttimeChanges.EquipmentModsModifiers)
// Never let mod chance go outside 0 - 100
foreach (
var equipmentSlotKvP in randomistionDetails.NighttimeChanges.EquipmentModsModifiers
)
// Never let mod chance go outside 0 - 100
{
randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] = Math.Min(
Math.Max(randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] + equipmentSlotKvP.Value, 0),
Math.Max(
randomistionDetails.EquipmentMods[equipmentSlotKvP.Key]
+ equipmentSlotKvP.Value,
0
),
100
);
}
@@ -204,7 +222,6 @@ public class BotInventoryGenerator(
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
var botEquipmentRole = _botGeneratorHelper.GetBotEquipmentRole(botRole);
// Iterate over all equipment slots of bot, do it in specifc order to reduce conflicts
// e.g. ArmorVest should be generated after TactivalVest
// or FACE_COVER before HEADWEAR
@@ -228,12 +245,12 @@ public class BotInventoryGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
}
@@ -244,20 +261,24 @@ public class BotInventoryGenerator(
{
RootEquipmentSlot = EquipmentSlots.Pockets,
// Unheard profiles have unique sized pockets
RootEquipmentPool = GetPocketPoolByGameEdition(chosenGameVersion, templateInventory, isPmc),
RootEquipmentPool = GetPocketPoolByGameEdition(
chosenGameVersion,
templateInventory,
isPmc
),
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GenerateModsBlacklist = [ItemTpl.POCKETS_1X4_TUE, ItemTpl.POCKETS_LARGE],
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
@@ -272,12 +293,12 @@ public class BotInventoryGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
@@ -292,12 +313,12 @@ public class BotInventoryGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
@@ -312,12 +333,12 @@ public class BotInventoryGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
@@ -332,25 +353,25 @@ public class BotInventoryGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
// Bot has no armor vest and flagged to be forced to wear armored rig in this event
if (botEquipConfig.ForceOnlyArmoredRigWhenNoArmor.GetValueOrDefault(false) && !hasArmorVest)
// Filter rigs down to only those with armor
// Filter rigs down to only those with armor
{
FilterRigsToThoseWithProtection(templateInventory.Equipment, botRole);
}
// Optimisation - Remove armored rigs from pool
if (hasArmorVest)
// Filter rigs down to only those with armor
// Filter rigs down to only those with armor
{
FilterRigsToThoseWithoutProtection(templateInventory.Equipment, botRole);
}
@@ -372,12 +393,12 @@ public class BotInventoryGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
}
);
}
@@ -389,13 +410,14 @@ public class BotInventoryGenerator(
/// <param name="templateInventory"></param>
/// <param name="isPmc">is bot a PMC</param>
/// <returns></returns>
protected Dictionary<string, double> GetPocketPoolByGameEdition(string chosenGameVersion, BotTypeInventory templateInventory, bool isPmc)
protected Dictionary<string, double> GetPocketPoolByGameEdition(
string chosenGameVersion,
BotTypeInventory templateInventory,
bool isPmc
)
{
return chosenGameVersion == GameEditions.UNHEARD && isPmc
? new Dictionary<string, double>
{
[ItemTpl.POCKETS_1X4_TUE] = 1
}
? new Dictionary<string, double> { [ItemTpl.POCKETS_1X4_TUE] = 1 }
: templateInventory.Equipment.GetValueOrDefault(EquipmentSlots.Pockets);
}
@@ -404,7 +426,10 @@ public class BotInventoryGenerator(
/// </summary>
/// <param name="templateEquipment">Equipment to filter TacticalVest of</param>
/// <param name="botRole">Role of bot vests are being filtered for</param>
public void FilterRigsToThoseWithProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole)
public void FilterRigsToThoseWithProtection(
Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment,
string botRole
)
{
var tacVestsWithArmor = templateEquipment[EquipmentSlots.TacticalVest]
.Where(kvp => _itemHelper.ItemHasSlots(kvp.Key))
@@ -414,7 +439,9 @@ public class BotInventoryGenerator(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to filter to only armored rigs as bot: {botRole} has none in pool");
_logger.Debug(
$"Unable to filter to only armored rigs as bot: {botRole} has none in pool"
);
}
return;
@@ -429,8 +456,11 @@ public class BotInventoryGenerator(
/// <param name="templateEquipment">Equipment to filter TacticalVest by</param>
/// <param name="botRole">Role of bot vests are being filtered for</param>
/// <param name="allowEmptyResult">Should the function return all rigs when 0 unarmored are found</param>
public void FilterRigsToThoseWithoutProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole,
bool allowEmptyResult = true)
public void FilterRigsToThoseWithoutProtection(
Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment,
string botRole,
bool allowEmptyResult = true
)
{
var tacVestsWithoutArmor = templateEquipment[EquipmentSlots.TacticalVest]
.Where(kvp => !_itemHelper.ItemHasSlots(kvp.Key))
@@ -440,7 +470,9 @@ public class BotInventoryGenerator(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool");
_logger.Debug(
$"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool"
);
}
return;
@@ -458,7 +490,9 @@ public class BotInventoryGenerator(
{
double? spawnChance = _slotsToCheck.Contains(settings.RootEquipmentSlot.ToString())
? 100
: settings.SpawnChances.EquipmentChances.GetValueOrDefault(settings.RootEquipmentSlot.ToString());
: settings.SpawnChances.EquipmentChances.GetValueOrDefault(
settings.RootEquipmentSlot.ToString()
);
if (!spawnChance.HasValue)
{
@@ -489,12 +523,16 @@ public class BotInventoryGenerator(
return false;
}
var chosenItemTpl = _weightedRandomHelper.GetWeightedValue(settings.RootEquipmentPool);
var chosenItemTpl = _weightedRandomHelper.GetWeightedValue(
settings.RootEquipmentPool
);
var dbResult = _itemHelper.GetItem(chosenItemTpl);
if (!dbResult.Key)
{
_logger.Error(_localisationService.GetText("bot-missing_item_template", chosenItemTpl));
_logger.Error(
_localisationService.GetText("bot-missing_item_template", chosenItemTpl)
);
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"EquipmentSlot-> {settings.RootEquipmentSlot}");
@@ -544,7 +582,10 @@ public class BotInventoryGenerator(
Template = pickedItemDb.Id,
ParentId = settings.Inventory.Equipment,
SlotId = settings.RootEquipmentSlot.ToString(),
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(pickedItemDb, settings.BotData.Role)
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(
pickedItemDb,
settings.BotData.Role
),
};
var botEquipBlacklist = _botEquipmentFilterService.GetBotEquipmentBlacklist(
@@ -553,10 +594,14 @@ public class BotInventoryGenerator(
);
// Edge case: Filter the armor items mod pool if bot exists in config dict + config has armor slot
if (_botConfig.Equipment.ContainsKey(settings.BotData.EquipmentRole) &&
settings.RandomisationDetails?.RandomisedArmorSlots != null &&
settings.RandomisationDetails.RandomisedArmorSlots.Contains(settings.RootEquipmentSlot.ToString()))
// Filter out mods from relevant blacklist
if (
_botConfig.Equipment.ContainsKey(settings.BotData.EquipmentRole)
&& settings.RandomisationDetails?.RandomisedArmorSlots != null
&& settings.RandomisationDetails.RandomisedArmorSlots.Contains(
settings.RootEquipmentSlot.ToString()
)
)
// Filter out mods from relevant blacklist
{
settings.ModPool[pickedItemDb.Id] = GetFilteredDynamicModsForItem(
pickedItemDb.Id,
@@ -564,7 +609,9 @@ public class BotInventoryGenerator(
);
}
var itemIsOnGenerateModBlacklist = settings.GenerateModsBlacklist != null && settings.GenerateModsBlacklist.Contains(pickedItemDb.Id);
var itemIsOnGenerateModBlacklist =
settings.GenerateModsBlacklist != null
&& settings.GenerateModsBlacklist.Contains(pickedItemDb.Id);
// Does item have slots for sub-mods to be inserted into
if (pickedItemDb.Properties?.Slots?.Count > 0 && !itemIsOnGenerateModBlacklist)
{
@@ -595,7 +642,10 @@ public class BotInventoryGenerator(
/// <param name="itemTpl">Item mod pool is being retrieved and filtered</param>
/// <param name="equipmentBlacklist">Blacklist to filter mod pool with</param>
/// <returns>Filtered pool of mods</returns>
public Dictionary<string, HashSet<string>> GetFilteredDynamicModsForItem(string itemTpl, Dictionary<string, HashSet<string>> equipmentBlacklist)
public Dictionary<string, HashSet<string>> GetFilteredDynamicModsForItem(
string itemTpl,
Dictionary<string, HashSet<string>> equipmentBlacklist
)
{
var modPool = _botEquipmentModPoolService.GetModsForGearSlot(itemTpl);
foreach (var modSlot in modPool)
@@ -605,14 +655,16 @@ public class BotInventoryGenerator(
{
blacklistedMods = [];
}
;
// Get mods not on blacklist
var filteredMods = modPool[modSlot.Key].Where(slotName => !blacklistedMods.Contains(slotName));
var filteredMods = modPool[modSlot.Key]
.Where(slotName => !blacklistedMods.Contains(slotName));
if (!filteredMods.Any())
{
_logger.Warning($"Filtering {modSlot.Key} pool resulting in 0 items, skipping filter");
_logger.Warning(
$"Filtering {modSlot.Key} pool resulting in 0 items, skipping filter"
);
continue;
}
@@ -633,14 +685,24 @@ public class BotInventoryGenerator(
/// <param name="isPmc">Is the bot being generated as a pmc</param>
/// <param name="itemGenerationLimitsMinMax">Limits for items the bot can have</param>
/// <param name="botLevel">level of bot having weapon generated</param>
public void GenerateAndAddWeaponsToBot(BotTypeInventory templateInventory, Chances equipmentChances, string sessionId, BotBaseInventory botInventory,
string botRole, bool isPmc, Generation itemGenerationLimitsMinMax, int botLevel)
public void GenerateAndAddWeaponsToBot(
BotTypeInventory templateInventory,
Chances equipmentChances,
string sessionId,
BotBaseInventory botInventory,
string botRole,
bool isPmc,
Generation itemGenerationLimitsMinMax,
int botLevel
)
{
var weaponSlotsToFill = GetDesiredWeaponsForBot(equipmentChances);
foreach (var desiredWeapons in weaponSlotsToFill)
// Add weapon to bot if true and bot json has something to put into the slot
// Add weapon to bot if true and bot json has something to put into the slot
{
if (desiredWeapons.ShouldSpawn && templateInventory.Equipment[desiredWeapons.Slot].Any())
if (
desiredWeapons.ShouldSpawn && templateInventory.Equipment[desiredWeapons.Slot].Any()
)
{
AddWeaponAndMagazinesToInventory(
sessionId,
@@ -664,24 +726,32 @@ public class BotInventoryGenerator(
/// <returns>What slots bot should have weapons generated for</returns>
public List<DesiredWeapons> GetDesiredWeaponsForBot(Chances equipmentChances)
{
var shouldSpawnPrimary = _randomUtil.GetChance100(equipmentChances.EquipmentChances["FirstPrimaryWeapon"]);
var shouldSpawnPrimary = _randomUtil.GetChance100(
equipmentChances.EquipmentChances["FirstPrimaryWeapon"]
);
return
[
new DesiredWeapons
{
Slot = EquipmentSlots.FirstPrimaryWeapon,
ShouldSpawn = shouldSpawnPrimary
ShouldSpawn = shouldSpawnPrimary,
},
new DesiredWeapons
{
Slot = EquipmentSlots.SecondPrimaryWeapon,
ShouldSpawn = shouldSpawnPrimary && _randomUtil.GetChance100(equipmentChances.EquipmentChances["SecondPrimaryWeapon"])
ShouldSpawn =
shouldSpawnPrimary
&& _randomUtil.GetChance100(
equipmentChances.EquipmentChances["SecondPrimaryWeapon"]
),
},
new DesiredWeapons
{
Slot = EquipmentSlots.Holster,
ShouldSpawn = !shouldSpawnPrimary || _randomUtil.GetChance100(equipmentChances.EquipmentChances["Holster"]) // No primary = force pistol
}
ShouldSpawn =
!shouldSpawnPrimary
|| _randomUtil.GetChance100(equipmentChances.EquipmentChances["Holster"]), // No primary = force pistol
},
];
}
@@ -697,9 +767,17 @@ public class BotInventoryGenerator(
/// <param name="isPmc">Is the bot being generated as a pmc</param>
/// <param name="itemGenerationWeights"></param>
/// <param name="botLevel"></param>
public void AddWeaponAndMagazinesToInventory(string sessionId, DesiredWeapons weaponSlot, BotTypeInventory templateInventory, BotBaseInventory botInventory,
Chances equipmentChances, string botRole,
bool isPmc, Generation itemGenerationWeights, int botLevel)
public void AddWeaponAndMagazinesToInventory(
string sessionId,
DesiredWeapons weaponSlot,
BotTypeInventory templateInventory,
BotBaseInventory botInventory,
Chances equipmentChances,
string botRole,
bool isPmc,
Generation itemGenerationWeights,
int botLevel
)
{
var generatedWeapon = _botWeaponGenerator.GenerateRandomWeapon(
sessionId,
@@ -725,15 +803,7 @@ public class BotInventoryGenerator(
public class DesiredWeapons
{
public EquipmentSlots Slot
{
get;
set;
}
public EquipmentSlots Slot { get; set; }
public bool ShouldSpawn
{
get;
set;
}
public bool ShouldSpawn { get; set; }
}
@@ -24,25 +24,28 @@ public class BotLevelGenerator(
/// <param name="botGenerationDetails">Details to help generate a bot</param>
/// <param name="bot">Bot the level is being generated for</param>
/// <returns>IRandomisedBotLevelResult object</returns>
public RandomisedBotLevelResult GenerateBotLevel(MinMax<int> levelDetails, BotGenerationDetails botGenerationDetails, BotBase bot)
public RandomisedBotLevelResult GenerateBotLevel(
MinMax<int> levelDetails,
BotGenerationDetails botGenerationDetails,
BotBase bot
)
{
if (!botGenerationDetails.IsPmc.GetValueOrDefault(false))
{
return new RandomisedBotLevelResult
{
Exp = 0,
Level = 1
};
return new RandomisedBotLevelResult { Exp = 0, Level = 1 };
}
var expTable = _databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable;
var botLevelRange = GetRelativePmcBotLevelRange(botGenerationDetails, levelDetails, expTable.Length);
var botLevelRange = GetRelativePmcBotLevelRange(
botGenerationDetails,
levelDetails,
expTable.Length
);
// Get random level based on the exp table.
var exp = 0;
var level = int.Parse(
ChooseBotLevel(botLevelRange.Min, botLevelRange.Max, 1, 1.15)
.ToString()
ChooseBotLevel(botLevelRange.Min, botLevelRange.Max, 1, 1.15).ToString()
); // TODO - nasty double to string to int conversion
for (var i = 0; i < level; i++)
{
@@ -55,11 +58,7 @@ public class BotLevelGenerator(
exp += _randomUtil.GetInt(0, expTable[level].Experience.Value - 1);
}
return new RandomisedBotLevelResult
{
Level = level,
Exp = exp
};
return new RandomisedBotLevelResult { Level = level, Exp = exp };
}
public double ChooseBotLevel(double min, double max, int shift, double number)
@@ -74,7 +73,11 @@ public class BotLevelGenerator(
/// <param name="levelDetails"></param>
/// <param name="maxAvailableLevel">Max level allowed</param>
/// <returns>A MinMax of the lowest and highest level to generate the bots</returns>
public MinMax<int> GetRelativePmcBotLevelRange(BotGenerationDetails botGenerationDetails, MinMax<int> levelDetails, int maxAvailableLevel)
public MinMax<int> GetRelativePmcBotLevelRange(
BotGenerationDetails botGenerationDetails,
MinMax<int> levelDetails,
int maxAvailableLevel
)
{
var levelOverride = botGenerationDetails.LocationSpecificPmcLevelOverride;
@@ -93,12 +96,14 @@ public class BotLevelGenerator(
// Get min level relative to player if value exists
var minLevel = botGenerationDetails.PlayerLevel.HasValue
? botGenerationDetails.PlayerLevel.Value - botGenerationDetails.BotRelativeLevelDeltaMin.Value
? botGenerationDetails.PlayerLevel.Value
- botGenerationDetails.BotRelativeLevelDeltaMin.Value
: 1 - botGenerationDetails.BotRelativeLevelDeltaMin.Value;
// Get max level relative to player if value exists
var maxLevel = botGenerationDetails.PlayerLevel.HasValue
? botGenerationDetails.PlayerLevel.Value + botGenerationDetails.BotRelativeLevelDeltaMax.Value
? botGenerationDetails.PlayerLevel.Value
+ botGenerationDetails.BotRelativeLevelDeltaMax.Value
: 1 + botGenerationDetails.BotRelativeLevelDeltaMin.Value;
// Bound the level to the min/max possible
@@ -54,7 +54,7 @@ public class BotLootGenerator(
return new ItemSpawnLimitSettings
{
CurrentLimits = limitsForBotDict,
GlobalLimits = GetItemSpawnLimitsForBotType(botRole)
GlobalLimits = GetItemSpawnLimitsForBotType(botRole),
};
}
@@ -67,33 +67,46 @@ public class BotLootGenerator(
/// <param name="botRole">Role of bot, e.g. asssult</param>
/// <param name="botInventory">Inventory to add loot to</param>
/// <param name="botLevel">Level of bot</param>
public void GenerateLoot(string sessionId, BotType botJsonTemplate, bool isPmc, string botRole, BotBaseInventory botInventory, int botLevel)
public void GenerateLoot(
string sessionId,
BotType botJsonTemplate,
bool isPmc,
string botRole,
BotBaseInventory botInventory,
int botLevel
)
{
// Limits on item types to be added as loot
var itemCounts = botJsonTemplate.BotGeneration?.Items;
if (
itemCounts?.BackpackLoot.Weights is null ||
itemCounts.PocketLoot.Weights is null ||
itemCounts.VestLoot.Weights is null ||
itemCounts.SpecialItems.Weights is null ||
itemCounts.Healing.Weights is null ||
itemCounts.Drugs.Weights is null ||
itemCounts.Food.Weights is null ||
itemCounts.Drink.Weights is null ||
itemCounts.Currency.Weights is null ||
itemCounts.Stims.Weights is null ||
itemCounts.Grenades.Weights is null
itemCounts?.BackpackLoot.Weights is null
|| itemCounts.PocketLoot.Weights is null
|| itemCounts.VestLoot.Weights is null
|| itemCounts.SpecialItems.Weights is null
|| itemCounts.Healing.Weights is null
|| itemCounts.Drugs.Weights is null
|| itemCounts.Food.Weights is null
|| itemCounts.Drink.Weights is null
|| itemCounts.Currency.Weights is null
|| itemCounts.Stims.Weights is null
|| itemCounts.Grenades.Weights is null
)
{
_logger.Warning(_localisationService.GetText("bot-unable_to_generate_bot_loot", botRole));
_logger.Warning(
_localisationService.GetText("bot-unable_to_generate_bot_loot", botRole)
);
return;
}
var backpackLootCount = _weightedRandomHelper.GetWeightedValue(itemCounts.BackpackLoot.Weights);
var backpackLootCount = _weightedRandomHelper.GetWeightedValue(
itemCounts.BackpackLoot.Weights
);
var pocketLootCount = _weightedRandomHelper.GetWeightedValue(itemCounts.PocketLoot.Weights);
var vestLootCount = _weightedRandomHelper.GetWeightedValue(itemCounts.VestLoot.Weights);
var specialLootItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts.SpecialItems.Weights);
var specialLootItemCount = _weightedRandomHelper.GetWeightedValue(
itemCounts.SpecialItems.Weights
);
var healingItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts.Healing.Weights);
var drugItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts.Drugs.Weights);
var foodItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts.Food.Weights);
@@ -127,7 +140,12 @@ public class BotLootGenerator(
// Special items
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Special, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.Special,
botJsonTemplate
),
containersBotHasAvailable,
specialLootItemCount,
botInventory,
@@ -138,7 +156,12 @@ public class BotLootGenerator(
// Healing items / Meds
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.HealingItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.HealingItems,
botJsonTemplate
),
containersBotHasAvailable,
healingItemCount,
botInventory,
@@ -151,7 +174,12 @@ public class BotLootGenerator(
// Drugs
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrugItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.DrugItems,
botJsonTemplate
),
containersBotHasAvailable,
drugItemCount,
botInventory,
@@ -164,7 +192,12 @@ public class BotLootGenerator(
// Food
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.FoodItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.FoodItems,
botJsonTemplate
),
containersBotHasAvailable,
foodItemCount,
botInventory,
@@ -177,7 +210,12 @@ public class BotLootGenerator(
// Drink
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrinkItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.DrinkItems,
botJsonTemplate
),
containersBotHasAvailable,
drinkItemCount,
botInventory,
@@ -190,7 +228,12 @@ public class BotLootGenerator(
// Currency
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.CurrencyItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.CurrencyItems,
botJsonTemplate
),
containersBotHasAvailable,
currencyItemCount,
botInventory,
@@ -203,7 +246,12 @@ public class BotLootGenerator(
// Stims
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.StimItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.StimItems,
botJsonTemplate
),
containersBotHasAvailable,
stimItemCount,
botInventory,
@@ -216,7 +264,12 @@ public class BotLootGenerator(
// Grenades
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.GrenadeItems, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.GrenadeItems,
botJsonTemplate
),
[EquipmentSlots.Pockets, EquipmentSlots.TacticalVest], // Can't use containersBotHasEquipped as we don't want grenades added to backpack
grenadeCount,
botInventory,
@@ -270,7 +323,7 @@ public class BotLootGenerator(
// TacticalVest - generate loot if they have one
if (containersBotHasAvailable.Contains(EquipmentSlots.TacticalVest))
// Vest
// Vest
{
AddLootFromPool(
_botLootCacheService.GetLootFromCache(
@@ -316,7 +369,12 @@ public class BotLootGenerator(
if (!isPmc || (isPmc && _pmcConfig.AddSecureContainerLootFromBotConfig))
{
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Secure, botJsonTemplate),
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.Secure,
botJsonTemplate
),
[EquipmentSlots.SecuredContainer],
50,
botInventory,
@@ -337,7 +395,8 @@ public class BotLootGenerator(
return null;
}
var matchingValue = _pmcConfig?.LootItemLimitsRub?.FirstOrDefault(minMaxValue => botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
var matchingValue = _pmcConfig?.LootItemLimitsRub?.FirstOrDefault(minMaxValue =>
botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
);
return matchingValue;
@@ -357,7 +416,8 @@ public class BotLootGenerator(
return 0;
}
var matchingValue = _pmcConfig.MaxBackpackLootTotalRub.FirstOrDefault(minMaxValue => botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
var matchingValue = _pmcConfig.MaxBackpackLootTotalRub.FirstOrDefault(minMaxValue =>
botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
);
return matchingValue?.Value;
}
@@ -367,11 +427,17 @@ public class BotLootGenerator(
/// </summary>
/// <param name="botInventory">Bot to check</param>
/// <returns>Array of available slots</returns>
protected HashSet<EquipmentSlots> GetAvailableContainersBotCanStoreItemsIn(BotBaseInventory botInventory)
protected HashSet<EquipmentSlots> GetAvailableContainersBotCanStoreItemsIn(
BotBaseInventory botInventory
)
{
HashSet<EquipmentSlots> result = [EquipmentSlots.Pockets];
if ((botInventory.Items ?? []).Any(item => item.SlotId == nameof(EquipmentSlots.TacticalVest)))
if (
(botInventory.Items ?? []).Any(item =>
item.SlotId == nameof(EquipmentSlots.TacticalVest)
)
)
{
result.Add(EquipmentSlots.TacticalVest);
}
@@ -393,10 +459,7 @@ public class BotLootGenerator(
{
// surv12
AddLootFromPool(
new Dictionary<string, double>
{
{ "5d02797c86f774203f38e30a", 1 }
},
new Dictionary<string, double> { { "5d02797c86f774203f38e30a", 1 } },
[EquipmentSlots.SecuredContainer],
1,
botInventory,
@@ -408,10 +471,7 @@ public class BotLootGenerator(
// AFAK
AddLootFromPool(
new Dictionary<string, double>
{
{ "60098ad7c2240c0fe85c570a", 1 }
},
new Dictionary<string, double> { { "60098ad7c2240c0fe85c570a", 1 } },
[EquipmentSlots.SecuredContainer],
10,
botInventory,
@@ -434,8 +494,7 @@ public class BotLootGenerator(
/// <param name="containersIdFull"></param>
/// <param name="totalValueLimitRub">Total value of loot allowed in roubles</param>
/// <param name="isPmc">Is bot being generated for a pmc</param>
protected void AddLootFromPool
(
protected void AddLootFromPool(
Dictionary<string, double> pool,
HashSet<EquipmentSlots> equipmentSlots,
double totalItemCount,
@@ -471,12 +530,17 @@ public class BotLootGenerator(
if (!key)
{
_logger.Warning($"Unable to process item tpl: {weightedItemTpl} for slots: {equipmentSlots} on bot: {botRole}");
_logger.Warning(
$"Unable to process item tpl: {weightedItemTpl} for slots: {equipmentSlots} on bot: {botRole}"
);
continue;
}
if (itemSpawnLimits is not null && ItemHasReachedSpawnLimit(itemToAddTemplate, botRole, itemSpawnLimits))
if (
itemSpawnLimits is not null
&& ItemHasReachedSpawnLimit(itemToAddTemplate, botRole, itemSpawnLimits)
)
{
// Remove item from pool to prevent it being picked again
pool.Remove(weightedItemTpl);
@@ -492,14 +556,19 @@ public class BotLootGenerator(
{
Id = newRootItemId,
Template = itemToAddTemplate?.Id ?? string.Empty,
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(itemToAddTemplate, botRole)
}
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(
itemToAddTemplate,
botRole
),
},
];
// Is Simple-Wallet / WZ wallet
if (_botConfig.WalletLoot.WalletTplPool.Contains(weightedItemTpl))
{
var addCurrencyToWallet = _randomUtil.GetChance100(_botConfig.WalletLoot.ChancePercent);
var addCurrencyToWallet = _randomUtil.GetChance100(
_botConfig.WalletLoot.ChancePercent
);
if (addCurrencyToWallet)
{
// Create the currency items we want to add to wallet
@@ -552,7 +621,9 @@ public class BotLootGenerator(
// Bot has no container to put item in, exit
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to add: {totalItemCount} items to bot as it lacks a container to include them");
_logger.Debug(
$"Unable to add: {totalItemCount} items to bot as it lacks a container to include them"
);
}
break;
@@ -564,9 +635,9 @@ public class BotLootGenerator(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
$"Failed placing item: {itemToAddTemplate.Id} - {itemToAddTemplate.Name}: {i} of: {totalItemCount} items into: {botRole} " +
$"containers: {string.Join(",", equipmentSlots)}. Tried: {fitItemIntoContainerAttempts} " +
$"times, reason: {itemAddedResult}, skipping"
$"Failed placing item: {itemToAddTemplate.Id} - {itemToAddTemplate.Name}: {i} of: {totalItemCount} items into: {botRole} "
+ $"containers: {string.Join(",", equipmentSlots)}. Tried: {fitItemIntoContainerAttempts} "
+ $"times, reason: {itemAddedResult}, skipping"
);
}
@@ -609,19 +680,20 @@ public class BotLootGenerator(
for (var index = 0; index < itemCount; index++)
{
// Choose the size of the currency stack - default is 5k, 10k, 15k, 20k, 25k
var chosenStackCount = _weightedRandomHelper.GetWeightedValue(_botConfig.WalletLoot.StackSizeWeight);
var chosenStackCount = _weightedRandomHelper.GetWeightedValue(
_botConfig.WalletLoot.StackSizeWeight
);
List<Item> items =
[
new()
{
Id = _hashUtil.Generate(),
Template = _weightedRandomHelper.GetWeightedValue(_botConfig.WalletLoot.CurrencyWeight),
Template = _weightedRandomHelper.GetWeightedValue(
_botConfig.WalletLoot.CurrencyWeight
),
ParentId = walletId,
Upd = new Upd
{
StackObjectsCount = int.Parse(chosenStackCount)
}
}
Upd = new Upd { StackObjectsCount = int.Parse(chosenStackCount) },
},
];
result.Add(items);
}
@@ -636,7 +708,12 @@ public class BotLootGenerator(
/// <param name="itemToAddChildrenTo">Item to add children to</param>
/// <param name="isPmc">Is the item being generated for a pmc (affects money/ammo stack sizes)</param>
/// <param name="botRole">role bot has that owns item</param>
public void AddRequiredChildItemsToParent(TemplateItem? itemToAddTemplate, List<Item> itemToAddChildrenTo, bool isPmc, string botRole)
public void AddRequiredChildItemsToParent(
TemplateItem? itemToAddTemplate,
List<Item> itemToAddChildrenTo,
bool isPmc,
string botRole
)
{
// Fill ammo box
if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.AMMO_BOX))
@@ -672,7 +749,8 @@ public class BotLootGenerator(
/// <param name="isPmc">are we generating for a pmc</param>
/// <param name="botLevel"></param>
/// <param name="containersIdFull"></param>
public void AddLooseWeaponsToInventorySlot(string sessionId,
public void AddLooseWeaponsToInventorySlot(
string sessionId,
BotBaseInventory botInventory,
EquipmentSlots equipmentSlot,
BotTypeInventory? templateInventory,
@@ -680,14 +758,15 @@ public class BotLootGenerator(
string botRole,
bool isPmc,
int botLevel,
HashSet<string>? containersIdFull)
HashSet<string>? containersIdFull
)
{
var chosenWeaponType = _randomUtil.GetArrayValue<string>(
[
EquipmentSlots.FirstPrimaryWeapon.ToString(),
EquipmentSlots.FirstPrimaryWeapon.ToString(),
EquipmentSlots.FirstPrimaryWeapon.ToString(),
EquipmentSlots.Holster.ToString()
EquipmentSlots.Holster.ToString(),
]
);
var randomisedWeaponCount = _randomUtil.GetInt(
@@ -716,7 +795,9 @@ public class BotLootGenerator(
var weaponRootItem = generatedWeapon.Weapon?.FirstOrDefault();
if (weaponRootItem is null)
{
_logger.Error($"Generated loose weapon: {chosenWeaponType} for: {botRole} level: {botLevel} was null, skipping");
_logger.Error(
$"Generated loose weapon: {chosenWeaponType} for: {botRole} level: {botLevel} was null, skipping"
);
continue;
}
@@ -733,7 +814,9 @@ public class BotLootGenerator(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Failed to add additional weapon: {weaponRootItem.Id} to bot backpack, reason: {result.ToString()}");
_logger.Debug(
$"Failed to add additional weapon: {weaponRootItem.Id} to bot backpack, reason: {result.ToString()}"
);
}
}
}
@@ -746,11 +829,15 @@ public class BotLootGenerator(
/// <param name="botRole">Bot type</param>
/// <param name="itemSpawnLimits"></param>
/// <returns>true if item has reached spawn limit</returns>
protected bool ItemHasReachedSpawnLimit(TemplateItem? itemTemplate, string botRole, ItemSpawnLimitSettings? itemSpawnLimits)
protected bool ItemHasReachedSpawnLimit(
TemplateItem? itemTemplate,
string botRole,
ItemSpawnLimitSettings? itemSpawnLimits
)
{
// PMCs and scavs have different sections of bot config for spawn limits
if (itemSpawnLimits is not null && itemSpawnLimits.GlobalLimits?.Count == 0)
// No items found in spawn limit, drop out
// No items found in spawn limit, drop out
{
return false;
}
@@ -763,24 +850,24 @@ public class BotLootGenerator(
var idToCheckFor = GetMatchingIdFromSpawnLimits(itemTemplate, itemSpawnLimits.GlobalLimits);
if (idToCheckFor is null)
// ParentId or tplid not found in spawnLimits, not a spawn limited item, skip
// ParentId or tplid not found in spawnLimits, not a spawn limited item, skip
{
return false;
}
// Use tryAdd to see if it exists, and automatically add 1
if (!itemSpawnLimits.CurrentLimits.TryAdd(idToCheckFor, 1))
// if it does exist, come in here and increment
// Increment item count with this bot type
// if it does exist, come in here and increment
// Increment item count with this bot type
{
itemSpawnLimits.CurrentLimits[idToCheckFor]++;
}
// Check if over limit
var currentLimitCount = itemSpawnLimits.CurrentLimits[idToCheckFor];
if (itemSpawnLimits.CurrentLimits[idToCheckFor] > itemSpawnLimits.GlobalLimits[idToCheckFor])
if (
itemSpawnLimits.CurrentLimits[idToCheckFor] > itemSpawnLimits.GlobalLimits[idToCheckFor]
)
{
// Prevent edge-case of small loot pools + code trying to add limited item over and over infinitely
if (currentLimitCount > currentLimitCount * 10)
@@ -794,7 +881,7 @@ public class BotLootGenerator(
{
botRole,
itemName = itemTemplate.Name,
attempts = currentLimitCount
attempts = currentLimitCount,
}
)
);
@@ -827,7 +914,9 @@ public class BotLootGenerator(
_itemHelper.AddUpdObjectToItem(moneyItem);
moneyItem.Upd.StackObjectsCount = int.Parse(_weightedRandomHelper.GetWeightedValue(currencyWeight));
moneyItem.Upd.StackObjectsCount = int.Parse(
_weightedRandomHelper.GetWeightedValue(currencyWeight)
);
}
/// <summary>
@@ -862,7 +951,12 @@ public class BotLootGenerator(
return _botConfig.ItemSpawnLimits[botRole.ToLower()];
}
_logger.Warning(_localisationService.GetText("bot-unable_to_find_spawn_limits_fallback_to_defaults", botRole));
_logger.Warning(
_localisationService.GetText(
"bot-unable_to_find_spawn_limits_fallback_to_defaults",
botRole
)
);
return new Dictionary<string, double>();
}
@@ -873,7 +967,10 @@ public class BotLootGenerator(
/// <param name="itemTemplate">item we want to look for in spawn limits</param>
/// <param name="spawnLimits">Limits to check for item</param>
/// <returns>id as string, otherwise undefined</returns>
public string? GetMatchingIdFromSpawnLimits(TemplateItem itemTemplate, Dictionary<string, double> spawnLimits)
public string? GetMatchingIdFromSpawnLimits(
TemplateItem itemTemplate,
Dictionary<string, double> spawnLimits
)
{
if (spawnLimits.ContainsKey(itemTemplate.Id))
{
@@ -36,7 +36,9 @@ public class BotWeaponGenerator(
{
protected const string _modMagazineSlotId = "mod_magazine";
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
protected IEnumerable<IInventoryMagGen> _inventoryMagGenComponents = MagGenSetUp(inventoryMagGenComponents);
protected IEnumerable<IInventoryMagGen> _inventoryMagGenComponents = MagGenSetUp(
inventoryMagGenComponents
);
protected PmcConfig _pmcConfig = _configServer.GetConfig<PmcConfig>();
protected RepairConfig _repairConfig = _configServer.GetConfig<RepairConfig>();
@@ -59,8 +61,16 @@ public class BotWeaponGenerator(
/// <param name="isPmc">Is weapon generated for a pmc</param>
/// <param name="botLevel"></param>
/// <returns>GenerateWeaponResult object</returns>
public GenerateWeaponResult GenerateRandomWeapon(string sessionId, string equipmentSlot, BotTypeInventory botTemplateInventory, string weaponParentId,
Dictionary<string, double> modChances, string botRole, bool isPmc, int botLevel)
public GenerateWeaponResult GenerateRandomWeapon(
string sessionId,
string equipmentSlot,
BotTypeInventory botTemplateInventory,
string weaponParentId,
Dictionary<string, double> modChances,
string botRole,
bool isPmc,
int botLevel
)
{
var weaponTpl = PickWeightedWeaponTemplateFromPool(equipmentSlot, botTemplateInventory);
return GenerateWeaponByTpl(
@@ -82,7 +92,10 @@ public class BotWeaponGenerator(
/// <param name="equipmentSlot">Primary/secondary/holster</param>
/// <param name="botTemplateInventory">e.g. assault.json</param>
/// <returns>Weapon template</returns>
public string PickWeightedWeaponTemplateFromPool(string equipmentSlot, BotTypeInventory botTemplateInventory)
public string PickWeightedWeaponTemplateFromPool(
string equipmentSlot,
BotTypeInventory botTemplateInventory
)
{
if (!Enum.TryParse(equipmentSlot, out EquipmentSlots key))
{
@@ -106,8 +119,17 @@ public class BotWeaponGenerator(
/// <param name="isPmc">Is weapon being generated for a PMC.</param>
/// <param name="botLevel">The level of the bot.</param>
/// <returns>GenerateWeaponResult object.</returns>
public GenerateWeaponResult? GenerateWeaponByTpl(string sessionId, string weaponTpl, string slotName, BotTypeInventory botTemplateInventory,
string weaponParentId, Dictionary<string, double> modChances, string botRole, bool isPmc, int botLevel)
public GenerateWeaponResult? GenerateWeaponByTpl(
string sessionId,
string weaponTpl,
string slotName,
BotTypeInventory botTemplateInventory,
string weaponParentId,
Dictionary<string, double> modChances,
string botRole,
bool isPmc,
int botLevel
)
{
var modPool = botTemplateInventory.Mods;
var weaponItemTemplate = _itemHelper.GetItem(weaponTpl).Value;
@@ -140,7 +162,7 @@ public class BotWeaponGenerator(
// Chance to add randomised weapon enhancement
if (isPmc && _randomUtil.GetChance100(_pmcConfig.WeaponHasEnhancementChancePercent))
// Add buff to weapon root
// Add buff to weapon root
{
_repairService.AddBuff(_repairConfig.RepairKit.Weapon, weaponWithModsArray[0]);
}
@@ -166,11 +188,11 @@ public class BotWeaponGenerator(
{
Role = botRole,
Level = botLevel,
EquipmentRole = botEquipmentRole
EquipmentRole = botEquipmentRole,
},
ModLimits = modLimits,
WeaponStats = new WeaponStats(),
ConflictingItemTpls = new HashSet<string>()
ConflictingItemTpls = new HashSet<string>(),
};
weaponWithModsArray = _botEquipmentModGenerator.GenerateModsForWeapon(
sessionId,
@@ -180,7 +202,7 @@ public class BotWeaponGenerator(
// Use weapon preset from globals.json if weapon isn't valid
if (!IsWeaponValid(weaponWithModsArray, botRole))
// Weapon is bad, fall back to weapons preset
// Weapon is bad, fall back to weapons preset
{
weaponWithModsArray = GetPresetWeaponMods(
weaponTpl,
@@ -191,7 +213,9 @@ public class BotWeaponGenerator(
);
}
var tempList = _cloner.Clone(weaponWithModsArray.Where(item => item.SlotId == _modMagazineSlotId));
var tempList = _cloner.Clone(
weaponWithModsArray.Where(item => item.SlotId == _modMagazineSlotId)
);
// Fill existing magazines to full and sync ammo type
foreach (var magazine in tempList)
{
@@ -199,11 +223,18 @@ public class BotWeaponGenerator(
}
// Add cartridge(s) to gun chamber(s)
if (weaponItemTemplate.Properties?.Chambers?.Count > 0 &&
weaponItemTemplate.Properties.Chambers.FirstOrDefault().Props.Filters.FirstOrDefault().Filter.Contains(ammoTpl))
if (
weaponItemTemplate.Properties?.Chambers?.Count > 0
&& weaponItemTemplate
.Properties.Chambers.FirstOrDefault()
.Props.Filters.FirstOrDefault()
.Filter.Contains(ammoTpl)
)
{
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
var chamberSlotNames = weaponItemTemplate.Properties.Chambers.Select(chamberSlot => chamberSlot.Name);
var chamberSlotNames = weaponItemTemplate.Properties.Chambers.Select(chamberSlot =>
chamberSlot.Name
);
AddCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames.ToList());
}
@@ -228,7 +259,7 @@ public class BotWeaponGenerator(
ChosenAmmoTemplate = ammoTpl,
ChosenUbglAmmoTemplate = ubglAmmoTpl,
WeaponMods = modPool,
WeaponTemplate = weaponItemTemplate
WeaponTemplate = weaponItemTemplate,
};
}
@@ -239,7 +270,11 @@ public class BotWeaponGenerator(
/// <param name="weaponWithModsList">Weapon and mods</param>
/// <param name="ammoTemplate">Cartridge to add to weapon</param>
/// <param name="chamberSlotIds">Name of slots to create or add ammo to</param>
protected void AddCartridgeToChamber(List<Item> weaponWithModsList, string ammoTemplate, List<string> chamberSlotIds)
protected void AddCartridgeToChamber(
List<Item> weaponWithModsList,
string ammoTemplate,
List<string> chamberSlotIds
)
{
foreach (var slotId in chamberSlotIds)
{
@@ -254,10 +289,7 @@ public class BotWeaponGenerator(
Template = ammoTemplate,
ParentId = weaponWithModsList[0].Id,
SlotId = slotId,
Upd = new Upd
{
StackObjectsCount = 1
}
Upd = new Upd { StackObjectsCount = 1 },
}
);
}
@@ -265,10 +297,7 @@ public class BotWeaponGenerator(
{
// Already exists, update values
existingItemWithSlot.Template = ammoTemplate;
existingItemWithSlot.Upd = new Upd
{
StackObjectsCount = 1
};
existingItemWithSlot.Upd = new Upd { StackObjectsCount = 1 };
}
}
}
@@ -283,8 +312,13 @@ public class BotWeaponGenerator(
/// <param name="weaponItemTemplate">Database template for weapon</param>
/// <param name="botRole">For durability values</param>
/// <returns>Base weapon item in a list</returns>
protected List<Item> ConstructWeaponBaseList(string weaponTemplate, string weaponParentId, string equipmentSlot, TemplateItem weaponItemTemplate,
string botRole)
protected List<Item> ConstructWeaponBaseList(
string weaponTemplate,
string weaponParentId,
string equipmentSlot,
TemplateItem weaponItemTemplate,
string botRole
)
{
return
[
@@ -294,8 +328,11 @@ public class BotWeaponGenerator(
Template = weaponTemplate,
ParentId = weaponParentId,
SlotId = equipmentSlot,
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(weaponItemTemplate, botRole)
}
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(
weaponItemTemplate,
botRole
),
},
];
}
@@ -308,10 +345,21 @@ public class BotWeaponGenerator(
/// <param name="itemTemplate">Item template</param>
/// <param name="botRole">Bot role</param>
/// <returns>List of weapon mods</returns>
protected List<Item> GetPresetWeaponMods(string weaponTemplate, string equipmentSlot, string weaponParentId, TemplateItem itemTemplate, string botRole)
protected List<Item> GetPresetWeaponMods(
string weaponTemplate,
string equipmentSlot,
string weaponParentId,
TemplateItem itemTemplate,
string botRole
)
{
// Invalid weapon generated, fallback to preset
_logger.Warning(_localisationService.GetText("bot-weapon_generated_incorrect_using_default", $"{weaponTemplate} - {itemTemplate.Name}"));
_logger.Warning(
_localisationService.GetText(
"bot-weapon_generated_incorrect_using_default",
$"{weaponTemplate} - {itemTemplate.Name}"
)
);
List<Item> weaponMods = [];
// TODO: Preset weapons trigger a lot of warnings regarding missing ammo in magazines & such
@@ -331,13 +379,18 @@ public class BotWeaponGenerator(
var parentItem = preset.Items[0];
parentItem.ParentId = weaponParentId;
parentItem.SlotId = equipmentSlot;
parentItem.Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(itemTemplate, botRole);
parentItem.Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(
itemTemplate,
botRole
);
preset.Items[0] = parentItem;
weaponMods.AddRange(preset.Items);
}
else
{
_logger.Error(_localisationService.GetText("bot-missing_weapon_preset", weaponTemplate));
_logger.Error(
_localisationService.GetText("bot-missing_weapon_preset", weaponTemplate)
);
}
return weaponMods;
@@ -360,10 +413,16 @@ public class BotWeaponGenerator(
}
// Iterate over required slots in db item, check mod exists for that slot
foreach (var modSlotTemplate in modTemplate.Properties.Slots?.Where(slot => slot.Required.GetValueOrDefault(false)) ?? [])
foreach (
var modSlotTemplate in modTemplate.Properties.Slots?.Where(slot =>
slot.Required.GetValueOrDefault(false)
) ?? []
)
{
var slotName = modSlotTemplate.Name;
var hasWeaponSlotItem = weaponItemList.Any(weaponItem => weaponItem.ParentId == mod.Id && weaponItem.SlotId == slotName);
var hasWeaponSlotItem = weaponItemList.Any(weaponItem =>
weaponItem.ParentId == mod.Id && weaponItem.SlotId == slotName
);
if (!hasWeaponSlotItem)
{
_logger.Warning(
@@ -374,7 +433,7 @@ public class BotWeaponGenerator(
modSlot = modSlotTemplate.Name,
modName = modTemplate.Name,
slotId = mod.SlotId,
botRole
botRole,
}
)
);
@@ -395,16 +454,27 @@ public class BotWeaponGenerator(
/// <param name="magWeights">Magazine weights for count to add to inventory</param>
/// <param name="inventory">Inventory to add magazines to</param>
/// <param name="botRole">The bot type we're generating extra mags for</param>
public void AddExtraMagazinesToInventory(GenerateWeaponResult generatedWeaponResult, GenerationData magWeights, BotBaseInventory inventory, string botRole)
public void AddExtraMagazinesToInventory(
GenerateWeaponResult generatedWeaponResult,
GenerationData magWeights,
BotBaseInventory inventory,
string botRole
)
{
var weaponAndMods = generatedWeaponResult.Weapon;
var weaponTemplate = generatedWeaponResult.WeaponTemplate;
var magazineTpl = GetMagazineTemplateFromWeaponTemplate(weaponAndMods, weaponTemplate, botRole);
var magazineTpl = GetMagazineTemplateFromWeaponTemplate(
weaponAndMods,
weaponTemplate,
botRole
);
var magTemplate = _itemHelper.GetItem(magazineTpl).Value;
if (magTemplate is null)
{
_logger.Error(_localisationService.GetText("bot-unable_to_find_magazine_item", magazineTpl));
_logger.Error(
_localisationService.GetText("bot-unable_to_find_magazine_item", magazineTpl)
);
return;
}
@@ -414,7 +484,10 @@ public class BotWeaponGenerator(
if (!ammoTemplate.Key)
{
_logger.Error(
_localisationService.GetText("bot-unable_to_find_ammo_item", generatedWeaponResult.ChosenAmmoTemplate)
_localisationService.GetText(
"bot-unable_to_find_ammo_item",
generatedWeaponResult.ChosenAmmoTemplate
)
);
return;
@@ -434,7 +507,8 @@ public class BotWeaponGenerator(
inventory
);
_inventoryMagGenComponents.FirstOrDefault(v => v.CanHandleInventoryMagGen(inventoryMagGenModel))
_inventoryMagGenComponents
.FirstOrDefault(v => v.CanHandleInventoryMagGen(inventoryMagGenModel))
.Process(inventoryMagGenModel);
// Add x stacks of bullets to SecuredContainer (bots use a magic mag packing skill to reload instantly)
@@ -452,7 +526,11 @@ public class BotWeaponGenerator(
/// <param name="weaponMods">Weapon list with mods</param>
/// <param name="generatedWeaponResult">Result of weapon generation</param>
/// <param name="inventory">Bot inventory to add grenades to</param>
protected void AddUbglGrenadesToBotInventory(List<Item> weaponMods, GenerateWeaponResult generatedWeaponResult, BotBaseInventory inventory)
protected void AddUbglGrenadesToBotInventory(
List<Item> weaponMods,
GenerateWeaponResult generatedWeaponResult,
BotBaseInventory inventory
)
{
// Find ubgl mod item + get details of it from db
var ubglMod = weaponMods.FirstOrDefault(x => x.SlotId == "mod_launcher");
@@ -461,16 +539,14 @@ public class BotWeaponGenerator(
// Define min/max of how many grenades bot will have
GenerationData ubglMinMax = new()
{
Weights = new Dictionary<double, double>
{
{ 1, 1 },
{ 2, 1 }
},
Whitelist = new Dictionary<string, double>()
Weights = new Dictionary<double, double> { { 1, 1 }, { 2, 1 } },
Whitelist = new Dictionary<string, double>(),
};
// get ammo template from db
var ubglAmmoDbTemplate = _itemHelper.GetItem(generatedWeaponResult.ChosenUbglAmmoTemplate).Value;
var ubglAmmoDbTemplate = _itemHelper
.GetItem(generatedWeaponResult.ChosenUbglAmmoTemplate)
.Value;
// Add greandes to bot inventory
var ubglAmmoGenModel = new InventoryMagGen(
@@ -495,16 +571,18 @@ public class BotWeaponGenerator(
/// <param name="ammoTpl">Ammo type to add.</param>
/// <param name="stackSize">Size of the ammo stack to add.</param>
/// <param name="inventory">Player inventory.</param>
protected void AddAmmoToSecureContainer(int stackCount, string ammoTpl, int stackSize, BotBaseInventory inventory)
protected void AddAmmoToSecureContainer(
int stackCount,
string ammoTpl,
int stackSize,
BotBaseInventory inventory
)
{
for (var i = 0; i < stackCount; i++)
{
var id = _hashUtil.Generate();
_botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
new HashSet<EquipmentSlots>
{
EquipmentSlots.SecuredContainer
},
new HashSet<EquipmentSlots> { EquipmentSlots.SecuredContainer },
id,
ammoTpl,
new List<Item>
@@ -513,11 +591,8 @@ public class BotWeaponGenerator(
{
Id = id,
Template = ammoTpl,
Upd = new Upd
{
StackObjectsCount = stackSize
}
}
Upd = new Upd { StackObjectsCount = stackSize },
},
},
inventory
);
@@ -531,7 +606,11 @@ public class BotWeaponGenerator(
/// <param name="weaponTemplate">Weapon to get magazine template for.</param>
/// <param name="botRole">The bot type we are getting the magazine for.</param>
/// <returns>Magazine template string.</returns>
protected string GetMagazineTemplateFromWeaponTemplate(List<Item> weaponMods, TemplateItem weaponTemplate, string botRole)
protected string GetMagazineTemplateFromWeaponTemplate(
List<Item> weaponMods,
TemplateItem weaponTemplate,
string botRole
)
{
var magazine = weaponMods.FirstOrDefault(m => m.SlotId == _modMagazineSlotId);
if (magazine is null)
@@ -545,21 +624,19 @@ public class BotWeaponGenerator(
// log error if no magazine AND not a chamber loaded weapon (e.g. shotgun revolver)
if (!weaponTemplate.Properties.IsChamberLoad ?? false)
// Shouldn't happen
// Shouldn't happen
{
_logger.Warning(
_localisationService.GetText(
"bot-weapon_missing_magazine_or_chamber",
new
{
weaponId = weaponTemplate.Id,
botRole
}
new { weaponId = weaponTemplate.Id, botRole }
)
);
}
var defaultMagTplId = _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(weaponTemplate);
var defaultMagTplId = _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(
weaponTemplate
);
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
@@ -579,10 +656,16 @@ public class BotWeaponGenerator(
/// <param name="cartridgePool">Dictionary of all cartridges keyed by type e.g. Caliber556x45NATO</param>
/// <param name="weaponTemplate">Weapon details from database we want to pick ammo for</param>
/// <returns>Ammo template that works with the desired gun</returns>
protected string? GetWeightedCompatibleAmmo(Dictionary<string, Dictionary<string, double>> cartridgePool, TemplateItem weaponTemplate)
protected string? GetWeightedCompatibleAmmo(
Dictionary<string, Dictionary<string, double>> cartridgePool,
TemplateItem weaponTemplate
)
{
var desiredCaliber = GetWeaponCaliber(weaponTemplate);
if (!cartridgePool.TryGetValue(desiredCaliber, out var cartridgePoolForWeapon) || cartridgePoolForWeapon?.Count == 0)
if (
!cartridgePool.TryGetValue(desiredCaliber, out var cartridgePoolForWeapon)
|| cartridgePoolForWeapon?.Count == 0
)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
@@ -593,7 +676,7 @@ public class BotWeaponGenerator(
{
weaponId = weaponTemplate.Id,
weaponName = weaponTemplate.Name,
defaultAmmo = weaponTemplate.Properties.DefAmmo
defaultAmmo = weaponTemplate.Properties.DefAmmo,
}
)
);
@@ -605,9 +688,11 @@ public class BotWeaponGenerator(
}
// Get cartridges the weapons first chamber allow
var compatibleCartridgesInTemplate = GetCompatibleCartridgesFromWeaponTemplate(weaponTemplate);
var compatibleCartridgesInTemplate = GetCompatibleCartridgesFromWeaponTemplate(
weaponTemplate
);
if (compatibleCartridgesInTemplate.Count == 0)
// No chamber data found in weapon, send default
// No chamber data found in weapon, send default
{
return weaponTemplate.Properties.DefAmmo;
}
@@ -626,7 +711,9 @@ public class BotWeaponGenerator(
if (!compatibleCartridges.Any())
{
// Get cartridges from the weapons first magazine in filters
var compatibleCartridgesInMagazine = GetCompatibleCartridgesFromMagazineTemplate(weaponTemplate);
var compatibleCartridgesInMagazine = GetCompatibleCartridgesFromMagazineTemplate(
weaponTemplate
);
if (compatibleCartridgesInMagazine.Count == 0)
{
// No compatible cartridges found in magazine, use default
@@ -634,7 +721,9 @@ public class BotWeaponGenerator(
}
// Get the caliber data from the first compatible round in the magazine
var magazineCaliberData = _itemHelper.GetItem(compatibleCartridgesInMagazine.FirstOrDefault()).Value.Properties.Caliber;
var magazineCaliberData = _itemHelper
.GetItem(compatibleCartridgesInMagazine.FirstOrDefault())
.Value.Properties.Caliber;
cartridgePoolForWeapon = cartridgePool[magazineCaliberData];
foreach (var cartridgeKvP in cartridgePoolForWeapon)
@@ -664,7 +753,9 @@ public class BotWeaponGenerator(
{
ArgumentNullException.ThrowIfNull(weaponTemplate);
var cartridges = weaponTemplate.Properties?.Chambers?.FirstOrDefault()?.Props?.Filters?[0].Filter;
var cartridges = weaponTemplate
.Properties?.Chambers?.FirstOrDefault()
?.Props?.Filters?[0].Filter;
if (cartridges is not null)
{
return cartridges;
@@ -680,26 +771,39 @@ public class BotWeaponGenerator(
/// <param name="weaponTemplate">Weapon db template to get magazine cartridges for</param>
/// <returns>Hashset of cartridge tpls</returns>
/// <exception cref="ArgumentNullException">Thrown when weaponTemplate is null.</exception>
protected HashSet<string> GetCompatibleCartridgesFromMagazineTemplate(TemplateItem weaponTemplate)
protected HashSet<string> GetCompatibleCartridgesFromMagazineTemplate(
TemplateItem weaponTemplate
)
{
ArgumentNullException.ThrowIfNull(weaponTemplate);
// Get the first magazine's template from the weapon
var magazineSlot = weaponTemplate.Properties.Slots?.FirstOrDefault(slot => slot.Name == "mod_magazine");
var magazineSlot = weaponTemplate.Properties.Slots?.FirstOrDefault(slot =>
slot.Name == "mod_magazine"
);
if (magazineSlot is null)
{
return [];
}
var magazineTemplate = _itemHelper.GetItem(magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault());
var magazineTemplate = _itemHelper.GetItem(
magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault()
);
if (!magazineTemplate.Key)
{
return [];
}
// Try to get cartridges from slots array first, if none found, try Cartridges array
var cartridges = magazineTemplate.Value.Properties.Slots.FirstOrDefault()?.Props?.Filters.FirstOrDefault()?.Filter
?? magazineTemplate.Value.Properties.Cartridges.FirstOrDefault()?.Props?.Filters.FirstOrDefault()?.Filter;
var cartridges =
magazineTemplate
.Value.Properties.Slots.FirstOrDefault()
?.Props?.Filters.FirstOrDefault()
?.Filter
?? magazineTemplate
.Value.Properties.Cartridges.FirstOrDefault()
?.Props?.Filters.FirstOrDefault()
?.Filter;
return cartridges ?? [];
}
@@ -717,7 +821,7 @@ public class BotWeaponGenerator(
}
if (!string.IsNullOrEmpty(weaponTemplate.Properties.AmmoCaliber))
// 9x18pmm has a typo, should be Caliber9x18PM
// 9x18pmm has a typo, should be Caliber9x18PM
{
return weaponTemplate.Properties.AmmoCaliber == "Caliber9x18PMM"
? "Caliber9x18PM"
@@ -729,9 +833,7 @@ public class BotWeaponGenerator(
var ammoInChamber = _itemHelper.GetItem(
weaponTemplate.Properties.Chambers[0].Props.Filters[0].Filter.FirstOrDefault()
);
return !ammoInChamber.Key
? null
: ammoInChamber.Value.Properties.Caliber;
return !ammoInChamber.Key ? null : ammoInChamber.Value.Properties.Caliber;
}
return null;
@@ -743,12 +845,18 @@ public class BotWeaponGenerator(
/// <param name="weaponMods">Weapon with children</param>
/// <param name="magazine">Magazine item</param>
/// <param name="cartridgeTemplate">Cartridge to insert into magazine</param>
protected void FillExistingMagazines(List<Item> weaponMods, Item magazine, string cartridgeTemplate)
protected void FillExistingMagazines(
List<Item> weaponMods,
Item magazine,
string cartridgeTemplate
)
{
var magazineTemplate = _itemHelper.GetItem(magazine.Template).Value;
if (magazineTemplate is null)
{
_logger.Error(_localisationService.GetText("bot-unable_to_find_magazine_item", magazine.Template));
_logger.Error(
_localisationService.GetText("bot-unable_to_find_magazine_item", magazine.Template)
);
return;
}
@@ -765,7 +873,12 @@ public class BotWeaponGenerator(
}
else
{
AddOrUpdateMagazinesChildWithAmmo(weaponMods, magazine, cartridgeTemplate, magazineTemplate);
AddOrUpdateMagazinesChildWithAmmo(
weaponMods,
magazine,
cartridgeTemplate,
magazineTemplate
);
}
}
@@ -784,10 +897,7 @@ public class BotWeaponGenerator(
Template = ubglAmmoTpl,
ParentId = ubglMod.Id,
SlotId = "patron_in_weapon",
Upd = new Upd
{
StackObjectsCount = 1
}
Upd = new Upd { StackObjectsCount = 1 },
}
);
}
@@ -799,9 +909,15 @@ public class BotWeaponGenerator(
/// <param name="magazine">Magazine item details we're adding cartridges to</param>
/// <param name="chosenAmmoTpl">Cartridge to put into the magazine</param>
/// <param name="magazineTemplate">Magazines db template</param>
protected void AddOrUpdateMagazinesChildWithAmmo(List<Item> weaponWithMods, Item magazine, string chosenAmmoTpl, TemplateItem magazineTemplate)
protected void AddOrUpdateMagazinesChildWithAmmo(
List<Item> weaponWithMods,
Item magazine,
string chosenAmmoTpl,
TemplateItem magazineTemplate
)
{
var magazineCartridgeChildItem = weaponWithMods.FirstOrDefault(m => m.ParentId == magazine.Id && m.SlotId == "cartridges"
var magazineCartridgeChildItem = weaponWithMods.FirstOrDefault(m =>
m.ParentId == magazine.Id && m.SlotId == "cartridges"
);
if (magazineCartridgeChildItem is not null)
{
@@ -813,13 +929,20 @@ public class BotWeaponGenerator(
List<Item> magazineWithCartridges = [magazine];
// Add cartridges as children to above mag array
_itemHelper.FillMagazineWithCartridge(magazineWithCartridges, magazineTemplate, chosenAmmoTpl, 1);
_itemHelper.FillMagazineWithCartridge(
magazineWithCartridges,
magazineTemplate,
chosenAmmoTpl,
1
);
// Replace existing magazine with above array of mag + cartridge stacks
var magazineIndex = weaponWithMods.FindIndex(i => i.Id == magazine.Id); // magazineWithCartridges
if (magazineIndex == -1)
{
_logger.Error($"Unable to add cartridges: {chosenAmmoTpl} to magazine: {magazine.Id} as none found");
_logger.Error(
$"Unable to add cartridges: {chosenAmmoTpl} to magazine: {magazine.Id} as none found"
);
return;
}
@@ -842,7 +965,9 @@ public class BotWeaponGenerator(
// This might not be necessary since we already filled the camoras with a random whitelisted and compatible ammo type,
// but I'm not sure whether this is also used elsewhere
var camoras = weaponMods
.Where(x => x.ParentId == magazineId && x.SlotId.StartsWith("camora", StringComparison.Ordinal))
.Where(x =>
x.ParentId == magazineId && x.SlotId.StartsWith("camora", StringComparison.Ordinal)
)
.ToList();
if (camoras.Count == 0)
@@ -859,10 +984,7 @@ public class BotWeaponGenerator(
}
else
{
camora.Upd = new Upd
{
StackObjectsCount = 1
};
camora.Upd = new Upd { StackObjectsCount = 1 };
}
}
}
@@ -60,24 +60,29 @@ public class FenceBaseAssortGenerator(
// Item base type blacklisted
if (traderConfig.Fence.Blacklist.Count > 0)
{
if (traderConfig.Fence.Blacklist.Contains(rootItemDb.Id) ||
itemHelper.IsOfBaseclasses(rootItemDb.Id, traderConfig.Fence.Blacklist)
)
if (
traderConfig.Fence.Blacklist.Contains(rootItemDb.Id)
|| itemHelper.IsOfBaseclasses(rootItemDb.Id, traderConfig.Fence.Blacklist)
)
{
continue;
}
}
// Only allow rigs with no slots (carrier rigs)
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.VEST) &&
(rootItemDb.Properties?.Slots?.Count ?? 0) > 0
)
if (
itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.VEST)
&& (rootItemDb.Properties?.Slots?.Count ?? 0) > 0
)
{
continue;
}
// Skip seasonal event items when not in seasonal event
if (traderConfig.Fence.BlacklistSeasonalItems && blockedSeasonalItems.Contains(rootItemDb.Id))
if (
traderConfig.Fence.BlacklistSeasonalItems
&& blockedSeasonalItems.Contains(rootItemDb.Id)
)
{
continue;
}
@@ -91,11 +96,8 @@ public class FenceBaseAssortGenerator(
Template = rootItemDb.Id,
ParentId = "hideout",
SlotId = "hideout",
Upd = new Upd
{
StackObjectsCount = 9999999
}
}
Upd = new Upd { StackObjectsCount = 9999999 },
},
};
// Ensure ammo is not above penetration limit value
@@ -108,7 +110,7 @@ public class FenceBaseAssortGenerator(
}
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO_BOX))
// Only add cartridges to box if box has no children
// Only add cartridges to box if box has no children
{
if (itemWithChildrenToAdd.Count == 1)
{
@@ -127,12 +129,17 @@ public class FenceBaseAssortGenerator(
// Create barter scheme (price)
var barterSchemeToAdd = new BarterScheme
{
Count = Math.Round((double) fenceService.GetItemPrice(rootItemDb.Id, itemWithChildrenToAdd)),
Template = Money.ROUBLES
Count = Math.Round(
(double)fenceService.GetItemPrice(rootItemDb.Id, itemWithChildrenToAdd)
),
Template = Money.ROUBLES,
};
// Add barter data to base
baseFenceAssort.BarterScheme[itemWithChildrenToAdd[0].Id] = [[barterSchemeToAdd]];
baseFenceAssort.BarterScheme[itemWithChildrenToAdd[0].Id] =
[
[barterSchemeToAdd],
];
// Add item to base
baseFenceAssort.Items.AddRange(itemWithChildrenToAdd);
@@ -146,7 +153,11 @@ public class FenceBaseAssortGenerator(
foreach (var defaultPreset in defaultPresets)
{
// Skip presets we've already added
if (baseFenceAssort.Items.Any(item => item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id))
if (
baseFenceAssort.Items.Any(item =>
item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id
)
)
{
continue;
}
@@ -167,8 +178,7 @@ public class FenceBaseAssortGenerator(
mod.Upd = new Upd
{
StackObjectsCount = 1,
SptPresetId =
defaultPreset.Id // Store preset id here so we can check it later to prevent preset dupes
SptPresetId = defaultPreset.Id, // Store preset id here so we can check it later to prevent preset dupes
};
// Updated root item, exit loop
@@ -191,9 +201,9 @@ public class FenceBaseAssortGenerator(
new BarterScheme
{
Template = Money.ROUBLES,
Count = Math.Round(price * itemQualityModifier)
}
}
Count = Math.Round(price * itemQualityModifier),
},
},
};
baseFenceAssort.LoyalLevelItems[itemAndChildren[0].Id] = 1;
@@ -210,7 +220,12 @@ public class FenceBaseAssortGenerator(
var ammoPenetrationPower = GetAmmoPenetrationPower(rootItemDb);
if (ammoPenetrationPower == null)
{
logger.Warning(localisationService.GetText("fence-unable_to_get_ammo_penetration_value", rootItemDb.Id));
logger.Warning(
localisationService.GetText(
"fence-unable_to_get_ammo_penetration_value",
rootItemDb.Id
)
);
return false;
}
@@ -227,13 +242,18 @@ public class FenceBaseAssortGenerator(
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO_BOX))
{
// Get the cartridge tpl found inside ammo box
var cartridgeTplInBox = rootItemDb.Properties.StackSlots[0].Props.Filters[0].Filter.FirstOrDefault();
var cartridgeTplInBox = rootItemDb
.Properties.StackSlots[0]
.Props.Filters[0]
.Filter.FirstOrDefault();
// Look up cartridge tpl in db
var ammoItemDb = itemHelper.GetItem(cartridgeTplInBox);
if (!ammoItemDb.Key)
{
logger.Warning(localisationService.GetText("fence-ammo_not_found_in_db", cartridgeTplInBox));
logger.Warning(
localisationService.GetText("fence-ammo_not_found_in_db", cartridgeTplInBox)
);
return null;
}
@@ -265,17 +285,20 @@ public class FenceBaseAssortGenerator(
}
// Check for and add required soft inserts to armors
var requiredSlots = itemDbDetails.Properties.Slots.Where(slot => slot.Required ?? false).ToList();
var requiredSlots = itemDbDetails
.Properties.Slots.Where(slot => slot.Required ?? false)
.ToList();
var hasRequiredSlots = requiredSlots.Count > 0;
if (hasRequiredSlots)
{
foreach (var requiredSlot in requiredSlots)
{
var modItemDbDetails = itemHelper.GetItem(requiredSlot.Props.Filters[0].Plate).Value;
var plateTpl =
requiredSlot.Props.Filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
var modItemDbDetails = itemHelper
.GetItem(requiredSlot.Props.Filters[0].Plate)
.Value;
var plateTpl = requiredSlot.Props.Filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
if (string.IsNullOrEmpty(plateTpl))
// Some bsg plate properties are empty, skip mod
// Some bsg plate properties are empty, skip mod
{
continue;
}
@@ -291,9 +314,9 @@ public class FenceBaseAssortGenerator(
Repairable = new UpdRepairable
{
Durability = modItemDbDetails.Properties.MaxDurability,
MaxDurability = modItemDbDetails.Properties.MaxDurability
}
}
MaxDurability = modItemDbDetails.Properties.MaxDurability,
},
},
};
armor.Add(mod);
@@ -301,7 +324,8 @@ public class FenceBaseAssortGenerator(
}
// Check for and add plate items
var plateSlots = itemDbDetails.Properties.Slots.Where(slot => itemHelper.IsRemovablePlateSlot(slot.Name))
var plateSlots = itemDbDetails
.Properties.Slots.Where(slot => itemHelper.IsRemovablePlateSlot(slot.Name))
.ToList();
if (plateSlots.Count > 0)
{
@@ -309,7 +333,7 @@ public class FenceBaseAssortGenerator(
{
var plateTpl = plateSlot.Props.Filters[0].Plate;
if (string.IsNullOrEmpty(plateTpl))
// Bsg data lacks a default plate, skip adding mod
// Bsg data lacks a default plate, skip adding mod
{
continue;
}
@@ -327,9 +351,9 @@ public class FenceBaseAssortGenerator(
Repairable = new UpdRepairable
{
Durability = modItemDbDetails.Properties.MaxDurability,
MaxDurability = modItemDbDetails.Properties.MaxDurability
}
}
MaxDurability = modItemDbDetails.Properties.MaxDurability,
},
},
}
);
}
@@ -33,14 +33,17 @@ public class LocationLootGenerator(
)
{
protected readonly LocationConfig _locationConfig = _configServer.GetConfig<LocationConfig>();
protected readonly SeasonalEventConfig _seasonalEventConfig = _configServer.GetConfig<SeasonalEventConfig>();
protected readonly SeasonalEventConfig _seasonalEventConfig =
_configServer.GetConfig<SeasonalEventConfig>();
/// Create a list of container objects with randomised loot
/// <param name="locationBase">Map base to generate containers for</param>
/// <param name="staticAmmoDist">Static ammo distribution</param>
/// <returns>List of container objects</returns>
public List<SpawnpointTemplate> GenerateStaticContainers(LocationBase locationBase,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist)
public List<SpawnpointTemplate> GenerateStaticContainers(
LocationBase locationBase,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist
)
{
var staticLootItemCount = 0;
var result = new List<SpawnpointTemplate>();
@@ -52,18 +55,26 @@ public class LocationLootGenerator(
if (staticWeaponsOnMapClone is null)
{
_logger.Error(
_localisationService.GetText("location-unable_to_find_static_weapon_for_map", locationBase.Name)
_localisationService.GetText(
"location-unable_to_find_static_weapon_for_map",
locationBase.Name
)
);
}
// Add mounted weapons to output loot
result.AddRange(staticWeaponsOnMapClone);
var allStaticContainersOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticContainers);
var allStaticContainersOnMapClone = _cloner.Clone(
mapData.StaticContainers.Value.StaticContainers
);
if (allStaticContainersOnMapClone is null)
{
_logger.Error(
_localisationService.GetText("location-unable_to_find_static_container_for_map", locationBase.Name)
_localisationService.GetText(
"location-unable_to_find_static_container_for_map",
locationBase.Name
)
);
}
@@ -82,12 +93,16 @@ public class LocationLootGenerator(
// Remove christmas items from loot data
if (!_seasonalEventService.ChristmasEventEnabled())
{
allStaticContainersOnMapClone = allStaticContainersOnMapClone.Where(item => !_seasonalEventConfig.ChristmasContainerIds.Contains(item.Template.Id)
allStaticContainersOnMapClone = allStaticContainersOnMapClone
.Where(item =>
!_seasonalEventConfig.ChristmasContainerIds.Contains(item.Template.Id)
)
.ToList();
}
var staticRandomisableContainersOnMap = GetRandomisableContainersOnMap(allStaticContainersOnMapClone);
var staticRandomisableContainersOnMap = GetRandomisableContainersOnMap(
allStaticContainersOnMapClone
);
// Keep track of static loot count
var staticContainerCount = 0;
@@ -98,13 +113,17 @@ public class LocationLootGenerator(
staticContainerCount += guaranteedContainers.Count;
// Add loot to guaranteed containers and add to result
foreach (var containerWithLoot in guaranteedContainers.Select(container => AddLootToContainer(
container,
staticForcedOnMapClone,
staticLootDist.Value,
staticAmmoDist,
locationId
)))
foreach (
var containerWithLoot in guaranteedContainers.Select(container =>
AddLootToContainer(
container,
staticForcedOnMapClone,
staticLootDist.Value,
staticAmmoDist,
locationId
)
)
)
{
result.Add(containerWithLoot.Template);
@@ -117,8 +136,10 @@ public class LocationLootGenerator(
}
// Randomisation is turned off globally or just turned off for this map
if (!_locationConfig.ContainerRandomisationSettings.Enabled || !_locationConfig.ContainerRandomisationSettings.Maps.ContainsKey(locationId)
)
if (
!_locationConfig.ContainerRandomisationSettings.Enabled
|| !_locationConfig.ContainerRandomisationSettings.Maps.ContainsKey(locationId)
)
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
@@ -149,15 +170,18 @@ public class LocationLootGenerator(
// Group containers by their groupId
if (mapData.Statics is null)
{
_logger.Warning(_localisationService.GetText("location-unable_to_generate_static_loot", locationId));
_logger.Warning(
_localisationService.GetText("location-unable_to_generate_static_loot", locationId)
);
return result;
}
// For each of the container groups, choose from the pool of containers, hydrate container with loot and add to result array
var mapping = GetGroupIdToContainerMappings(mapData.Statics, staticRandomisableContainersOnMap);
var mapping = GetGroupIdToContainerMappings(
mapData.Statics,
staticRandomisableContainersOnMap
);
foreach (var (key, data) in mapping)
{
// Count chosen was 0, skip
@@ -170,7 +194,9 @@ public class LocationLootGenerator(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Group: {key} has no containers with < 100 % spawn chance to choose from, skipping");
_logger.Debug(
$"Group: {key} has no containers with < 100 % spawn chance to choose from, skipping"
);
}
continue;
@@ -186,7 +212,9 @@ public class LocationLootGenerator(
{
if (_randomUtil.GetChance100(containerIdsCopy[containerId.Key] * 100))
{
data.ContainerIdsWithProbability[containerId.Key] = containerIdsCopy[containerId.Key];
data.ContainerIdsWithProbability[containerId.Key] = containerIdsCopy[
containerId.Key
];
}
}
@@ -205,7 +233,8 @@ public class LocationLootGenerator(
foreach (var chosenContainerId in chosenContainerIds)
{
// Look up container object from full list of containers on map
var containerObject = staticRandomisableContainersOnMap.FirstOrDefault(staticContainer => staticContainer.Template.Id == chosenContainerId
var containerObject = staticRandomisableContainersOnMap.FirstOrDefault(
staticContainer => staticContainer.Template.Id == chosenContainerId
);
if (containerObject is null)
{
@@ -236,7 +265,10 @@ public class LocationLootGenerator(
_logger.Success($"A total of: {staticLootItemCount} static items spawned");
_logger.Success(
_localisationService.GetText("location-containers_generated_success", staticContainerCount)
_localisationService.GetText(
"location-containers_generated_success",
staticContainerCount
)
);
return result;
@@ -247,12 +279,15 @@ public class LocationLootGenerator(
/// </summary>
/// <param name="staticContainers"></param>
/// <returns>StaticContainerData array</returns>
protected List<StaticContainerData> GetRandomisableContainersOnMap(List<StaticContainerData> staticContainers)
protected List<StaticContainerData> GetRandomisableContainersOnMap(
List<StaticContainerData> staticContainers
)
{
return staticContainers.Where(staticContainer =>
staticContainer.Probability != 1 &&
!staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false) &&
!_locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains(
return staticContainers
.Where(staticContainer =>
staticContainer.Probability != 1
&& !staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false)
&& !_locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains(
staticContainer.Template.Items.FirstOrDefault().Template
)
)
@@ -264,12 +299,15 @@ public class LocationLootGenerator(
/// </summary>
/// <param name="staticContainersOnMap"></param>
/// <returns>IStaticContainerData array</returns>
protected List<StaticContainerData> GetGuaranteedContainers(List<StaticContainerData> staticContainersOnMap)
protected List<StaticContainerData> GetGuaranteedContainers(
List<StaticContainerData> staticContainersOnMap
)
{
return staticContainersOnMap.Where(staticContainer =>
staticContainer.Probability == 1 ||
staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false) ||
_locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains(
return staticContainersOnMap
.Where(staticContainer =>
staticContainer.Probability == 1
|| staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false)
|| _locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains(
staticContainer.Template.Items.FirstOrDefault().Template
)
)
@@ -283,7 +321,10 @@ public class LocationLootGenerator(
/// <param name="groupId">Name of the group the containers are being collected for</param>
/// <param name="containerData">Containers and probability values for a groupId</param>
/// <returns>List of chosen container Ids</returns>
protected List<string> GetContainersByProbability(string groupId, ContainerGroupCount containerData)
protected List<string> GetContainersByProbability(
string groupId,
ContainerGroupCount containerData
)
{
var chosenContainerIds = new List<string>();
@@ -301,15 +342,14 @@ public class LocationLootGenerator(
}
// Create probability array with all possible container ids in this group and their relative probability of spawning
var containerDistribution =
new ProbabilityObjectArray<string, double>(_mathUtil, _cloner);
var containerDistribution = new ProbabilityObjectArray<string, double>(_mathUtil, _cloner);
foreach (var x in containerIds)
{
var value = containerData.ContainerIdsWithProbability[x];
containerDistribution.Add(new ProbabilityObject<string, double>(x, value, value));
}
chosenContainerIds.AddRange(containerDistribution.Draw((int) containerData.ChosenCount));
chosenContainerIds.AddRange(containerDistribution.Draw((int)containerData.ChosenCount));
return chosenContainerIds;
}
@@ -322,7 +362,8 @@ public class LocationLootGenerator(
/// <returns>dictionary keyed by groupId</returns>
protected Dictionary<string, ContainerGroupCount> GetGroupIdToContainerMappings(
StaticContainer staticContainerGroupData,
List<StaticContainerData> staticContainersOnMap)
List<StaticContainerData> staticContainersOnMap
)
{
// Create dictionary of all group ids and choose a count of containers the map will spawn of that group
var mapping = new Dictionary<string, ContainerGroupCount>();
@@ -332,31 +373,45 @@ public class LocationLootGenerator(
{
ContainerIdsWithProbability = new Dictionary<string, double>(),
ChosenCount = _randomUtil.GetInt(
(int) Math.Round(
groupKvP.Value.MinContainers.Value *
_locationConfig.ContainerRandomisationSettings.ContainerGroupMinSizeMultiplier
),
(int) Math.Round(
groupKvP.Value.MaxContainers.Value *
_locationConfig.ContainerRandomisationSettings.ContainerGroupMaxSizeMultiplier
)
)
(int)
Math.Round(
groupKvP.Value.MinContainers.Value
* _locationConfig
.ContainerRandomisationSettings
.ContainerGroupMinSizeMultiplier
),
(int)
Math.Round(
groupKvP.Value.MaxContainers.Value
* _locationConfig
.ContainerRandomisationSettings
.ContainerGroupMaxSizeMultiplier
)
),
};
}
// Add an empty group for containers without a group id but still have a < 100% chance to spawn
// Likely bad BSG data, will be fixed...eventually, example of the groupIds: `NEED_TO_BE_FIXED1`,`NEED_TO_BE_FIXED_SE02`, `NEED_TO_BE_FIXED_NW_01`
mapping.Add(string.Empty, new ContainerGroupCount
{
ContainerIdsWithProbability = new Dictionary<string, double>(),
ChosenCount = -1
});
mapping.Add(
string.Empty,
new ContainerGroupCount
{
ContainerIdsWithProbability = new Dictionary<string, double>(),
ChosenCount = -1,
}
);
// Iterate over all containers and add to group keyed by groupId
// Containers without a group go into a group with empty key ""
foreach (var container in staticContainersOnMap)
{
if (!staticContainerGroupData.Containers.TryGetValue(container.Template.Id, out var groupData))
if (
!staticContainerGroupData.Containers.TryGetValue(
container.Template.Id,
out var groupData
)
)
{
_logger.Error(
_localisationService.GetText(
@@ -385,10 +440,14 @@ public class LocationLootGenerator(
new ContainerGroupCount
{
ChosenCount = 0d,
ContainerIdsWithProbability = new Dictionary<string, double>()
ContainerIdsWithProbability = new Dictionary<string, double>(),
}
);
mapping[groupData.GroupId].ContainerIdsWithProbability.TryAdd(container.Template.Id, container.Probability.Value);
mapping[groupData.GroupId]
.ContainerIdsWithProbability.TryAdd(
container.Template.Id,
container.Probability.Value
);
}
return mapping;
@@ -404,7 +463,8 @@ public class LocationLootGenerator(
/// <param name="staticAmmoDist">staticAmmo.json</param>
/// <param name="locationName">Name of the map to generate static loot for</param>
/// <returns>StaticContainerData</returns>
protected StaticContainerData AddLootToContainer(StaticContainerData staticContainer,
protected StaticContainerData AddLootToContainer(
StaticContainerData staticContainer,
List<StaticForced>? staticForced,
Dictionary<string, StaticLootDetails> staticLootDist,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist,
@@ -422,7 +482,11 @@ public class LocationLootGenerator(
var containerMap = _itemHelper.GetContainerMapping(containerTpl);
// Choose count of items to add to container
var itemCountToAdd = GetWeightedCountOfContainerItems(containerTpl, staticLootDist, locationName);
var itemCountToAdd = GetWeightedCountOfContainerItems(
containerTpl,
staticLootDist,
locationName
);
if (itemCountToAdd == 0)
{
return containerClone;
@@ -468,11 +532,15 @@ public class LocationLootGenerator(
: chosenItemWithChildren.Items;
// look for open slot to put chosen item into
var result = _containerHelper.FindSlotForItem(containerMap, chosenItemWithChildren.Width, chosenItemWithChildren.Height);
var result = _containerHelper.FindSlotForItem(
containerMap,
chosenItemWithChildren.Width,
chosenItemWithChildren.Height
);
if (!result.Success.GetValueOrDefault(false))
{
if (failedToFitAttemptCount > _locationConfig.FitLootIntoContainerAttempts)
// x attempts to fit an item, container is probably full, stop trying to add more
// x attempts to fit an item, container is probably full, stop trying to add more
{
break;
}
@@ -499,7 +567,9 @@ public class LocationLootGenerator(
{
X = result.X,
Y = result.Y,
R = result.Rotation.GetValueOrDefault(false) ? ItemRotation.Vertical : ItemRotation.Horizontal
R = result.Rotation.GetValueOrDefault(false)
? ItemRotation.Vertical
: ItemRotation.Horizontal,
};
// Add loot to container before returning
@@ -516,23 +586,21 @@ public class LocationLootGenerator(
/// <param name="staticLootDist">staticLoot.json</param>
/// <param name="locationName">Map name (to get per-map multiplier for from config)</param>
/// <returns>item count</returns>
protected int GetWeightedCountOfContainerItems(string containerTypeId,
Dictionary<string, StaticLootDetails> staticLootDist, string locationName)
protected int GetWeightedCountOfContainerItems(
string containerTypeId,
Dictionary<string, StaticLootDetails> staticLootDist,
string locationName
)
{
// Create probability array to calculate the total count of lootable items inside container
var itemCountArray =
new ProbabilityObjectArray<int, float?>(_mathUtil, _cloner);
var itemCountArray = new ProbabilityObjectArray<int, float?>(_mathUtil, _cloner);
var countDistribution = staticLootDist[containerTypeId]?.ItemCountDistribution;
if (countDistribution is null)
{
_logger.Warning(
_localisationService.GetText(
"location-unable_to_find_count_distribution_for_container",
new
{
containerId = containerTypeId,
locationName
}
new { containerId = containerTypeId, locationName }
)
);
@@ -551,7 +619,8 @@ public class LocationLootGenerator(
);
}
return (int) Math.Round(GetStaticLootMultiplierForLocation(locationName) * itemCountArray.Draw()[0]);
return (int)
Math.Round(GetStaticLootMultiplierForLocation(locationName) * itemCountArray.Draw()[0]);
}
/// <summary>
@@ -563,18 +632,23 @@ public class LocationLootGenerator(
/// <returns>ProbabilityObjectArray of item tpls + probability</returns>
protected ProbabilityObjectArray<string, float?> GetPossibleLootItemsForContainer(
string containerTypeId,
Dictionary<string, StaticLootDetails> staticLootDist)
Dictionary<string, StaticLootDetails> staticLootDist
)
{
var seasonalEventActive = _seasonalEventService.SeasonalEventEnabled();
var seasonalItemTplBlacklist = _seasonalEventService.GetInactiveSeasonalEventItems();
var itemDistribution =
new ProbabilityObjectArray<string, float?>(_mathUtil, _cloner);
var itemDistribution = new ProbabilityObjectArray<string, float?>(_mathUtil, _cloner);
var itemContainerDistribution = staticLootDist[containerTypeId]?.ItemDistribution;
if (itemContainerDistribution is null)
{
_logger.Warning(_localisationService.GetText("location-missing_item_distribution_data", containerTypeId));
_logger.Warning(
_localisationService.GetText(
"location-missing_item_distribution_data",
containerTypeId
)
);
return itemDistribution;
}
@@ -586,14 +660,16 @@ public class LocationLootGenerator(
// Skip seasonal event items if they're not enabled
continue;
}
if (_itemFilterService.IsLootableItemBlacklisted(icd.Tpl))
{
// Ensure no blacklisted lootable items are in pool
continue;
}
itemDistribution.Add(new ProbabilityObject<string, float?>(icd.Tpl, icd.RelativeProbability.Value, null));
itemDistribution.Add(
new ProbabilityObject<string, float?>(icd.Tpl, icd.RelativeProbability.Value, null)
);
}
return itemDistribution;
@@ -620,9 +696,11 @@ public class LocationLootGenerator(
/// <param name="staticAmmoDist"></param>
/// <param name="locationName">Location to generate loot for</param>
/// <returns>Array of spawn points with loot in them</returns>
public List<SpawnpointTemplate> GenerateDynamicLoot(LooseLoot dynamicLootDist,
public List<SpawnpointTemplate> GenerateDynamicLoot(
LooseLoot dynamicLootDist,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist,
string locationName)
string locationName
)
{
List<SpawnpointTemplate> loot = [];
List<Spawnpoint> dynamicForcedSpawnPoints = [];
@@ -630,11 +708,13 @@ public class LocationLootGenerator(
// Remove christmas items from loot data
if (!_seasonalEventService.ChristmasEventEnabled())
{
dynamicLootDist.Spawnpoints = dynamicLootDist.Spawnpoints.Where(point =>
dynamicLootDist.Spawnpoints = dynamicLootDist
.Spawnpoints.Where(point =>
!point.Template.Id.StartsWith("christmas", StringComparison.OrdinalIgnoreCase)
)
.ToList();
dynamicLootDist.SpawnpointsForced = dynamicLootDist.SpawnpointsForced.Where(point =>
dynamicLootDist.SpawnpointsForced = dynamicLootDist
.SpawnpointsForced.Where(point =>
!point.Template.Id.StartsWith("christmas", StringComparison.OrdinalIgnoreCase)
)
.ToList();
@@ -642,7 +722,9 @@ public class LocationLootGenerator(
// Build the list of forced loot from both `spawnpointsForced` and any point marked `IsAlwaysSpawn`
dynamicForcedSpawnPoints.AddRange(dynamicLootDist.SpawnpointsForced);
dynamicForcedSpawnPoints.AddRange(dynamicLootDist.Spawnpoints.Where(point => point.Template.IsAlwaysSpawn ?? false));
dynamicForcedSpawnPoints.AddRange(
dynamicLootDist.Spawnpoints.Where(point => point.Template.IsAlwaysSpawn ?? false)
);
// Add forced loot
AddForcedLoot(loot, dynamicForcedSpawnPoints, locationName, staticAmmoDist);
@@ -651,16 +733,19 @@ public class LocationLootGenerator(
// Draw from random distribution
var desiredSpawnPointCount = Math.Round(
GetLooseLootMultiplierForLocation(locationName) * _randomUtil.GetNormallyDistributedRandomNumber(
(double) dynamicLootDist.SpawnpointCount.Mean,
(double) dynamicLootDist.SpawnpointCount.Std
)
GetLooseLootMultiplierForLocation(locationName)
* _randomUtil.GetNormallyDistributedRandomNumber(
(double)dynamicLootDist.SpawnpointCount.Mean,
(double)dynamicLootDist.SpawnpointCount.Std
)
);
// Positions not in forced but have 100% chance to spawn
List<Spawnpoint> guaranteedLoosePoints = [];
var blacklistedSpawnPoints = _locationConfig.LooseLootBlacklist.GetValueOrDefault(locationName);
var blacklistedSpawnPoints = _locationConfig.LooseLootBlacklist.GetValueOrDefault(
locationName
);
var spawnPointArray = new ProbabilityObjectArray<string, Spawnpoint>(_mathUtil, _cloner);
foreach (var spawnPoint in allDynamicSpawnPoints)
@@ -689,7 +774,13 @@ public class LocationLootGenerator(
continue;
}
spawnPointArray.Add(new ProbabilityObject<string, Spawnpoint>(spawnPoint.Template.Id, spawnPoint.Probability ?? 0, spawnPoint));
spawnPointArray.Add(
new ProbabilityObject<string, Spawnpoint>(
spawnPoint.Template.Id,
spawnPoint.Probability ?? 0,
spawnPoint
)
);
}
// Select a number of spawn points to add loot to
@@ -700,16 +791,19 @@ public class LocationLootGenerator(
var randomSpawnPointCount = desiredSpawnPointCount - chosenSpawnPoints.Count;
// Only draw random spawn points if needed
if (randomSpawnPointCount > 0 && spawnPointArray.Count > 0)
// Add randomly chosen spawn points
// Add randomly chosen spawn points
{
foreach (var si in spawnPointArray.Draw((int) randomSpawnPointCount, false))
foreach (var si in spawnPointArray.Draw((int)randomSpawnPointCount, false))
{
chosenSpawnPoints.Add(spawnPointArray.Data(si));
}
}
// Filter out duplicate locationIds // prob can be done better
chosenSpawnPoints = chosenSpawnPoints.GroupBy(spawnPoint => spawnPoint.LocationId).Select(group => group.First()).ToList();
chosenSpawnPoints = chosenSpawnPoints
.GroupBy(spawnPoint => spawnPoint.LocationId)
.Select(group => group.First())
.ToList();
// Do we have enough items in pool to fulfill requirement
var tooManySpawnPointsRequested = desiredSpawnPointCount - chosenSpawnPoints.Count > 0;
@@ -724,7 +818,7 @@ public class LocationLootGenerator(
{
requested = desiredSpawnPointCount + guaranteedLoosePoints.Count,
found = chosenSpawnPoints.Count,
mapName = locationName
mapName = locationName,
}
)
);
@@ -740,22 +834,27 @@ public class LocationLootGenerator(
if (spawnPoint.Template is null)
{
_logger.Warning(
_localisationService.GetText("location-missing_dynamic_template", spawnPoint.LocationId)
_localisationService.GetText(
"location-missing_dynamic_template",
spawnPoint.LocationId
)
);
continue;
}
// Ensure no blacklisted lootable items are in pool
spawnPoint.Template.Items = spawnPoint.Template.Items.Where(item => !_itemFilterService.IsLootableItemBlacklisted(item.Template)
spawnPoint.Template.Items = spawnPoint
.Template.Items.Where(item =>
!_itemFilterService.IsLootableItemBlacklisted(item.Template)
)
.ToList();
// Ensure no seasonal items are in pool if not in-season
if (!seasonalEventActive)
{
spawnPoint.Template.Items = spawnPoint.Template.Items.Where(item => !seasonalItemTplBlacklist.Contains(item.Template)
)
spawnPoint.Template.Items = spawnPoint
.Template.Items.Where(item => !seasonalItemTplBlacklist.Contains(item.Template))
.ToList();
}
@@ -765,7 +864,10 @@ public class LocationLootGenerator(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
_localisationService.GetText("location-spawnpoint_missing_items", spawnPoint.Template.Id)
_localisationService.GetText(
"location-spawnpoint_missing_items",
spawnPoint.Template.Id
)
);
}
@@ -784,13 +886,22 @@ public class LocationLootGenerator(
continue;
}
itemArray.Add(new ProbabilityObject<string, double?>(itemDist.ComposedKey.Key, itemDist.RelativeProbability ?? 0, null));
itemArray.Add(
new ProbabilityObject<string, double?>(
itemDist.ComposedKey.Key,
itemDist.RelativeProbability ?? 0,
null
)
);
}
if (itemArray.Count == 0)
{
_logger.Warning(
_localisationService.GetText("location-loot_pool_is_empty_skipping", spawnPoint.Template.Id)
_localisationService.GetText(
"location-loot_pool_is_empty_skipping",
spawnPoint.Template.Id
)
);
continue;
@@ -822,44 +933,65 @@ public class LocationLootGenerator(
/// <param name="lootLocationTemplates">List to add forced loot spawn locations to</param>
/// <param name="forcedSpawnPoints">Forced loot locations that must be added</param>
/// <param name="locationName">Name of map currently having force loot created for</param>
protected void AddForcedLoot(List<SpawnpointTemplate> lootLocationTemplates,
List<Spawnpoint> forcedSpawnPoints, string locationName,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist)
protected void AddForcedLoot(
List<SpawnpointTemplate> lootLocationTemplates,
List<Spawnpoint> forcedSpawnPoints,
string locationName,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist
)
{
var lootToForceSingleAmountOnMap = _locationConfig.ForcedLootSingleSpawnById.GetValueOrDefault(locationName);
var lootToForceSingleAmountOnMap =
_locationConfig.ForcedLootSingleSpawnById.GetValueOrDefault(locationName);
if (lootToForceSingleAmountOnMap is not null)
// Process loot items defined as requiring only 1 spawn position as they appear in multiple positions on the map
// Process loot items defined as requiring only 1 spawn position as they appear in multiple positions on the map
{
foreach (var itemTpl in lootToForceSingleAmountOnMap)
{
// Get all spawn positions for item tpl in forced loot array
var items = forcedSpawnPoints.Where(forcedSpawnPoint => forcedSpawnPoint.Template.Items.FirstOrDefault().Template == itemTpl);
var items = forcedSpawnPoints.Where(forcedSpawnPoint =>
forcedSpawnPoint.Template.Items.FirstOrDefault().Template == itemTpl
);
if (!items.Any())
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to adjust loot item {itemTpl} as it does not exist inside {locationName} forced loot.");
_logger.Debug(
$"Unable to adjust loot item {itemTpl} as it does not exist inside {locationName} forced loot."
);
}
continue;
}
// Create probability array of all spawn positions for this spawn id
var spawnPointArray = new ProbabilityObjectArray<string, Spawnpoint>(_mathUtil, _cloner);
var spawnPointArray = new ProbabilityObjectArray<string, Spawnpoint>(
_mathUtil,
_cloner
);
foreach (var si in items)
// use locationId as template.Id is the same across all items
// use locationId as template.Id is the same across all items
{
spawnPointArray.Add(new ProbabilityObject<string, Spawnpoint>(si.LocationId, si.Probability ?? 0, si));
spawnPointArray.Add(
new ProbabilityObject<string, Spawnpoint>(
si.LocationId,
si.Probability ?? 0,
si
)
);
}
// Choose 1 out of all found spawn positions for spawn id and add to loot array
foreach (var spawnPointLocationId in spawnPointArray.Draw(1, false))
{
var itemToAdd = items.FirstOrDefault(item => item.LocationId == spawnPointLocationId);
var itemToAdd = items.FirstOrDefault(item =>
item.LocationId == spawnPointLocationId
);
var lootItem = itemToAdd?.Template;
if (lootItem is null)
{
_logger.Warning($"Item with spawn point id {spawnPointLocationId} could not be found, skipping");
_logger.Warning(
$"Item with spawn point id {spawnPointLocationId} could not be found, skipping"
);
continue;
}
@@ -909,7 +1041,8 @@ public class LocationLootGenerator(
forcedLootLocation.Template.Items = createItemResult.Items;
// Push forced location into array as long as it doesnt exist already
var existingLocation = lootLocationTemplates.Any(spawnPoint => spawnPoint.Id == locationTemplateToAdd.Id
var existingLocation = lootLocationTemplates.Any(spawnPoint =>
spawnPoint.Id == locationTemplateToAdd.Id
);
if (!existingLocation)
{
@@ -934,13 +1067,19 @@ public class LocationLootGenerator(
/// <param name="items"> Location loot Template </param>
/// <param name="staticAmmoDist"> Ammo distributions </param>
/// <returns> ContainerItem object </returns>
protected ContainerItem CreateDynamicLootItem(string? chosenComposedKey, List<Item> items, Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist)
protected ContainerItem CreateDynamicLootItem(
string? chosenComposedKey,
List<Item> items,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist
)
{
var chosenItem = items.FirstOrDefault(item => item.Id == chosenComposedKey);
var chosenTpl = chosenItem?.Template;
if (chosenTpl is null)
{
throw new Exception($"Item for tpl {chosenComposedKey} was not found in the spawn point");
throw new Exception(
$"Item for tpl {chosenComposedKey} was not found in the spawn point"
);
}
var itemTemplate = _itemHelper.GetItem(chosenTpl).Value;
@@ -958,48 +1097,34 @@ public class LocationLootGenerator(
var stackCount =
itemTemplate.Properties.StackMaxSize == 1
? 1
: _randomUtil.GetInt(itemTemplate.Properties.StackMinRandom.Value, itemTemplate.Properties.StackMaxRandom.Value);
: _randomUtil.GetInt(
itemTemplate.Properties.StackMinRandom.Value,
itemTemplate.Properties.StackMaxRandom.Value
);
itemWithMods.Add(
new Item
{
Id = _hashUtil.Generate(),
Template = chosenTpl,
Upd = new Upd
{
StackObjectsCount = stackCount
}
Upd = new Upd { StackObjectsCount = stackCount },
}
);
}
else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO_BOX))
{
// Fill with cartridges
List<Item> ammoBoxItem =
[
new()
{
Id = _hashUtil.Generate(),
Template = chosenTpl
}
];
List<Item> ammoBoxItem = [new() { Id = _hashUtil.Generate(), Template = chosenTpl }];
_itemHelper.AddCartridgesToAmmoBox(ammoBoxItem, itemTemplate);
itemWithMods.AddRange(ammoBoxItem);
}
else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MAGAZINE))
{
// Create array with just magazine
List<Item> magazineItem =
[
new()
{
Id = _hashUtil.Generate(),
Template = chosenTpl
}
];
List<Item> magazineItem = [new() { Id = _hashUtil.Generate(), Template = chosenTpl }];
if (_randomUtil.GetChance100(_locationConfig.StaticMagazineLootHasAmmoChancePercent))
// Add randomised amount of cartridges
// Add randomised amount of cartridges
{
_itemHelper.FillMagazineWithRandomCartridge(
magazineItem,
@@ -1022,7 +1147,7 @@ public class LocationLootGenerator(
itemWithChildren = _itemHelper.ReplaceIDs(_cloner.Clone(itemWithChildren));
if (_locationConfig.TplsToStripChildItemsFrom.Contains(chosenItem.Template))
// Strip children from parent before adding
// Strip children from parent before adding
{
itemWithChildren = [itemWithChildren.FirstOrDefault()];
}
@@ -1037,16 +1162,16 @@ public class LocationLootGenerator(
{
Items = itemWithMods,
Width = size.Width,
Height = size.Height
Height = size.Height,
};
}
// TODO: rewrite, BIG yikes
protected ContainerItem? CreateStaticLootItem(
string chosenTpl,
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist,
string? parentId = null)
string? parentId = null
)
{
var itemTemplate = _itemHelper.GetItem(chosenTpl).Value;
if (itemTemplate.Properties is null)
@@ -1058,14 +1183,7 @@ public class LocationLootGenerator(
var width = itemTemplate.Properties.Width;
var height = itemTemplate.Properties.Height;
List<Item> items =
[
new()
{
Id = _hashUtil.Generate(),
Template = chosenTpl
}
];
List<Item> items = [new() { Id = _hashUtil.Generate(), Template = chosenTpl }];
var rootItem = items.FirstOrDefault();
// Use passed in parentId as override for new item
@@ -1075,19 +1193,20 @@ public class LocationLootGenerator(
}
if (
_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MONEY) ||
_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO)
_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MONEY)
|| _itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO)
)
{
// Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked
var stackCount = itemTemplate.Properties.StackMaxSize == 1
? 1
: _randomUtil.GetInt(itemTemplate.Properties.StackMinRandom.Value, itemTemplate.Properties.StackMaxRandom.Value);
var stackCount =
itemTemplate.Properties.StackMaxSize == 1
? 1
: _randomUtil.GetInt(
itemTemplate.Properties.StackMinRandom.Value,
itemTemplate.Properties.StackMaxRandom.Value
);
rootItem.Upd = new Upd
{
StackObjectsCount = stackCount
};
rootItem.Upd = new Upd { StackObjectsCount = stackCount };
}
// No spawn point, use default template
else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.WEAPON))
@@ -1120,11 +1239,16 @@ public class LocationLootGenerator(
{
Items = items,
Width = width,
Height = height
Height = height,
};
}
protected List<Item> GetArmorItems(string chosenTpl, Item? rootItem, List<Item> items, TemplateItem armorDbTemplate)
protected List<Item> GetArmorItems(
string chosenTpl,
Item? rootItem,
List<Item> items,
TemplateItem armorDbTemplate
)
{
var defaultPreset = _presetHelper.GetDefaultPreset(chosenTpl);
if (defaultPreset is not null)
@@ -1163,9 +1287,10 @@ public class LocationLootGenerator(
/// <returns>Root Item</returns>
protected Item? CreateWeaponRootAndChildren(
string chosenTpl,
Dictionary<string,List<StaticAmmoDetails>> cartridgePool,
Dictionary<string, List<StaticAmmoDetails>> cartridgePool,
string? parentId,
ref List<Item> items)
ref List<Item> items
)
{
List<Item> children = [];
@@ -1175,7 +1300,10 @@ public class LocationLootGenerator(
{
try
{
children = _itemHelper.ReparentItemAndChildren(defaultPreset.Items.FirstOrDefault(), defaultPreset.Items);
children = _itemHelper.ReparentItemAndChildren(
defaultPreset.Items.FirstOrDefault(),
defaultPreset.Items
);
}
catch (Exception e)
{
@@ -1190,7 +1318,7 @@ public class LocationLootGenerator(
tpl = chosenTpl,
defaultId = defaultPreset.Id,
defaultName = defaultPreset.Name,
parentId
parentId,
}
)
);
@@ -1214,11 +1342,7 @@ public class LocationLootGenerator(
_logger.Error(
_localisationService.GetText(
"location-missing_root_item",
new
{
tpl = chosenTpl,
parentId
}
new { tpl = chosenTpl, parentId }
)
);
@@ -1237,11 +1361,7 @@ public class LocationLootGenerator(
_logger.Error(
_localisationService.GetText(
"location-unable_to_reparent_item",
new
{
tpl = chosenTpl,
parentId
}
new { tpl = chosenTpl, parentId }
)
);
_logger.Error(e.StackTrace);
@@ -1285,7 +1405,8 @@ public class LocationLootGenerator(
Dictionary<string, List<StaticAmmoDetails>> staticAmmoDist,
Item? rootItem,
TemplateItem itemTemplate,
List<Item> items)
List<Item> items
)
{
List<Item> magazineWithCartridges = [rootItem];
_itemHelper.FillMagazineWithRandomCartridge(
@@ -1305,40 +1426,20 @@ public class LocationLootGenerator(
public record ContainerGroupCount
{
[JsonPropertyName("containerIdsWithProbability")]
public Dictionary<string, double>? ContainerIdsWithProbability
{
get;
set;
}
public Dictionary<string, double>? ContainerIdsWithProbability { get; set; }
[JsonPropertyName("chosenCount")]
public double? ChosenCount
{
get;
set;
}
public double? ChosenCount { get; set; }
}
public class ContainerItem
{
[JsonPropertyName("items")]
public List<Item>? Items
{
get;
set;
}
public List<Item>? Items { get; set; }
[JsonPropertyName("width")]
public int? Width
{
get;
set;
}
public int? Width { get; set; }
[JsonPropertyName("height")]
public int? Height
{
get;
set;
}
public int? Height { get; set; }
}
@@ -58,18 +58,16 @@ public class LootGenerator(
{
// Choose one at random + add to results array
var chosenSealedContainer = _randomUtil.GetArrayValue(sealedWeaponContainerPool);
result.Add([
new Item
{
Id = _hashUtil.Generate(),
Template = chosenSealedContainer.Id,
Upd = new Upd
result.Add(
[
new Item
{
StackObjectsCount = 1,
SpawnedInSession = true
}
}
]);
Id = _hashUtil.Generate(),
Template = chosenSealedContainer.Id,
Upd = new Upd { StackObjectsCount = 1, SpawnedInSession = true },
},
]
);
}
}
@@ -85,11 +83,21 @@ public class LootGenerator(
// Pool has items we could add as loot, proceed
if (rewardPoolResults.ItemPool.Count > 0)
{
var randomisedItemCount = _randomUtil.GetInt(options.ItemCount.Min, options.ItemCount.Max);
var randomisedItemCount = _randomUtil.GetInt(
options.ItemCount.Min,
options.ItemCount.Max
);
for (var index = 0; index < randomisedItemCount; index++)
{
if (!FindAndAddRandomItemToLoot(rewardPoolResults.ItemPool, itemTypeCounts, options, result))
// Failed to add, reduce index so we get another attempt
if (
!FindAndAddRandomItemToLoot(
rewardPoolResults.ItemPool,
itemTypeCounts,
options,
result
)
)
// Failed to add, reduce index so we get another attempt
{
index--;
}
@@ -105,9 +113,8 @@ public class LootGenerator(
);
if (randomisedWeaponPresetCount > 0)
{
var weaponDefaultPresets = globalDefaultPresets.Where(preset =>
_itemHelper.IsOfBaseclass(preset.Encyclopedia, BaseClasses.WEAPON)
)
var weaponDefaultPresets = globalDefaultPresets
.Where(preset => _itemHelper.IsOfBaseclass(preset.Encyclopedia, BaseClasses.WEAPON))
.ToList();
if (weaponDefaultPresets.Any())
@@ -115,14 +122,14 @@ public class LootGenerator(
for (var index = 0; index < randomisedWeaponPresetCount; index++)
{
if (
!FindAndAddRandomPresetToLoot(
weaponDefaultPresets,
itemTypeCounts,
rewardPoolResults.Blacklist,
result
)
!FindAndAddRandomPresetToLoot(
weaponDefaultPresets,
itemTypeCounts,
rewardPoolResults.Blacklist,
result
)
// Failed to add, reduce index so we get another attempt
)
// Failed to add, reduce index so we get another attempt
{
index--;
}
@@ -140,9 +147,8 @@ public class LootGenerator(
var armorDefaultPresets = globalDefaultPresets.Where(preset =>
_itemHelper.ArmorItemCanHoldMods(preset.Encyclopedia)
);
var levelFilteredArmorPresets = armorDefaultPresets.Where(armor =>
IsArmorOfDesiredProtectionLevel(armor, options)
)
var levelFilteredArmorPresets = armorDefaultPresets
.Where(armor => IsArmorOfDesiredProtectionLevel(armor, options))
.ToList();
// Add some armors to rewards
@@ -151,14 +157,14 @@ public class LootGenerator(
for (var index = 0; index < randomisedArmorPresetCount; index++)
{
if (
!FindAndAddRandomPresetToLoot(
levelFilteredArmorPresets,
itemTypeCounts,
rewardPoolResults.Blacklist,
result
)
!FindAndAddRandomPresetToLoot(
levelFilteredArmorPresets,
itemTypeCounts,
rewardPoolResults.Blacklist,
result
)
// Failed to add, reduce index so we get another attempt
)
// Failed to add, reduce index so we get another attempt
{
index--;
}
@@ -195,7 +201,9 @@ public class LootGenerator(
for (var i = 0; i < randomisedItemCount; i++)
{
// Clone preset and alter Ids to be unique
var presetWithUniqueIds = _itemHelper.ReplaceIDs(_cloner.Clone(preset.Items));
var presetWithUniqueIds = _itemHelper.ReplaceIDs(
_cloner.Clone(preset.Items)
);
// Add to results
result.Add(presetWithUniqueIds);
@@ -203,7 +211,6 @@ public class LootGenerator(
}
continue;
}
// Non-preset item to be added
@@ -211,11 +218,7 @@ public class LootGenerator(
{
Id = _hashUtil.Generate(),
Template = itemTpl,
Upd = new Upd
{
StackObjectsCount = randomisedItemCount,
SpawnedInSession = true
}
Upd = new Upd { StackObjectsCount = randomisedItemCount, SpawnedInSession = true },
};
var splitResults = _itemHelper.SplitStack(newLootItem);
foreach (var splitItem in splitResults)
@@ -242,11 +245,12 @@ public class LootGenerator(
List<string> itemTypeWhitelist,
bool useRewardItemBlacklist,
bool allowBossItems,
bool blockSeasonalItemsOutOfSeason)
bool blockSeasonalItemsOutOfSeason
)
{
var itemsDb = _databaseService.GetItems().Values;
var itemBlacklist = new HashSet<string>();
itemBlacklist.UnionWith([.._itemFilterService.GetBlacklistedItems(), ..itemTplBlacklist]);
itemBlacklist.UnionWith([.. _itemFilterService.GetBlacklistedItems(), .. itemTplBlacklist]);
if (useRewardItemBlacklist)
{
@@ -256,10 +260,12 @@ public class LootGenerator(
var itemTypeBlacklist = _itemFilterService.GetItemRewardBaseTypeBlacklist();
var itemsMatchingTypeBlacklist = itemsDb
.Where(templateItem => !string.IsNullOrEmpty(templateItem.Parent)) // Ignore items without parents
.Where(templateItem => _itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist))
.Where(templateItem =>
_itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist)
)
.Select(templateItem => templateItem.Id);
itemBlacklist.UnionWith([..rewardItemBlacklist, ..itemsMatchingTypeBlacklist]);
itemBlacklist.UnionWith([.. rewardItemBlacklist, .. itemsMatchingTypeBlacklist]);
}
if (!allowBossItems)
@@ -272,19 +278,16 @@ public class LootGenerator(
itemBlacklist.UnionWith(_seasonalEventService.GetInactiveSeasonalEventItems());
}
var items = itemsDb.Where(item =>
!itemBlacklist.Contains(item.Id) &&
string.Equals(item.Type, "item", StringComparison.OrdinalIgnoreCase) &&
!item.Properties.QuestItem.GetValueOrDefault(false) &&
itemTypeWhitelist.Contains(item.Parent)
var items = itemsDb
.Where(item =>
!itemBlacklist.Contains(item.Id)
&& string.Equals(item.Type, "item", StringComparison.OrdinalIgnoreCase)
&& !item.Properties.QuestItem.GetValueOrDefault(false)
&& itemTypeWhitelist.Contains(item.Parent)
)
.ToList();
return new ItemRewardPoolResults
{
ItemPool = items,
Blacklist = itemBlacklist
};
return new ItemRewardPoolResults { ItemPool = items, Blacklist = itemBlacklist };
}
/// <summary>
@@ -326,7 +329,7 @@ public class LootGenerator(
itemTypeCounts[itemTypeId.Key] = new ItemLimit
{
Current = 0,
Max = limits[itemTypeId.Key]
Max = limits[itemTypeId.Key],
};
}
@@ -341,13 +344,19 @@ public class LootGenerator(
/// <param name="options">item filters</param>
/// <param name="result">array to add found item to</param>
/// <returns>true if item was valid and added to pool</returns>
protected bool FindAndAddRandomItemToLoot(List<TemplateItem> items, Dictionary<string, ItemLimit> itemTypeCounts,
protected bool FindAndAddRandomItemToLoot(
List<TemplateItem> items,
Dictionary<string, ItemLimit> itemTypeCounts,
LootRequest options,
List<List<Item>> result)
List<List<Item>> result
)
{
var randomItem = _randomUtil.GetArrayValue(items);
var itemLimitCount = itemTypeCounts.TryGetValue(randomItem.Parent, out var randomItemLimitCount);
var itemLimitCount = itemTypeCounts.TryGetValue(
randomItem.Parent,
out var randomItemLimitCount
);
if (!itemLimitCount && randomItemLimitCount?.Current > randomItemLimitCount?.Max)
{
return false;
@@ -363,11 +372,7 @@ public class LootGenerator(
{
Id = _hashUtil.Generate(),
Template = randomItem.Id,
Upd = new Upd
{
StackObjectsCount = 1,
SpawnedInSession = true
}
Upd = new Upd { StackObjectsCount = 1, SpawnedInSession = true },
};
// Special case - handle items that need a stackcount > 1
@@ -380,7 +385,7 @@ public class LootGenerator(
result.Add([newLootItem]);
if (randomItemLimitCount is not null)
// Increment item count as it's in limit array
// Increment item count as it's in limit array
{
randomItemLimitCount.Current++;
}
@@ -417,10 +422,12 @@ public class LootGenerator(
/// <param name="itemBlacklist">Items to skip</param>
/// <param name="result">List to add chosen preset to</param>
/// <returns>true if preset was valid and added to pool</returns>
protected bool FindAndAddRandomPresetToLoot(List<Preset> presetPool,
protected bool FindAndAddRandomPresetToLoot(
List<Preset> presetPool,
Dictionary<string, ItemLimit> itemTypeCounts,
HashSet<string> itemBlacklist,
List<List<Item>> result)
List<List<Item>> result
)
{
if (presetPool.Count == 0)
{
@@ -437,7 +444,12 @@ public class LootGenerator(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Warning(_localisationService.GetText("loot-chosen_preset_missing_encyclopedia_value", chosenPreset?.Id));
_logger.Warning(
_localisationService.GetText(
"loot-chosen_preset_missing_encyclopedia_value",
chosenPreset?.Id
)
);
}
return false;
@@ -449,7 +461,9 @@ public class LootGenerator(
{
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"$Unable to find preset with tpl: {chosenPreset.Encyclopedia}, skipping");
_logger.Debug(
$"$Unable to find preset with tpl: {chosenPreset.Encyclopedia}, skipping"
);
}
return false;
@@ -464,13 +478,21 @@ public class LootGenerator(
// Some custom mod items lack a parent property
if (itemDbDetails.Value?.Parent is null)
{
_logger.Error(_localisationService.GetText("loot-item_missing_parentid", itemDbDetails.Value?.Name));
_logger.Error(
_localisationService.GetText(
"loot-item_missing_parentid",
itemDbDetails.Value?.Name
)
);
return false;
}
// Check chosen preset hasn't exceeded spawn limit
var hasItemLimitCount = itemTypeCounts.TryGetValue(itemDbDetails.Value.Parent, out var itemLimitCount);
var hasItemLimitCount = itemTypeCounts.TryGetValue(
itemDbDetails.Value.Parent,
out var itemLimitCount
);
if (!hasItemLimitCount && itemLimitCount?.Current > itemLimitCount?.Max)
{
return false;
@@ -485,7 +507,7 @@ public class LootGenerator(
result.Add(presetAndMods);
if (itemLimitCount is not null)
// Increment item count as item has been chosen and its inside itemLimitCount dictionary
// Increment item count as item has been chosen and its inside itemLimitCount dictionary
{
itemLimitCount.Current++;
}
@@ -499,7 +521,9 @@ public class LootGenerator(
/// </summary>
/// <param name="containerSettings">sealed weapon container settings</param>
/// <returns>List of items with children lists</returns>
public List<List<Item>> GetSealedWeaponCaseLoot(SealedAirdropContainerSettings containerSettings)
public List<List<Item>> GetSealedWeaponCaseLoot(
SealedAirdropContainerSettings containerSettings
)
{
List<List<Item>> itemsToReturn = [];
@@ -513,7 +537,10 @@ public class LootGenerator(
if (!weaponDetailsDb.Key)
{
_logger.Error(
_localisationService.GetText("loot-non_item_picked_as_sealed_weapon_crate_reward", chosenWeaponTpl)
_localisationService.GetText(
"loot-non_item_picked_as_sealed_weapon_crate_reward",
chosenWeaponTpl
)
);
return itemsToReturn;
@@ -528,9 +555,14 @@ public class LootGenerator(
if (chosenWeaponPreset is null)
{
_logger.Warning(
_localisationService.GetText("loot-default_preset_not_found_using_random", chosenWeaponTpl)
_localisationService.GetText(
"loot-default_preset_not_found_using_random",
chosenWeaponTpl
)
);
chosenWeaponPreset = _randomUtil.GetArrayValue(
_presetHelper.GetPresets(chosenWeaponTpl)
);
chosenWeaponPreset = _randomUtil.GetArrayValue(_presetHelper.GetPresets(chosenWeaponTpl));
}
// Clean up Ids to ensure they're all unique and prevent collisions
@@ -543,11 +575,17 @@ public class LootGenerator(
// Get a random collection of weapon mods related to chosen weawpon and add them to result array
var linkedItemsToWeapon = _ragfairLinkedItemService.GetLinkedDbItems(chosenWeaponTpl);
itemsToReturn.AddRange(
GetSealedContainerWeaponModRewards(containerSettings, linkedItemsToWeapon, chosenWeaponPreset)
GetSealedContainerWeaponModRewards(
containerSettings,
linkedItemsToWeapon,
chosenWeaponPreset
)
);
// Handle non-weapon mod reward types
itemsToReturn.AddRange(GetSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb.Value));
itemsToReturn.AddRange(
GetSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb.Value)
);
return itemsToReturn;
}
@@ -558,8 +596,10 @@ public class LootGenerator(
/// <param name="containerSettings">Sealed weapon container settings</param>
/// <param name="weaponDetailsDb">Details for the weapon to reward player</param>
/// <returns>List of item with children lists</returns>
protected List<List<Item>> GetSealedContainerNonWeaponModRewards(SealedAirdropContainerSettings containerSettings,
TemplateItem weaponDetailsDb)
protected List<List<Item>> GetSealedContainerNonWeaponModRewards(
SealedAirdropContainerSettings containerSettings,
TemplateItem weaponDetailsDb
)
{
List<List<Item>> rewards = [];
@@ -576,11 +616,10 @@ public class LootGenerator(
{
// Get ammo boxes from db
var ammoBoxesDetails = containerSettings.AmmoBoxWhitelist.Select(tpl =>
{
var itemDetails = _itemHelper.GetItem(tpl);
return itemDetails.Value;
}
);
{
var itemDetails = _itemHelper.GetItem(tpl);
return itemDetails.Value;
});
// Need to find boxes that matches weapons caliber
var weaponCaliber = weaponDetailsDb.Properties.AmmoCaliber;
@@ -602,11 +641,7 @@ public class LootGenerator(
var chosenAmmoBox = _randomUtil.GetArrayValue(ammoBoxesMatchingCaliber);
var ammoBoxReward = new List<Item>
{
new()
{
Id = _hashUtil.Generate(),
Template = chosenAmmoBox.Id
}
new() { Id = _hashUtil.Generate(), Template = chosenAmmoBox.Id },
};
_itemHelper.AddCartridgesToAmmoBox(ammoBoxReward, chosenAmmoBox);
rewards.Add(ammoBoxReward);
@@ -616,13 +651,14 @@ public class LootGenerator(
}
// Get all items of the desired type + not quest items + not globally blacklisted
var rewardItemPool = _databaseService.GetItems()
var rewardItemPool = _databaseService
.GetItems()
.Values.Where(item =>
item.Parent == rewardKey &&
string.Equals(item.Type, "item", StringComparison.OrdinalIgnoreCase) &&
_itemFilterService.IsItemBlacklisted(item.Id) &&
!(containerSettings.AllowBossItems || _itemFilterService.IsBossItem(item.Id)) &&
item.Properties.QuestItem is null
item.Parent == rewardKey
&& string.Equals(item.Type, "item", StringComparison.OrdinalIgnoreCase)
&& _itemFilterService.IsItemBlacklisted(item.Id)
&& !(containerSettings.AllowBossItems || _itemFilterService.IsBossItem(item.Id))
&& item.Properties.QuestItem is null
);
if (!rewardItemPool.Any())
@@ -641,11 +677,7 @@ public class LootGenerator(
var chosenRewardItem = _randomUtil.GetArrayValue(rewardItemPool);
var rewardItem = new List<Item>
{
new()
{
Id = _hashUtil.Generate(),
Template = chosenRewardItem.Id
}
new() { Id = _hashUtil.Generate(), Template = chosenRewardItem.Id },
};
rewards.Add(rewardItem);
@@ -662,8 +694,11 @@ public class LootGenerator(
/// <param name="linkedItemsToWeapon">All items that can be attached/inserted into weapon</param>
/// <param name="chosenWeaponPreset">The weapon preset given to player as reward</param>
/// <returns>List of item with children lists</returns>
protected List<List<Item>> GetSealedContainerWeaponModRewards(SealedAirdropContainerSettings containerSettings, List<TemplateItem> linkedItemsToWeapon,
Preset chosenWeaponPreset)
protected List<List<Item>> GetSealedContainerWeaponModRewards(
SealedAirdropContainerSettings containerSettings,
List<TemplateItem> linkedItemsToWeapon,
Preset chosenWeaponPreset
)
{
List<List<Item>> modRewards = [];
@@ -678,7 +713,8 @@ public class LootGenerator(
}
// Get items that fulfil reward type criteria from items that fit on gun
var relatedItems = linkedItemsToWeapon?.Where(item => item?.Parent == rewardKey && !_itemFilterService.IsItemBlacklisted(item.Id)
var relatedItems = linkedItemsToWeapon?.Where(item =>
item?.Parent == rewardKey && !_itemFilterService.IsItemBlacklisted(item.Id)
);
if (relatedItems is null || !relatedItems.Any())
{
@@ -698,11 +734,7 @@ public class LootGenerator(
var chosenItem = _randomUtil.DrawRandomFromList(relatedItems.ToList());
var reward = new List<Item>
{
new()
{
Id = _hashUtil.Generate(),
Template = chosenItem[0].Id
}
new() { Id = _hashUtil.Generate(), Template = chosenItem[0].Id },
};
modRewards.Add(reward);
@@ -742,11 +774,7 @@ public class LootGenerator(
List<Item> rewardItem =
[
new()
{
Id = _hashUtil.Generate(),
Template = chosenRewardItemTpl
}
new() { Id = _hashUtil.Generate(), Template = chosenRewardItemTpl },
];
itemsToReturn.Add(rewardItem);
}
@@ -761,47 +789,33 @@ public class LootGenerator(
/// <returns>Single tpl</returns>
protected string PickRewardItem(RewardDetails rewardContainerDetails)
{
if (rewardContainerDetails.RewardTplPool is not null && rewardContainerDetails.RewardTplPool.Count > 0)
if (
rewardContainerDetails.RewardTplPool is not null
&& rewardContainerDetails.RewardTplPool.Count > 0
)
{
return _weightedRandomHelper.GetWeightedValue(rewardContainerDetails.RewardTplPool);
}
return _randomUtil.GetArrayValue(
GetItemRewardPool([], rewardContainerDetails.RewardTypePool, true, true, false)
.ItemPool.Select(item => item.Id
)
.ItemPool.Select(item => item.Id)
);
}
public record ItemRewardPoolResults
{
public List<TemplateItem> ItemPool
{
get;
set;
}
public List<TemplateItem> ItemPool { get; set; }
public HashSet<string> Blacklist
{
get;
set;
}
public HashSet<string> Blacklist { get; set; }
}
}
public class ItemLimit
{
[JsonPropertyName("current")]
public int Current
{
get;
set;
}
public int Current { get; set; }
[JsonPropertyName("max")]
public int Max
{
get;
set;
}
public int Max { get; set; }
}
@@ -18,7 +18,8 @@ public class PMCLootGenerator(
RagfairPriceService ragfairPriceService,
SeasonalEventService seasonalEventService,
WeightedRandomHelper weightedRandomHelper,
ConfigServer configServer)
ConfigServer configServer
)
{
private readonly PmcConfig _pmcConfig = configServer.GetConfig<PmcConfig>();
@@ -53,7 +54,12 @@ public class PMCLootGenerator(
var blacklist = GetContainerLootBlacklist();
// Generate loot and cache - Also pass check to ensure only 1x2 items are allowed (Unheard bots have big pockets, hence the need for 1x2)
var pool = GenerateLootPool(pmcRole, allowedItemTypeWhitelist, blacklist, ItemFitsInto1By2Slot);
var pool = GenerateLootPool(
pmcRole,
allowedItemTypeWhitelist,
blacklist,
ItemFitsInto1By2Slot
);
_pocketLootPool.TryAdd(pmcRole, pool);
return pool;
@@ -83,7 +89,12 @@ public class PMCLootGenerator(
blacklist.UnionWith(_pmcConfig.VestLoot.Blacklist); // Include vest-specific blacklist
// Generate loot and cache - Also pass check to ensure items up to 2x2 are allowed, some vests have big slots
var pool = GenerateLootPool(pmcRole, allowedItemTypeWhitelist, blacklist, ItemFitsInto2By2Slot);
var pool = GenerateLootPool(
pmcRole,
allowedItemTypeWhitelist,
blacklist,
ItemFitsInto2By2Slot
);
_vestLootPool.TryAdd(pmcRole, pool);
return pool;
@@ -99,7 +110,6 @@ public class PMCLootGenerator(
{
lock (BackpackLock)
{
// Already exists, return values
if (_backpackLootPool.TryGetValue(pmcRole, out var existingLootPool))
{
@@ -126,7 +136,12 @@ public class PMCLootGenerator(
/// <param name="itemTplAndParentBlacklist">Item and parent blacklist</param>
/// <param name="genericItemCheck">An optional delegate to validate the TemplateItem object being processed</param>
/// <returns>Dictionary of items and weights inversely tied to the items price</returns>
protected Dictionary<string, double> GenerateLootPool(string pmcRole, HashSet<string> allowedItemTypeWhitelist, HashSet<string> itemTplAndParentBlacklist, Func<TemplateItem, bool>? genericItemCheck)
protected Dictionary<string, double> GenerateLootPool(
string pmcRole,
HashSet<string> allowedItemTypeWhitelist,
HashSet<string> itemTplAndParentBlacklist,
Func<TemplateItem, bool>? genericItemCheck
)
{
var lootPool = new Dictionary<string, double>();
var items = databaseService.GetItems();
@@ -136,13 +151,15 @@ public class PMCLootGenerator(
// Filter all items in DB to ones we want with passed in whitelist + blacklist + generic 'IsValidItem' check
// Also run Delegate if it's not null
var itemTplsToAdd = items.Where(item =>
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
itemHelper.IsValidItem(item.Value.Id) &&
!itemTplAndParentBlacklist.Contains(item.Value.Id) &&
!itemTplAndParentBlacklist.Contains(item.Value.Parent) &&
(genericItemCheck?.Invoke(item.Value) ?? true) // if delegate is null, force check to be true
).Select(x => x.Key);
var itemTplsToAdd = items
.Where(item =>
allowedItemTypeWhitelist.Contains(item.Value.Parent)
&& itemHelper.IsValidItem(item.Value.Id)
&& !itemTplAndParentBlacklist.Contains(item.Value.Id)
&& !itemTplAndParentBlacklist.Contains(item.Value.Parent)
&& (genericItemCheck?.Invoke(item.Value) ?? true) // if delegate is null, force check to be true
)
.Select(x => x.Key);
// Store all items + price in above lootPool dictionary
foreach (var tpl in itemTplsToAdd)
@@ -202,7 +219,6 @@ public class PMCLootGenerator(
logger.Error($"Unable to find price overrides for PMC: {pmcRole}");
return null;
}
/// <summary>
@@ -213,7 +229,10 @@ public class PMCLootGenerator(
/// <returns>Rouble price</returns>
protected double GetItemPrice(string tpl, Dictionary<string, double>? pmcPriceOverrides = null)
{
if (pmcPriceOverrides is not null && pmcPriceOverrides.TryGetValue(tpl, out var overridePrice))
if (
pmcPriceOverrides is not null
&& pmcPriceOverrides.TryGetValue(tpl, out var overridePrice)
)
{
// There's a price override for this item, use override instead of default price
return overridePrice;
@@ -245,7 +264,7 @@ public class PMCLootGenerator(
return $"{item.Properties.Width}x{item.Properties.Height}" switch
{
"1x1" or "1x2" or "2x1" => true,
_ => false
_ => false,
};
}
}
@@ -13,7 +13,6 @@ using SPTarkov.Server.Core.Utils.Cloners;
using SPTarkov.Server.Core.Utils.Json;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Generators;
[Injectable]
@@ -54,9 +53,16 @@ public class PlayerScavGenerator(
var scavKarmaLevel = GetScavKarmaLevel(pmcDataClone);
// use karma level to get correct karmaSettings
if (!_playerScavConfig.KarmaLevel.TryGetValue(scavKarmaLevel.ToString(), out var playerScavKarmaSettings))
if (
!_playerScavConfig.KarmaLevel.TryGetValue(
scavKarmaLevel.ToString(),
out var playerScavKarmaSettings
)
)
{
_logger.Error(_localisationService.GetText("scav-missing_karma_settings", scavKarmaLevel));
_logger.Error(
_localisationService.GetText("scav-missing_karma_settings", scavKarmaLevel)
);
}
if (_logger.IsLogEnabled(LogLevel.Debug))
@@ -101,24 +107,20 @@ public class PlayerScavGenerator(
scavData.Info.Level = GetScavLevel(existingScavDataClone);
scavData.Info.Experience = GetScavExperience(existingScavDataClone);
scavData.Quests = existingScavDataClone.Quests ?? [];
scavData.TaskConditionCounters = existingScavDataClone.TaskConditionCounters ?? new Dictionary<string, TaskConditionCounter>();
scavData.Notes = existingScavDataClone.Notes ??
new Notes
{
DataNotes = new List<Note>()
};
scavData.WishList = existingScavDataClone.WishList ?? new DictionaryOrList<string, int>(new Dictionary<string, int>(), new List<int>());
scavData.TaskConditionCounters =
existingScavDataClone.TaskConditionCounters
?? new Dictionary<string, TaskConditionCounter>();
scavData.Notes = existingScavDataClone.Notes ?? new Notes { DataNotes = new List<Note>() };
scavData.WishList =
existingScavDataClone.WishList
?? new DictionaryOrList<string, int>(new Dictionary<string, int>(), new List<int>());
scavData.Encyclopedia = pmcDataClone.Encyclopedia ?? new Dictionary<string, bool>();
// Add additional items to player scav as loot
AddAdditionalLootToPlayerScavContainers(
playerScavKarmaSettings.LootItemsToAddChancePercent,
scavData,
[
EquipmentSlots.TacticalVest,
EquipmentSlots.Pockets,
EquipmentSlots.Backpack
]
[EquipmentSlots.TacticalVest, EquipmentSlots.Pockets, EquipmentSlots.Backpack]
);
// Remove secure container
@@ -139,8 +141,11 @@ public class PlayerScavGenerator(
/// <param name="possibleItemsToAdd">dict of tpl + % chance to be added</param>
/// <param name="scavData"></param>
/// <param name="containersToAddTo">Possible slotIds to add loot to</param>
protected void AddAdditionalLootToPlayerScavContainers(Dictionary<string, double> possibleItemsToAdd, BotBase scavData,
HashSet<EquipmentSlots> containersToAddTo)
protected void AddAdditionalLootToPlayerScavContainers(
Dictionary<string, double> possibleItemsToAdd,
BotBase scavData,
HashSet<EquipmentSlots> containersToAddTo
)
{
foreach (var tpl in possibleItemsToAdd)
{
@@ -153,7 +158,9 @@ public class PlayerScavGenerator(
var itemResult = _itemHelper.GetItem(tpl.Key);
if (!itemResult.Key)
{
_logger.Warning(_localisationService.GetText("scav-unable_to_add_item_to_player_scav", tpl));
_logger.Warning(
_localisationService.GetText("scav-unable_to_add_item_to_player_scav", tpl)
);
continue;
}
@@ -164,8 +171,8 @@ public class PlayerScavGenerator(
{
Id = _hashUtil.Generate(),
Template = itemTemplate.Id,
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(itemTemplate)
}
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(itemTemplate),
},
};
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
@@ -197,7 +204,9 @@ public class PlayerScavGenerator(
// can be empty during profile creation
if (!pmcData.TradersInfo.TryGetValue(Traders.FENCE, out var fenceInfo))
{
_logger.Warning(_localisationService.GetText("scav-missing_karma_level_getting_default"));
_logger.Warning(
_localisationService.GetText("scav-missing_karma_level_getting_default")
);
return 0;
}
@@ -239,7 +248,10 @@ public class PlayerScavGenerator(
/// </summary>
/// <param name="karmaSettings">Values to modify the bot template with</param>
/// <param name="baseBotNode">bot template to modify according to karama level settings</param>
protected void AdjustBotTemplateWithKarmaSpecificSettings(KarmaLevel karmaSettings, BotType baseBotNode)
protected void AdjustBotTemplateWithKarmaSpecificSettings(
KarmaLevel karmaSettings,
BotType baseBotNode
)
{
// Adjust equipment chance values
foreach (var equipmentKvP in karmaSettings.Modifiers.Equipment)
@@ -251,8 +263,13 @@ public class PlayerScavGenerator(
}
// Try add new key with value
if (!baseBotNode.BotChances.EquipmentChances.TryAdd(equipmentKvP.Key, equipmentKvP.Value))
// Unable to add new, update existing
if (
!baseBotNode.BotChances.EquipmentChances.TryAdd(
equipmentKvP.Key,
equipmentKvP.Value
)
)
// Unable to add new, update existing
{
baseBotNode.BotChances.EquipmentChances[equipmentKvP.Key] += equipmentKvP.Value;
}
@@ -272,7 +289,6 @@ public class PlayerScavGenerator(
baseBotNode.BotChances.WeaponModsChances.TryAdd(modKvP.Key, 0);
baseBotNode.BotChances.WeaponModsChances[modKvP.Key] += value;
}
;
}
@@ -280,14 +296,19 @@ public class PlayerScavGenerator(
var props = baseBotNode.BotGeneration.Items.GetType().GetProperties();
foreach (var itemLimitKvP in karmaSettings.ItemLimits)
{
var prop = props.FirstOrDefault(x => string.Equals(x.Name, itemLimitKvP.Key, StringComparison.OrdinalIgnoreCase));
var prop = props.FirstOrDefault(x =>
string.Equals(x.Name, itemLimitKvP.Key, StringComparison.OrdinalIgnoreCase)
);
prop.SetValue(baseBotNode.BotGeneration.Items, itemLimitKvP.Value);
}
// Blacklist equipment, keyed by equipment slot
foreach (var equipmentBlacklistKvP in karmaSettings.EquipmentBlacklist)
{
baseBotNode.BotInventory.Equipment.TryGetValue(equipmentBlacklistKvP.Key, out var equipmentDict);
baseBotNode.BotInventory.Equipment.TryGetValue(
equipmentBlacklistKvP.Key,
out var equipmentDict
);
foreach (var itemToRemove in equipmentBlacklistKvP.Value)
{
equipmentDict.Remove(itemToRemove);
@@ -311,7 +332,7 @@ public class PlayerScavGenerator(
{
Common = [],
Mastering = [],
Points = 0
Points = 0,
};
}
@@ -357,18 +378,26 @@ public class PlayerScavGenerator(
protected PmcData SetScavCooldownTimer(PmcData scavData, PmcData pmcData)
{
// Get sum of all scav cooldown reduction timer bonuses
var modifier = 1d + pmcData.Bonuses
.Where(x => x.Type == BonusType.ScavCooldownTimer)
.Sum(bonus => (bonus?.Value ?? 1) / 100);
var modifier =
1d
+ pmcData
.Bonuses.Where(x => x.Type == BonusType.ScavCooldownTimer)
.Sum(bonus => (bonus?.Value ?? 1) / 100);
var fenceInfo = _fenceService.GetFenceInfo(pmcData);
modifier *= fenceInfo.SavageCooldownModifier ?? 1d;
// Make sure to apply ScavCooldownTimer bonus from Hideout if the player has it.
var scavLockDuration = _databaseService.GetGlobals().Configuration.SavagePlayCooldown * modifier;
var scavLockDuration =
_databaseService.GetGlobals().Configuration.SavagePlayCooldown * modifier;
var fullProfile = _profileHelper.GetFullProfile(pmcData?.SessionId);
if (fullProfile?.ProfileInfo?.Edition?.StartsWith(AccountTypes.SPT_DEVELOPER, StringComparison.OrdinalIgnoreCase) ?? false)
if (
fullProfile?.ProfileInfo?.Edition?.StartsWith(
AccountTypes.SPT_DEVELOPER,
StringComparison.OrdinalIgnoreCase
) ?? false
)
{
// Force lock duration to 10seconds for dev profiles
scavLockDuration = 10;
@@ -376,7 +405,9 @@ public class PlayerScavGenerator(
if (scavData?.Info != null)
{
scavData.Info.SavageLockTime = Math.Round(_timeUtil.GetTimeStamp() + (scavLockDuration ?? 0));
scavData.Info.SavageLockTime = Math.Round(
_timeUtil.GetTimeStamp() + (scavLockDuration ?? 0)
);
}
return scavData;
@@ -11,7 +11,8 @@ namespace SPTarkov.Server.Core.Generators;
public class PmcWaveGenerator(
ISptLogger<PmcWaveGenerator> logger,
DatabaseService databaseService,
ConfigServer configServer)
ConfigServer configServer
)
{
protected readonly PmcConfig _pmcConfig = configServer.GetConfig<PmcConfig>();
@@ -31,7 +31,7 @@ public class RagfairAssortGenerator(
BaseClasses.INVENTORY,
BaseClasses.STATIONARY_CONTAINER,
BaseClasses.POCKETS,
BaseClasses.BUILT_IN_INSERTS
BaseClasses.BUILT_IN_INSERTS,
];
/// <summary>
@@ -53,7 +53,9 @@ public class RagfairAssortGenerator(
List<List<Item>> results = [];
// Get cloned items from db
var dbItemsClone = itemHelper.GetItems().Where(item => !string.Equals(item.Type, "Node", StringComparison.OrdinalIgnoreCase));
var dbItemsClone = itemHelper
.GetItems()
.Where(item => !string.Equals(item.Type, "Node", StringComparison.OrdinalIgnoreCase));
// Store processed preset tpls so we don't add them when processing non-preset items
HashSet<string> processedArmorItems = [];
@@ -76,7 +78,7 @@ public class RagfairAssortGenerator(
{
StackObjectsCount = 99999999,
UnlimitedCount = true,
SptPresetId = preset.Id
SptPresetId = preset.Id,
};
results.Add(presetAndMods);
@@ -91,24 +93,21 @@ public class RagfairAssortGenerator(
// Skip seasonal items when not in-season
if (
RagfairConfig.Dynamic.RemoveSeasonalItemsWhenNotInEvent &&
!seasonalEventActive &&
seasonalItemTplBlacklist.Contains(item.Id)
RagfairConfig.Dynamic.RemoveSeasonalItemsWhenNotInEvent
&& !seasonalEventActive
&& seasonalItemTplBlacklist.Contains(item.Id)
)
{
continue;
}
if (processedArmorItems.Contains(item.Id))
// Already processed
// Already processed
{
continue;
}
var ragfairAssort = CreateRagfairAssortRootItem(
item.Id,
item.Id
); // tpl and id must be the same so hideout recipe rewards work
var ragfairAssort = CreateRagfairAssortRootItem(item.Id, item.Id); // tpl and id must be the same so hideout recipe rewards work
results.Add([ragfairAssort]);
}
@@ -147,11 +146,7 @@ public class RagfairAssortGenerator(
Template = tplId,
ParentId = "hideout",
SlotId = "hideout",
Upd = new Upd
{
StackObjectsCount = 99999999,
UnlimitedCount = true
}
Upd = new Upd { StackObjectsCount = 99999999, UnlimitedCount = true },
};
}
}
@@ -68,7 +68,15 @@ public class RagfairOfferGenerator(
bool sellInOnePiece = false
)
{
var offer = CreateOffer(userId, time, items, barterScheme, loyalLevel, quantity, sellInOnePiece);
var offer = CreateOffer(
userId,
time,
items,
barterScheme,
loyalLevel,
quantity,
sellInOnePiece
);
ragfairOfferService.AddOffer(offer);
return offer;
@@ -95,25 +103,25 @@ public class RagfairOfferGenerator(
bool isPackOffer = false
)
{
var offerRequirements = barterScheme.Select(barter =>
var offerRequirements = barterScheme
.Select(barter =>
{
var offerRequirement = new OfferRequirement
{
var offerRequirement = new OfferRequirement
{
Template = barter.Template,
Count = Math.Round(barter.Count.Value, 2),
OnlyFunctional = barter.OnlyFunctional ?? false
};
Template = barter.Template,
Count = Math.Round(barter.Count.Value, 2),
OnlyFunctional = barter.OnlyFunctional ?? false,
};
// Dogtags define level and side
if (barter.Level != null)
{
offerRequirement.Level = barter.Level;
offerRequirement.Side = barter.Side;
}
return offerRequirement;
// Dogtags define level and side
if (barter.Level != null)
{
offerRequirement.Level = barter.Level;
offerRequirement.Side = barter.Side;
}
)
return offerRequirement;
})
.ToList();
// Clone to avoid modifying original array
@@ -122,13 +130,21 @@ public class RagfairOfferGenerator(
// Hydrate ammo boxes with cartridges + ensure only 1 item is present (ammo box)
// On offer refresh don't re-add cartridges to ammo box that already has cartridges
if (itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX) && itemsClone.Count == 1)
if (
itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX)
&& itemsClone.Count == 1
)
{
itemHelper.AddCartridgesToAmmoBox(itemsClone, itemHelper.GetItem(rootItem.Template).Value);
itemHelper.AddCartridgesToAmmoBox(
itemsClone,
itemHelper.GetItem(rootItem.Template).Value
);
}
var roubleListingPrice = Math.Round(ConvertOfferRequirementsIntoRoubles(offerRequirements));
var singleItemListingPrice = isPackOffer ? roubleListingPrice / quantity : roubleListingPrice;
var singleItemListingPrice = isPackOffer
? roubleListingPrice / quantity
: roubleListingPrice;
var offer = new RagfairOffer
{
@@ -146,7 +162,7 @@ public class RagfairOfferGenerator(
LoyaltyLevel = loyalLevel,
SellInOnePiece = isPackOffer,
Locked = false,
Quantity = quantity
Quantity = quantity,
};
offerCounter++;
@@ -165,11 +181,7 @@ public class RagfairOfferGenerator(
// Trader offer
if (isTrader)
{
return new RagfairOfferUser
{
Id = userId,
MemberType = MemberCategory.Trader
};
return new RagfairOfferUser { Id = userId, MemberType = MemberCategory.Trader };
}
var isPlayerOffer = profileHelper.IsPlayer(userId);
@@ -185,7 +197,7 @@ public class RagfairOfferGenerator(
Rating = playerProfile.RagfairInfo.Rating ?? 0,
IsRatingGrowing = playerProfile.RagfairInfo.IsRatingGrowing,
Avatar = null,
Aid = playerProfile.Aid
Aid = playerProfile.Aid,
};
}
@@ -201,7 +213,7 @@ public class RagfairOfferGenerator(
),
IsRatingGrowing = randomUtil.GetBool(),
Avatar = null,
Aid = hashUtil.GenerateAccountId()
Aid = hashUtil.GenerateAccountId(),
};
}
@@ -210,14 +222,17 @@ public class RagfairOfferGenerator(
/// </summary>
/// <param name="offerRequirements"> barter requirements for offer </param>
/// <returns> rouble cost of offer </returns>
protected double ConvertOfferRequirementsIntoRoubles(IEnumerable<OfferRequirement> offerRequirements)
protected double ConvertOfferRequirementsIntoRoubles(
IEnumerable<OfferRequirement> offerRequirements
)
{
var roublePrice = 0d;
foreach (var requirement in offerRequirements)
{
roublePrice += paymentHelper.IsMoneyTpl(requirement.Template)
? Math.Round(CalculateRoublePrice(requirement.Count.Value, requirement.Template))
: ragfairPriceService.GetFleaPriceForItem(requirement.Template) * requirement.Count.Value; // Get flea price for barter offer items
: ragfairPriceService.GetFleaPriceForItem(requirement.Template)
* requirement.Count.Value; // Get flea price for barter offer items
}
return roublePrice;
@@ -290,7 +305,10 @@ public class RagfairOfferGenerator(
}
// Generated pmc offer
return randomUtil.GetDouble(ragfairConfig.Dynamic.Rating.Min, ragfairConfig.Dynamic.Rating.Max);
return randomUtil.GetDouble(
ragfairConfig.Dynamic.Rating.Min,
ragfairConfig.Dynamic.Rating.Max
);
}
/// <summary>
@@ -301,13 +319,15 @@ public class RagfairOfferGenerator(
protected bool GetRatingGrowing(string userID)
{
if (profileHelper.IsPlayer(userID))
// player offer
// player offer
{
return saveServer.GetProfile(userID).CharacterData?.PmcData?.RagfairInfo?.IsRatingGrowing ?? false;
return saveServer
.GetProfile(userID)
.CharacterData?.PmcData?.RagfairInfo?.IsRatingGrowing ?? false;
}
if (ragfairServerHelper.IsTrader(userID))
// trader offer
// trader offer
{
return true;
}
@@ -328,20 +348,30 @@ public class RagfairOfferGenerator(
if (profileHelper.IsPlayer(userID))
{
// Player offer = current time + offerDurationTimeInHour;
var offerDurationTimeHours = databaseService.GetGlobals().Configuration.RagFair.OfferDurationTimeInHour;
return (long) (timeUtil.GetTimeStamp() + Math.Round((double) offerDurationTimeHours * TimeUtil.OneHourAsSeconds));
var offerDurationTimeHours = databaseService
.GetGlobals()
.Configuration.RagFair.OfferDurationTimeInHour;
return (long)(
timeUtil.GetTimeStamp()
+ Math.Round((double)offerDurationTimeHours * TimeUtil.OneHourAsSeconds)
);
}
if (ragfairServerHelper.IsTrader(userID))
// Trader offer
// Trader offer
{
return (long) databaseService.GetTrader(userID).Base.NextResupply;
return (long)databaseService.GetTrader(userID).Base.NextResupply;
}
// Generated fake-player offer
return (long) Math.Round(
time + randomUtil.GetDouble(ragfairConfig.Dynamic.EndTimeSeconds.Min, ragfairConfig.Dynamic.EndTimeSeconds.Max)
);
return (long)
Math.Round(
time
+ randomUtil.GetDouble(
ragfairConfig.Dynamic.EndTimeSeconds.Min,
ragfairConfig.Dynamic.EndTimeSeconds.Max
)
);
}
/// <summary>
@@ -360,7 +390,9 @@ public class RagfairOfferGenerator(
stopwatch.Stop();
if (logger.IsLogEnabled(LogLevel.Debug) && stopwatch.ElapsedMilliseconds > 0)
{
logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts - {assortItemsToProcess.Count} items");
logger.Debug(
$"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts - {assortItemsToProcess.Count} items"
);
}
stopwatch.Restart();
@@ -369,10 +401,13 @@ public class RagfairOfferGenerator(
{
tasks.Add(
Task.Factory.StartNew(() =>
{
CreateOffersFromAssort(assortItem, replacingExpiredOffers, ragfairConfig.Dynamic);
}
)
{
CreateOffersFromAssort(
assortItem,
replacingExpiredOffers,
ragfairConfig.Dynamic
);
})
);
}
@@ -408,7 +443,10 @@ public class RagfairOfferGenerator(
// Armor presets can hold plates above the allowed flea level, remove if necessary
if (isPreset && ragfairConfig.Dynamic.Blacklist.EnableBsgList)
{
RemoveBannedPlatesFromPreset(assortItemWithChildren, ragfairConfig.Dynamic.Blacklist.ArmorPlate);
RemoveBannedPlatesFromPreset(
assortItemWithChildren,
ragfairConfig.Dynamic.Blacklist.ArmorPlate
);
}
// Get number of offers to create
@@ -427,7 +465,12 @@ public class RagfairOfferGenerator(
clonedAssort[0].ParentId = null;
clonedAssort[0].SlotId = null;
CreateSingleOfferForItem(hashUtil.Generate(), clonedAssort, isPreset, itemToSellDetails.Value);
CreateSingleOfferForItem(
hashUtil.Generate(),
clonedAssort,
isPreset,
itemToSellDetails.Value
);
}
}
@@ -443,14 +486,16 @@ public class RagfairOfferGenerator(
)
{
if (!itemHelper.ArmorItemCanHoldMods(presetWithChildren[0].Template))
// Cant hold armor inserts, skip
// Cant hold armor inserts, skip
{
return false;
}
var plateSlots = presetWithChildren.Where(item => itemHelper.GetRemovablePlateSlotIds().Contains(item.SlotId?.ToLower())).ToList();
var plateSlots = presetWithChildren
.Where(item => itemHelper.GetRemovablePlateSlotIds().Contains(item.SlotId?.ToLower()))
.ToList();
if (plateSlots.Count == 0)
// Has no plate slots e.g. "front_plate", exit
// Has no plate slots e.g. "front_plate", exit
{
return false;
}
@@ -500,10 +545,10 @@ public class RagfairOfferGenerator(
var isBarterOffer = randomUtil.GetChance100(ragfairConfig.Dynamic.Barter.ChancePercent);
var isPackOffer =
randomUtil.GetChance100(ragfairConfig.Dynamic.Pack.ChancePercent) &&
!isBarterOffer &&
itemWithChildren.Count == 1 &&
itemHelper.IsOfBaseclasses(
randomUtil.GetChance100(ragfairConfig.Dynamic.Pack.ChancePercent)
&& !isBarterOffer
&& itemWithChildren.Count == 1
&& itemHelper.IsOfBaseclasses(
itemWithChildren[0].Template,
ragfairConfig.Dynamic.Pack.ItemTypeWhitelist
);
@@ -513,15 +558,21 @@ public class RagfairOfferGenerator(
{
var armorConfig = ragfairConfig.Dynamic.Armor;
var shouldRemovePlates = randomUtil.GetChance100(armorConfig.RemoveRemovablePlateChance);
if (shouldRemovePlates && itemHelper.ArmorItemHasRemovablePlateSlots(itemWithChildren[0].Template))
var shouldRemovePlates = randomUtil.GetChance100(
armorConfig.RemoveRemovablePlateChance
);
if (
shouldRemovePlates
&& itemHelper.ArmorItemHasRemovablePlateSlots(itemWithChildren[0].Template)
)
{
var offerItemPlatesToRemove = itemWithChildren.Where(item =>
armorConfig.PlateSlotIdToRemovePool.Contains(item.SlotId?.ToLower())
);
// Latest first, to ensure we don't move later items off by 1 each time we remove an item below it
var indexesToRemove = offerItemPlatesToRemove.Select(plateItem => itemWithChildren.IndexOf(plateItem))
var indexesToRemove = offerItemPlatesToRemove
.Select(plateItem => itemWithChildren.IndexOf(plateItem))
.ToHashSet();
foreach (var index in indexesToRemove.OrderByDescending(x => x))
{
@@ -540,7 +591,11 @@ public class RagfairOfferGenerator(
);
// Don't randomise pack items
barterScheme = CreateCurrencyBarterScheme(itemWithChildren, isPackOffer, desiredStackSize);
barterScheme = CreateCurrencyBarterScheme(
itemWithChildren,
isPackOffer,
desiredStackSize
);
}
else if (isBarterOffer)
{
@@ -600,13 +655,14 @@ public class RagfairOfferGenerator(
}
var blacklist = ragfairConfig.Dynamic.Blacklist;
var childAssortItems = assortsClone.Items
.Where(x => !string.Equals(x.ParentId, "hideout", StringComparison.Ordinal)).ToList();
var childAssortItems = assortsClone
.Items.Where(x => !string.Equals(x.ParentId, "hideout", StringComparison.Ordinal))
.ToList();
foreach (var item in assortsClone.Items)
{
// We only want to process 'base/root' items, no children
if (item.SlotId != "hideout")
// skip mod items
// skip mod items
{
continue;
}
@@ -617,12 +673,17 @@ public class RagfairOfferGenerator(
var itemDetails = itemHelper.GetItem(item.Template);
if (!itemDetails.Key)
{
logger.Warning(localisationService.GetText("ragfair-tpl_not_a_valid_item", item.Template));
logger.Warning(
localisationService.GetText("ragfair-tpl_not_a_valid_item", item.Template)
);
continue;
}
// Don't include items that BSG has blacklisted from flea
if (blacklist.EnableBsgList && !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false))
if (
blacklist.EnableBsgList
&& !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false)
)
{
continue;
}
@@ -631,7 +692,7 @@ public class RagfairOfferGenerator(
var isPreset = presetHelper.IsPreset(item.Id);
var items = isPreset
? ragfairServerHelper.GetPresetItems(item)
: [item, ..itemHelper.FindAndReturnChildrenByAssort(item.Id, childAssortItems)];
: [item, .. itemHelper.FindAndReturnChildrenByAssort(item.Id, childAssortItems)];
if (!assortsClone.BarterScheme.TryGetValue(item.Id, out var barterScheme))
{
@@ -642,7 +703,7 @@ public class RagfairOfferGenerator(
{
itemId = item.Id,
tpl = item.Template,
name = trader.Base.Nickname
name = trader.Base.Nickname,
}
)
);
@@ -652,7 +713,14 @@ public class RagfairOfferGenerator(
var barterSchemeItems = barterScheme[0];
var loyalLevel = assortsClone.LoyalLevelItems[item.Id];
CreateAndAddFleaOffer(traderID, time, items, barterSchemeItems, loyalLevel, (int?) item.Upd.StackObjectsCount ?? 1);
CreateAndAddFleaOffer(
traderID,
time,
items,
barterSchemeItems,
loyalLevel,
(int?)item.Upd.StackObjectsCount ?? 1
);
// Refresh complete, reset flag to false
trader.Base.RefreshTraderRagfairOffers = false;
@@ -666,7 +734,11 @@ public class RagfairOfferGenerator(
/// <param name="userID"> ID of owner of item </param>
/// <param name="itemWithMods"> Item and mods, get condition of first item (only first array item is modified) </param>
/// <param name="itemDetails"> DB details of first item</param>
protected void RandomiseOfferItemUpdProperties(string userID, List<Item> itemWithMods, TemplateItem itemDetails)
protected void RandomiseOfferItemUpdProperties(
string userID,
List<Item> itemWithMods,
TemplateItem itemDetails
)
{
// Add any missing properties to first item in array
AddMissingConditions(itemWithMods[0]);
@@ -675,13 +747,17 @@ public class RagfairOfferGenerator(
{
var parentId = GetDynamicConditionIdForTpl(itemDetails.Id);
if (string.IsNullOrEmpty(parentId))
// No condition details found, don't proceed with modifying item conditions
// No condition details found, don't proceed with modifying item conditions
{
return;
}
// Roll random chance to randomise item condition
if (randomUtil.GetChance100(ragfairConfig.Dynamic.Condition[parentId].ConditionChance * 100))
if (
randomUtil.GetChance100(
ragfairConfig.Dynamic.Condition[parentId].ConditionChance * 100
)
)
{
RandomiseItemCondition(parentId, itemWithMods, itemDetails);
}
@@ -723,29 +799,35 @@ public class RagfairOfferGenerator(
var rootItem = itemWithMods[0];
var itemConditionValues = ragfairConfig.Dynamic.Condition[conditionSettingsId];
var maxMultiplier = randomUtil.GetDouble(itemConditionValues.Max.Min, itemConditionValues.Max.Min);
var maxMultiplier = randomUtil.GetDouble(
itemConditionValues.Max.Min,
itemConditionValues.Max.Min
);
var currentMultiplier = randomUtil.GetDouble(
itemConditionValues.Current.Min,
itemConditionValues.Current.Max
);
// Randomise armor + plates + armor related things
if (itemHelper.ArmorItemCanHoldMods(rootItem.Template) ||
itemHelper.IsOfBaseclasses(rootItem.Template, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
)
if (
itemHelper.ArmorItemCanHoldMods(rootItem.Template)
|| itemHelper.IsOfBaseclasses(
rootItem.Template,
[BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT]
)
)
{
RandomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier);
// Add hits to visor
var visorMod = itemWithMods.FirstOrDefault(item => item.ParentId == BaseClasses.ARMORED_EQUIPMENT && item.SlotId == "mod_equipment_000");
var visorMod = itemWithMods.FirstOrDefault(item =>
item.ParentId == BaseClasses.ARMORED_EQUIPMENT && item.SlotId == "mod_equipment_000"
);
if (randomUtil.GetChance100(25) && visorMod != null)
{
itemHelper.AddUpdObjectToItem(visorMod);
visorMod.Upd.FaceShield = new UpdFaceShield
{
Hits = randomUtil.GetInt(1, 3)
};
visorMod.Upd.FaceShield = new UpdFaceShield { Hits = randomUtil.GetInt(1, 3) };
}
return;
@@ -754,7 +836,12 @@ public class RagfairOfferGenerator(
// Randomise Weapons
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.WEAPON))
{
RandomiseWeaponDurability(itemWithMods[0], itemDetails, maxMultiplier, currentMultiplier);
RandomiseWeaponDurability(
itemWithMods[0],
itemDetails,
maxMultiplier,
currentMultiplier
);
return;
}
@@ -762,7 +849,7 @@ public class RagfairOfferGenerator(
if (rootItem.Upd?.MedKit != null)
{
// Randomize health
var hpResource = Math.Round((double) rootItem.Upd.MedKit.HpResource * maxMultiplier);
var hpResource = Math.Round((double)rootItem.Upd.MedKit.HpResource * maxMultiplier);
rootItem.Upd.MedKit.HpResource = hpResource == 0D ? 1D : hpResource;
return;
}
@@ -770,14 +857,15 @@ public class RagfairOfferGenerator(
if (rootItem.Upd?.Key != null && itemDetails.Properties.MaximumNumberOfUsage > 1)
{
// Randomize key uses
rootItem.Upd.Key.NumberOfUsages = (int?) Math.Round(itemDetails.Properties.MaximumNumberOfUsage.Value * (1 - maxMultiplier));
rootItem.Upd.Key.NumberOfUsages = (int?)
Math.Round(itemDetails.Properties.MaximumNumberOfUsage.Value * (1 - maxMultiplier));
return;
}
if (rootItem.Upd?.FoodDrink != null)
{
// randomize food/drink value
var hpPercent = Math.Round((double) itemDetails.Properties.MaxResource * maxMultiplier);
var hpPercent = Math.Round((double)itemDetails.Properties.MaxResource * maxMultiplier);
rootItem.Upd.FoodDrink.HpPercent = hpPercent == 0D ? 1D : hpPercent;
return;
@@ -786,7 +874,9 @@ public class RagfairOfferGenerator(
if (rootItem.Upd?.RepairKit != null)
{
// randomize repair kit (armor/weapon) uses
var resource = Math.Round((double) itemDetails.Properties.MaxRepairResource * maxMultiplier);
var resource = Math.Round(
(double)itemDetails.Properties.MaxRepairResource * maxMultiplier
);
rootItem.Upd.RepairKit.Resource = resource == 0D ? 1D : resource;
return;
@@ -795,11 +885,11 @@ public class RagfairOfferGenerator(
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.FUEL))
{
var totalCapacity = itemDetails.Properties.MaxResource;
var remainingFuel = Math.Round((double) totalCapacity * maxMultiplier);
var remainingFuel = Math.Round((double)totalCapacity * maxMultiplier);
rootItem.Upd.Resource = new UpdResource
{
UnitsConsumed = totalCapacity - remainingFuel,
Value = remainingFuel
Value = remainingFuel,
};
}
}
@@ -821,13 +911,19 @@ public class RagfairOfferGenerator(
// Max
var baseMaxDurability = itemDbDetails.Properties.MaxDurability;
var lowestMaxDurability = randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double) lowestMaxDurability, (double) baseMaxDurability));
var chosenMaxDurability = Math.Round(
randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability)
);
// Current
var lowestCurrentDurability = randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
var chosenCurrentDurability = Math.Round(randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability));
var lowestCurrentDurability =
randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
var chosenCurrentDurability = Math.Round(
randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability)
);
item.Upd.Repairable.Durability = chosenCurrentDurability == 0 ? 1D : chosenCurrentDurability; // Never var value become 0
item.Upd.Repairable.Durability =
chosenCurrentDurability == 0 ? 1D : chosenCurrentDurability; // Never var value become 0
item.Upd.Repairable.MaxDurability = chosenMaxDurability;
}
@@ -851,16 +947,22 @@ public class RagfairOfferGenerator(
itemHelper.AddUpdObjectToItem(armorItem);
var baseMaxDurability = itemDbDetails.Properties.MaxDurability;
var lowestMaxDurability = randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double) lowestMaxDurability, (double) baseMaxDurability));
var lowestMaxDurability =
randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
var chosenMaxDurability = Math.Round(
randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability)
);
var lowestCurrentDurability = randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
var chosenCurrentDurability = Math.Round(randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability));
var lowestCurrentDurability =
randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
var chosenCurrentDurability = Math.Round(
randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability)
);
armorItem.Upd.Repairable = new UpdRepairable
{
Durability = chosenCurrentDurability == 0D ? 1D : chosenCurrentDurability, // Never var value become 0
MaxDurability = chosenMaxDurability
MaxDurability = chosenMaxDurability,
};
}
}
@@ -886,7 +988,7 @@ public class RagfairOfferGenerator(
item.Upd.Repairable = new UpdRepairable
{
Durability = props.Durability,
MaxDurability = props.Durability
MaxDurability = props.Durability,
};
return;
@@ -894,20 +996,14 @@ public class RagfairOfferGenerator(
if (isMedkit && props.MaxHpResource > 0)
{
item.Upd.MedKit = new UpdMedKit
{
HpResource = props.MaxHpResource
};
item.Upd.MedKit = new UpdMedKit { HpResource = props.MaxHpResource };
return;
}
if (isKey)
{
item.Upd.Key = new UpdKey
{
NumberOfUsages = 0
};
item.Upd.Key = new UpdKey { NumberOfUsages = 0 };
return;
}
@@ -915,20 +1011,14 @@ public class RagfairOfferGenerator(
// Food/drink
if (isConsumable)
{
item.Upd.FoodDrink = new UpdFoodDrink
{
HpPercent = props.MaxResource
};
item.Upd.FoodDrink = new UpdFoodDrink { HpPercent = props.MaxResource };
return;
}
if (isRepairKit)
{
item.Upd.RepairKit = new UpdRepairKit
{
Resource = props.MaxRepairResource
};
item.Upd.RepairKit = new UpdRepairKit { Resource = props.MaxRepairResource };
}
}
@@ -938,7 +1028,10 @@ public class RagfairOfferGenerator(
/// <param name="offerItems"> Items for sale in offer </param>
/// <param name="barterConfig"> Barter config from ragfairConfig.Dynamic.barter </param>
/// <returns> Barter scheme </returns>
protected List<BarterScheme> CreateBarterBarterScheme(List<Item> offerItems, BarterDetails barterConfig)
protected List<BarterScheme> CreateBarterBarterScheme(
List<Item> offerItems,
BarterDetails barterConfig
)
{
// Get flea price of item being sold
var priceOfOfferItem = ragfairPriceService.GetDynamicOfferPriceForOffer(
@@ -954,13 +1047,17 @@ public class RagfairOfferGenerator(
}
// Get a randomised number of barter items to list offer for
var barterItemCount = randomUtil.GetInt(barterConfig.ItemCountMin, barterConfig.ItemCountMax);
var barterItemCount = randomUtil.GetInt(
barterConfig.ItemCountMin,
barterConfig.ItemCountMax
);
// Get desired cost of individual item offer will be listed for e.g. offer = 15k, item count = 3, desired item cost = 5k
var desiredItemCostRouble = Math.Round(priceOfOfferItem / barterItemCount);
// Rouble amount to go above/below when looking for an item (Wiggle cost of item a little)
var offerCostVarianceRoubles = desiredItemCostRouble * barterConfig.PriceRangeVariancePercent / 100;
var offerCostVarianceRoubles =
desiredItemCostRouble * barterConfig.PriceRangeVariancePercent / 100;
// Dict of items and their flea price (cached on first use)
var itemFleaPrices = GetFleaPricesAsArray();
@@ -969,13 +1066,15 @@ public class RagfairOfferGenerator(
var min = desiredItemCostRouble - offerCostVarianceRoubles;
var max = desiredItemCostRouble + offerCostVarianceRoubles;
var itemsInsidePriceBounds = itemFleaPrices.Where(itemAndPrice =>
itemAndPrice.Price >= min &&
itemAndPrice.Price <= max &&
!string.Equals(itemAndPrice.Tpl, offerItems[0].Template,
StringComparison.OrdinalIgnoreCase) // Don't allow the item being sold to be chosen
itemAndPrice.Price >= min
&& itemAndPrice.Price <= max
&& !string.Equals(
itemAndPrice.Tpl,
offerItems[0].Template,
StringComparison.OrdinalIgnoreCase
) // Don't allow the item being sold to be chosen
);
// No items on flea have a matching price, fall back to currency
if (!itemsInsidePriceBounds.Any())
{
@@ -985,14 +1084,7 @@ public class RagfairOfferGenerator(
// Choose random item from price-filtered flea items
var randomItem = randomUtil.GetArrayValue(itemsInsidePriceBounds.ToList());
return
[
new BarterScheme
{
Count = barterItemCount,
Template = randomItem.Tpl
}
];
return [new BarterScheme { Count = barterItemCount, Template = randomItem.Tpl }];
}
/// <summary>
@@ -1008,16 +1100,13 @@ public class RagfairOfferGenerator(
// Only get prices for items that also exist in items.json
var filteredFleaItems = fleaPrices
.Select(kvTpl => new TplWithFleaPrice
{
Tpl = kvTpl.Key,
Price = kvTpl.Value
}
)
.Select(kvTpl => new TplWithFleaPrice { Tpl = kvTpl.Key, Price = kvTpl.Value })
.Where(item => itemHelper.GetItem(item.Tpl).Key);
var itemTypeBlacklist = ragfairConfig.Dynamic.Barter.ItemTypeBlacklist;
allowedFleaPriceItemsForBarter = filteredFleaItems.Where(item => !itemHelper.IsOfBaseclasses(item.Tpl, itemTypeBlacklist)).ToList();
allowedFleaPriceItemsForBarter = filteredFleaItems
.Where(item => !itemHelper.IsOfBaseclasses(item.Tpl, itemTypeBlacklist))
.ToList();
}
return allowedFleaPriceItemsForBarter;
@@ -1037,15 +1126,13 @@ public class RagfairOfferGenerator(
)
{
var currency = ragfairServerHelper.GetDynamicOfferCurrency();
var price = ragfairPriceService.GetDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer) * multiplier;
var price =
ragfairPriceService.GetDynamicOfferPriceForOffer(
offerWithChildren,
currency,
isPackOffer
) * multiplier;
return
[
new BarterScheme
{
Count = price,
Template = currency
}
];
return [new BarterScheme { Count = price, Template = currency }];
}
}
@@ -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();
@@ -64,10 +64,15 @@ public class RepeatableQuestRewardGenerator(
string traderId,
RepeatableQuestConfig repeatableConfig,
BaseQuestConfig eliminationConfig,
List<string>? rewardTplBlacklist = null)
List<string>? rewardTplBlacklist = null
)
{
// 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;
@@ -77,7 +82,7 @@ public class RepeatableQuestRewardGenerator(
{
Started = [],
Success = [],
Fail = []
Fail = [],
};
// Start reward index to keep track
@@ -95,28 +100,40 @@ public class RepeatableQuestRewardGenerator(
AvailableInGameEditions = [],
Index = rewardIndex,
Value = rewardParams.RewardXP,
Type = RewardType.Experience
Type = RewardType.Experience,
}
);
rewardIndex++;
}
// Add money reward
rewards.Success.Add(GetMoneyReward(traderId, rewardParams.RewardRoubles.Value, rewardIndex));
rewards.Success.Add(
GetMoneyReward(traderId, rewardParams.RewardRoubles.Value, rewardIndex)
);
rewardIndex++;
// Add GP coin reward
rewards.Success.Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount.Value, rewardIndex));
rewards.Success.Add(
GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount.Value, 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?.RewardCanBeWeapon ??
(false && _randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent ?? 0))
)
if (
traderWhitelistDetails?.RewardCanBeWeapon
?? (
false
&& _randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent ?? 0)
)
)
{
var chosenWeapon = GetRandomWeaponPresetWithinBudget(itemRewardBudget.Value, rewardIndex);
var chosenWeapon = GetRandomWeaponPresetWithinBudget(
itemRewardBudget.Value,
rewardIndex
);
if (chosenWeapon is not null)
{
rewards.Success.Add(chosenWeapon.Value.Key);
@@ -127,11 +144,16 @@ 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)
var filteredRewardItemPool = inBudgetRewardItemPool.Where(item =>
!rewardTplBlacklist.Contains(item.Id)
);
if (filteredRewardItemPool.Count() > 0)
{
@@ -139,7 +161,6 @@ public class RepeatableQuestRewardGenerator(
}
}
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
@@ -159,7 +180,9 @@ 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++;
}
}
@@ -176,19 +199,21 @@ public class RepeatableQuestRewardGenerator(
Target = traderId,
Value = rewardParams.RewardReputation,
Type = RewardType.TraderStanding,
Index = rewardIndex
Index = rewardIndex,
};
rewards.Success.Add(reward);
rewardIndex++;
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Adding: {rewardParams.RewardReputation} {traderId} trader reputation reward");
_logger.Debug(
$"Adding: {rewardParams.RewardReputation} {traderId} trader reputation reward"
);
}
}
// Chance of adding skill reward
if (_randomUtil.GetChance100((double) rewardParams.SkillRewardChance * 100))
if (_randomUtil.GetChance100((double)rewardParams.SkillRewardChance * 100))
{
var targetSkill = _randomUtil.GetArrayValue(eliminationConfig.PossibleSkillRewards);
Reward reward = new()
@@ -200,20 +225,26 @@ public class RepeatableQuestRewardGenerator(
Target = targetSkill,
Value = rewardParams.SkillPointReward,
Type = RewardType.Skill,
Index = rewardIndex
Index = rewardIndex,
};
rewards.Success.Add(reward);
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? difficulty, int pmcLevel)
protected QuestRewardValues GetQuestRewardValues(
RewardScaling? rewardScaling,
double? difficulty,
int pmcLevel
)
{
// difficulty could go from 0.2 ... -> for lowest difficulty receive 0.2*nominal reward
var levelsConfig = rewardScaling.Levels;
@@ -258,61 +289,102 @@ public class RepeatableQuestRewardGenerator(
gpCoinConfig,
rewardSpreadConfig
),
RewardXP = GetRewardXp(effectiveDifficulty, pmcLevel, levelsConfig, xpConfig, rewardSpreadConfig)
RewardXP = GetRewardXp(
effectiveDifficulty,
pmcLevel,
levelsConfig,
xpConfig,
rewardSpreadConfig
),
};
}
protected double GetRewardXp(double? effectiveDifficulty, int pmcLevel, List<double>? levelsConfig,
List<double>? xpConfig, double? rewardSpreadConfig)
protected double GetRewardXp(
double? effectiveDifficulty,
int pmcLevel,
List<double>? levelsConfig,
List<double>? xpConfig,
double? rewardSpreadConfig
)
{
return Math.Floor(
effectiveDifficulty *
_mathUtil.Interp1(pmcLevel, levelsConfig, xpConfig) *
_randomUtil.GetDouble((double) (1 - rewardSpreadConfig), (double) (1 + rewardSpreadConfig)) ??
0
effectiveDifficulty
* _mathUtil.Interp1(pmcLevel, levelsConfig, xpConfig)
* _randomUtil.GetDouble(
(double)(1 - rewardSpreadConfig),
(double)(1 + rewardSpreadConfig)
)
?? 0
);
}
protected double GetGpCoinRewardCount(double? effectiveDifficulty, int pmcLevel, List<double>? levelsConfig,
protected double GetGpCoinRewardCount(
double? effectiveDifficulty,
int pmcLevel,
List<double>? levelsConfig,
List<double>? gpCoinConfig,
double? rewardSpreadConfig)
double? rewardSpreadConfig
)
{
return Math.Ceiling(
effectiveDifficulty *
_mathUtil.Interp1(pmcLevel, levelsConfig, gpCoinConfig) *
_randomUtil.GetDouble((double) (1 - rewardSpreadConfig), (double) (1 + rewardSpreadConfig)) ??
0
effectiveDifficulty
* _mathUtil.Interp1(pmcLevel, levelsConfig, gpCoinConfig)
* _randomUtil.GetDouble(
(double)(1 - rewardSpreadConfig),
(double)(1 + rewardSpreadConfig)
)
?? 0
);
}
protected double GetRewardRep(double? effectiveDifficulty, int pmcLevel, List<double>? levelsConfig,
protected double GetRewardRep(
double? effectiveDifficulty,
int pmcLevel,
List<double>? levelsConfig,
List<double>? reputationConfig,
double? rewardSpreadConfig)
double? rewardSpreadConfig
)
{
return Math.Round(
100 *
effectiveDifficulty *
_mathUtil.Interp1(pmcLevel, levelsConfig, reputationConfig) *
_randomUtil.GetDouble((double) (1 - rewardSpreadConfig), (double) (1 + rewardSpreadConfig)) ??
0
) /
100;
100
* effectiveDifficulty
* _mathUtil.Interp1(pmcLevel, levelsConfig, reputationConfig)
* _randomUtil.GetDouble(
(double)(1 - rewardSpreadConfig),
(double)(1 + rewardSpreadConfig)
)
?? 0
) / 100;
}
protected int GetRewardNumItems(int pmcLevel, List<double>? levelsConfig, List<double>? itemsConfig)
protected int GetRewardNumItems(
int pmcLevel,
List<double>? levelsConfig,
List<double>? itemsConfig
)
{
return _randomUtil.RandInt(1, (int) Math.Round(_mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig) ?? 0) + 1);
return _randomUtil.RandInt(
1,
(int)Math.Round(_mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig) ?? 0) + 1
);
}
protected double GetRewardRoubles(double? effectiveDifficulty, int pmcLevel, List<double>? levelsConfig,
protected double GetRewardRoubles(
double? effectiveDifficulty,
int pmcLevel,
List<double>? levelsConfig,
List<double>? roublesConfig,
double? rewardSpreadConfig)
double? rewardSpreadConfig
)
{
return Math.Floor(
effectiveDifficulty *
_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) *
_randomUtil.GetDouble(1d - rewardSpreadConfig.Value, 1d + rewardSpreadConfig.Value) ??
0
effectiveDifficulty
* _mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig)
* _randomUtil.GetDouble(
1d - rewardSpreadConfig.Value,
1d + rewardSpreadConfig.Value
)
?? 0
);
}
@@ -324,8 +396,12 @@ public class RepeatableQuestRewardGenerator(
/// <param name="itemRewardBudget"> Rouble budget all item rewards must fit in </param>
/// <param name="repeatableConfig"> Config for quest type </param>
/// <returns> Dictionary of items and stack size</returns>
protected Dictionary<TemplateItem, int> GetRewardableItemsFromPoolWithinBudget(List<TemplateItem> itemPool,
int maxItemCount, double itemRewardBudget, RepeatableQuestConfig repeatableConfig)
protected Dictionary<TemplateItem, int> GetRewardableItemsFromPoolWithinBudget(
List<TemplateItem> itemPool,
int maxItemCount,
double itemRewardBudget,
RepeatableQuestConfig repeatableConfig
)
{
var itemsToReturn = new Dictionary<TemplateItem, int>();
var exhausableItemPool = new ExhaustableArray<TemplateItem>(itemPool, _randomUtil, _cloner);
@@ -346,7 +422,10 @@ 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;
@@ -373,7 +452,9 @@ 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
@@ -390,7 +471,9 @@ 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"
);
}
}
}
@@ -410,8 +493,11 @@ 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;
@@ -425,23 +511,22 @@ public class RepeatableQuestRewardGenerator(
var stackMaxCount = Math.Min(itemSelected.Properties.StackMaxSize.Value, 100);
// Ensure stack size is at least 1 + is no larger than the max possible stack size
return (int) Math.Max(1, Math.Min(stackSizeThatFitsBudget, stackMaxCount));
return (int)Math.Max(1, Math.Min(stackSizeThatFitsBudget, 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(
_presetHelper.GetDefaultPresetOrItemPrice(item.Id) < maxRoublePriceToStack
&& !_itemHelper.IsOfBaseclasses(
item.Id,
[
BaseClasses.WEAPON,
BaseClasses.ARMORED_EQUIPMENT,
BaseClasses.AMMO
]
) &&
!_itemHelper.ItemRequiresSoftInserts(item.Id);
[BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO]
)
&& !_itemHelper.ItemRequiresSoftInserts(item.Id);
return isEligibleForStackSizeIncrease && _randomUtil.GetChance100(randomChanceToPass);
}
@@ -460,7 +545,7 @@ public class RepeatableQuestRewardGenerator(
{
new(3000, [2, 3, 4]),
new(10000, [2, 3]),
new(int.MaxValue, [2, 3, 4]) // Default for prices 10001+ RUB
new(int.MaxValue, [2, 3, 4]), // Default for prices 10001+ RUB
};
// Find the appropriate price tier and return a random stack size from its options
@@ -480,8 +565,11 @@ public class RepeatableQuestRewardGenerator(
/// <param name="roublesBudget"> Total value of items to return </param>
/// <param name="traderId"> ID of the trader who will give player reward </param>
/// <returns> List of reward items that fit budget </returns>
protected List<TemplateItem> ChooseRewardItemsWithinBudget(RepeatableQuestConfig repeatableConfig,
double? roublesBudget, string traderId)
protected List<TemplateItem> ChooseRewardItemsWithinBudget(
RepeatableQuestConfig repeatableConfig,
double? roublesBudget,
string traderId
)
{
// First filter for type and baseclass to avoid lookup in handbook for non-available items
var rewardableItemPool = GetRewardableItems(repeatableConfig, traderId);
@@ -498,11 +586,7 @@ public class RepeatableQuestRewardGenerator(
_logger.Warning(
_localisationService.GetText(
"repeatable-no_reward_item_found_in_price_range",
new
{
minPrice,
roublesBudget
}
new { minPrice, roublesBudget }
)
);
@@ -522,15 +606,18 @@ 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 =>
{
var itemPrice = _presetHelper.GetDefaultPresetOrItemPrice(item.Id);
return itemPrice < roublesBudget && itemPrice > minPrice;
}
)
return rewardItems
.Where(item =>
{
var itemPrice = _presetHelper.GetDefaultPresetOrItemPrice(item.Id);
return itemPrice < roublesBudget && itemPrice > minPrice;
})
.ToList();
}
@@ -540,7 +627,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>(
@@ -568,7 +658,12 @@ public class RepeatableQuestRewardGenerator(
var chosenPreset = _cloner.Clone(randomPreset);
return new KeyValuePair<Reward, double>(
GeneratePresetReward(chosenPreset.Encyclopedia, 1, rewardIndex, chosenPreset.Items),
GeneratePresetReward(
chosenPreset.Encyclopedia,
1,
rewardIndex,
chosenPreset.Items
),
presetPrice
);
}
@@ -586,7 +681,13 @@ 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(string tpl, int count, int index, List<Item>? preset, bool foundInRaid = true)
protected Reward GeneratePresetReward(
string tpl,
int count,
int index,
List<Item>? preset,
bool foundInRaid = true
)
{
var id = _hashUtil.Generate();
var questRewardItem = new Reward
@@ -601,7 +702,7 @@ public class RepeatableQuestRewardGenerator(
IsEncoded = false,
FindInRaid = foundInRaid,
Type = RewardType.Item,
Items = []
Items = [],
};
// Get presets root item
@@ -630,7 +731,12 @@ 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(string tpl, double count, int index, bool foundInRaid = true)
protected Reward GenerateItemReward(
string tpl,
double count,
int index,
bool foundInRaid = true
)
{
var id = _hashUtil.Generate();
var questRewardItem = new Reward
@@ -645,18 +751,14 @@ public class RepeatableQuestRewardGenerator(
IsEncoded = false,
FindInRaid = foundInRaid,
Type = RewardType.Item,
Items = []
Items = [],
};
var rootItem = new Item
{
Id = id,
Template = tpl,
Upd = new Upd
{
StackObjectsCount = count,
SpawnedInSession = foundInRaid
}
Upd = new Upd { StackObjectsCount = count, SpawnedInSession = foundInRaid },
};
questRewardItem.Items = [rootItem];
@@ -667,17 +769,20 @@ public class RepeatableQuestRewardGenerator(
{
// Determine currency based on trader
// PK and Fence use Euros, everyone else is Roubles
var currency = traderId is Traders.PEACEKEEPER or Traders.FENCE ? Money.EUROS : Money.ROUBLES;
var currency = traderId is Traders.PEACEKEEPER or Traders.FENCE
? Money.EUROS
: Money.ROUBLES;
// Convert reward amount to Euros if necessary
var rewardAmountToGivePlayer =
currency == Money.EUROS ? _handbookHelper.FromRUB(rewardRoubles, Money.EUROS) : rewardRoubles;
currency == Money.EUROS
? _handbookHelper.FromRUB(rewardRoubles, Money.EUROS)
: rewardRoubles;
// Get chosen currency + amount and return
return GenerateItemReward(currency, rewardAmountToGivePlayer, rewardIndex, false);
}
/// <summary>
/// Picks rewardable items from items.json <br />
/// This means they must: <br />
@@ -688,7 +793,10 @@ public class RepeatableQuestRewardGenerator(
/// <param name="repeatableQuestConfig"> Config </param>
/// <param name="tradderId"> ID of trader who will give reward to player </param>
/// <returns> List of rewardable items [[_tpl, itemTemplate],...] </returns>
public List<TemplateItem> GetRewardableItems(RepeatableQuestConfig repeatableQuestConfig, string traderId)
public List<TemplateItem> GetRewardableItems(
RepeatableQuestConfig repeatableQuestConfig,
string traderId
)
{
// Get an array of seasonal items that should not be shown right now as seasonal event is not active
var seasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems();
@@ -696,31 +804,32 @@ public class RepeatableQuestRewardGenerator(
// 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
// those are not in the game yet (e.g. AGS grenade launcher)
return _databaseService.GetItems()
return _databaseService
.GetItems()
.Values.Where(itemTemplate =>
{
// Base "Item" item has no parent, ignore it
if (itemTemplate.Parent == "")
{
// Base "Item" item has no parent, ignore it
if (itemTemplate.Parent == "")
{
return false;
}
if (seasonalItems.Contains(itemTemplate.Id))
{
return false;
}
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(trader => trader.TraderId == traderId
);
return IsValidRewardItem(
itemTemplate.Id,
repeatableQuestConfig.RewardBlacklist,
repeatableQuestConfig.RewardBaseTypeBlacklist,
traderWhitelist?.RewardBaseWhitelist
);
return false;
}
)
if (seasonalItems.Contains(itemTemplate.Id))
{
return false;
}
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(trader =>
trader.TraderId == traderId
);
return IsValidRewardItem(
itemTemplate.Id,
repeatableQuestConfig.RewardBlacklist,
repeatableQuestConfig.RewardBaseTypeBlacklist,
traderWhitelist?.RewardBaseWhitelist
);
})
.ToList();
}
@@ -733,10 +842,12 @@ public class RepeatableQuestRewardGenerator(
/// <param name="itemTypeBlacklist"> Specific item base types to ignore </param>
/// <param name="itemBaseWhitelist"> Default null, specific trader item base classes</param>
/// <returns> True if item is valid reward </returns>
public bool IsValidRewardItem(string tpl,
public bool IsValidRewardItem(
string tpl,
HashSet<string> itemTplBlacklist,
HashSet<string> itemTypeBlacklist,
List<string>? itemBaseWhitelist = null)
List<string>? itemBaseWhitelist = null
)
{
// Return early if not valid item to give as reward
if (!_itemHelper.IsValidItem(tpl))
@@ -746,10 +857,10 @@ public class RepeatableQuestRewardGenerator(
// Check item is not blacklisted
if (
_itemFilterService.IsItemBlacklisted(tpl) ||
_itemFilterService.IsItemRewardBlacklisted(tpl) ||
itemTplBlacklist.Contains(tpl) ||
_itemFilterService.IsItemBlacklisted(tpl)
|| _itemFilterService.IsItemRewardBlacklisted(tpl)
|| itemTplBlacklist.Contains(tpl)
|| _itemFilterService.IsItemBlacklisted(tpl)
)
{
return false;
@@ -53,7 +53,10 @@ public class ScavCaseRewardGenerator(
// Get items that fit the price criteria as set by the scavCase config
var commonPricedItems = GetFilteredItemsByPrice(_dbItemsCache, rewardItemCounts.Common);
var rarePricedItems = GetFilteredItemsByPrice(_dbItemsCache, rewardItemCounts.Rare);
var superRarePricedItems = GetFilteredItemsByPrice(_dbItemsCache, rewardItemCounts.Superrare);
var superRarePricedItems = GetFilteredItemsByPrice(
_dbItemsCache,
rewardItemCounts.Superrare
);
// Get randomly picked items from each item collection, the count range of which is defined in hideout/scavcase.json
var randomlyPickedCommonRewards = PickRandomRewards(
@@ -65,7 +68,8 @@ public class ScavCaseRewardGenerator(
var randomlyPickedRareRewards = PickRandomRewards(
rarePricedItems,
rewardItemCounts.Rare,
RewardRarity.Rare);
RewardRarity.Rare
);
var randomlyPickedSuperRareRewards = PickRandomRewards(
superRarePricedItems,
@@ -74,9 +78,18 @@ public class ScavCaseRewardGenerator(
);
// Add randomised stack sizes to ammo and money rewards
var commonRewards = RandomiseContainerItemRewards(randomlyPickedCommonRewards, RewardRarity.Common);
var rareRewards = RandomiseContainerItemRewards(randomlyPickedRareRewards, RewardRarity.Rare);
var superRareRewards = RandomiseContainerItemRewards(randomlyPickedSuperRareRewards, RewardRarity.SuperRare);
var commonRewards = RandomiseContainerItemRewards(
randomlyPickedCommonRewards,
RewardRarity.Common
);
var rareRewards = RandomiseContainerItemRewards(
randomlyPickedRareRewards,
RewardRarity.Rare
);
var superRareRewards = RandomiseContainerItemRewards(
randomlyPickedSuperRareRewards,
RewardRarity.SuperRare
);
var result = new List<List<Item>>();
result = result.Concat(commonRewards).ToList();
@@ -97,120 +110,131 @@ public class ScavCaseRewardGenerator(
var inactiveSeasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems();
if (!_dbItemsCache.Any())
{
_dbItemsCache = _databaseService.GetItems()
_dbItemsCache = _databaseService
.GetItems()
.Values.Where(item =>
{
// Base "Item" item has no parent, ignore it
if (item.Parent == "")
{
// Base "Item" item has no parent, ignore it
if (item.Parent == "")
{
return false;
}
if (item.Type == "Node")
{
return false;
}
if (item.Properties.QuestItem ?? false)
{
return false;
}
// Skip item if item id is on blacklist
if (
item.Type != "Item" ||
_scavCaseConfig.RewardItemBlacklist.Contains(item.Id) ||
_itemFilterService.IsItemBlacklisted(item.Id)
)
{
return false;
}
// Globally reward-blacklisted
if (_itemFilterService.IsItemRewardBlacklisted(item.Id))
{
return false;
}
if (!_scavCaseConfig.AllowBossItemsAsRewards && _itemFilterService.IsBossItem(item.Id))
{
return false;
}
// Skip item if parent id is blacklisted
if (_itemHelper.IsOfBaseclasses(item.Id, _scavCaseConfig.RewardItemParentBlacklist))
{
return false;
}
if (inactiveSeasonalItems.Contains(item.Id))
{
return false;
}
return true;
return false;
}
)
if (item.Type == "Node")
{
return false;
}
if (item.Properties.QuestItem ?? false)
{
return false;
}
// Skip item if item id is on blacklist
if (
item.Type != "Item"
|| _scavCaseConfig.RewardItemBlacklist.Contains(item.Id)
|| _itemFilterService.IsItemBlacklisted(item.Id)
)
{
return false;
}
// Globally reward-blacklisted
if (_itemFilterService.IsItemRewardBlacklisted(item.Id))
{
return false;
}
if (
!_scavCaseConfig.AllowBossItemsAsRewards
&& _itemFilterService.IsBossItem(item.Id)
)
{
return false;
}
// Skip item if parent id is blacklisted
if (
_itemHelper.IsOfBaseclasses(
item.Id,
_scavCaseConfig.RewardItemParentBlacklist
)
)
{
return false;
}
if (inactiveSeasonalItems.Contains(item.Id))
{
return false;
}
return true;
})
.ToList();
}
if (!_dbAmmoItemsCache.Any())
{
_dbAmmoItemsCache = _databaseService.GetItems()
_dbAmmoItemsCache = _databaseService
.GetItems()
.Values.Where(item =>
{
// Base "Item" item has no parent, ignore it
if (item.Parent == "")
{
// Base "Item" item has no parent, ignore it
if (item.Parent == "")
{
return false;
}
if (item.Type != "Item")
{
return false;
}
// Not ammo, skip
if (!_itemHelper.IsOfBaseclass(item.Id, BaseClasses.AMMO))
{
return false;
}
// Skip item if item id is on blacklist
if (
_scavCaseConfig.RewardItemBlacklist.Contains(item.Id) ||
_itemFilterService.IsItemBlacklisted(item.Id)
)
{
return false;
}
// Globally reward-blacklisted
if (_itemFilterService.IsItemRewardBlacklisted(item.Id))
{
return false;
}
if (!_scavCaseConfig.AllowBossItemsAsRewards && _itemFilterService.IsBossItem(item.Id))
{
return false;
}
// Skip seasonal items
if (inactiveSeasonalItems.Contains(item.Id))
{
return false;
}
// Skip ammo that doesn't stack as high as value in config
if (item.Properties.StackMaxSize < _scavCaseConfig.AmmoRewards.MinStackSize)
{
return false;
}
return true;
return false;
}
)
if (item.Type != "Item")
{
return false;
}
// Not ammo, skip
if (!_itemHelper.IsOfBaseclass(item.Id, BaseClasses.AMMO))
{
return false;
}
// Skip item if item id is on blacklist
if (
_scavCaseConfig.RewardItemBlacklist.Contains(item.Id)
|| _itemFilterService.IsItemBlacklisted(item.Id)
)
{
return false;
}
// Globally reward-blacklisted
if (_itemFilterService.IsItemRewardBlacklisted(item.Id))
{
return false;
}
if (
!_scavCaseConfig.AllowBossItemsAsRewards
&& _itemFilterService.IsBossItem(item.Id)
)
{
return false;
}
// Skip seasonal items
if (inactiveSeasonalItems.Contains(item.Id))
{
return false;
}
// Skip ammo that doesn't stack as high as value in config
if (item.Properties.StackMaxSize < _scavCaseConfig.AmmoRewards.MinStackSize)
{
return false;
}
return true;
})
.ToList();
}
}
@@ -224,13 +248,14 @@ public class ScavCaseRewardGenerator(
protected List<TemplateItem> PickRandomRewards(
List<TemplateItem> items,
RewardCountAndPriceDetails itemFilters,
string rarity)
string rarity
)
{
List<TemplateItem> result = [];
var rewardWasMoney = false;
var rewardWasAmmo = false;
var randomCount = _randomUtil.GetInt((int) itemFilters.MinCount, (int) itemFilters.MaxCount);
var randomCount = _randomUtil.GetInt((int)itemFilters.MinCount, (int)itemFilters.MaxCount);
for (var i = 0; i < randomCount; i++)
{
if (RewardShouldBeMoney() && !rewardWasMoney)
@@ -301,26 +326,30 @@ public class ScavCaseRewardGenerator(
protected TemplateItem GetRandomAmmo(string rarity)
{
var possibleAmmoPool = _dbAmmoItemsCache.Where(ammo =>
{
// Is ammo handbook price between desired range
var handbookPrice = _ragfairPriceService.GetStaticPriceForItem(ammo.Id);
if (_scavCaseConfig.AmmoRewards.AmmoRewardValueRangeRub.TryGetValue(rarity,
out var matchingAmmoRewardForRarity) &&
handbookPrice >= matchingAmmoRewardForRarity.Min &&
handbookPrice <= matchingAmmoRewardForRarity.Max
{
// Is ammo handbook price between desired range
var handbookPrice = _ragfairPriceService.GetStaticPriceForItem(ammo.Id);
if (
_scavCaseConfig.AmmoRewards.AmmoRewardValueRangeRub.TryGetValue(
rarity,
out var matchingAmmoRewardForRarity
)
{
return true;
}
return false;
&& handbookPrice >= matchingAmmoRewardForRarity.Min
&& handbookPrice <= matchingAmmoRewardForRarity.Max
)
{
return true;
}
);
return false;
});
if (!possibleAmmoPool.Any())
{
// Filtered pool is empty
_logger.Warning(localisationService.GetText("scavcase-no_cartridges_found_matching_price"));
_logger.Warning(
localisationService.GetText("scavcase-no_cartridges_found_matching_price")
);
}
// Get a random ammo and return it
@@ -334,7 +363,10 @@ public class ScavCaseRewardGenerator(
/// <param name="rewardItems">items to convert</param>
/// <param name="rarity">The rarity desired ammo reward is for</param>
/// <returns>Product array</returns>
protected List<List<Item>> RandomiseContainerItemRewards(List<TemplateItem> rewardItems, string rarity)
protected List<List<Item>> RandomiseContainerItemRewards(
List<TemplateItem> rewardItems,
string rarity
)
{
// Each array is an item + children
List<List<Item>> result = [];
@@ -346,8 +378,8 @@ public class ScavCaseRewardGenerator(
{
Id = _hashUtil.Generate(),
Template = rewardItemDb.Id,
Upd = null
}
Upd = null,
},
];
var rootItem = resultItem.FirstOrDefault();
@@ -357,14 +389,16 @@ public class ScavCaseRewardGenerator(
}
// Armor or weapon = use default preset from globals.json
else if (
_itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(rewardItemDb.Id) ||
_itemHelper.IsOfBaseclass(rewardItemDb.Id, BaseClasses.WEAPON)
_itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(rewardItemDb.Id)
|| _itemHelper.IsOfBaseclass(rewardItemDb.Id, BaseClasses.WEAPON)
)
{
var preset = _presetHelper.GetDefaultPreset(rewardItemDb.Id);
if (preset is null)
{
_logger.Warning($"No preset for item: {rewardItemDb.Id} {rewardItemDb.Name}, skipping");
_logger.Warning(
$"No preset for item: {rewardItemDb.Id} {rewardItemDb.Name}, skipping"
);
continue;
}
@@ -375,11 +409,13 @@ public class ScavCaseRewardGenerator(
resultItem = presetAndMods;
}
else if (_itemHelper.IsOfBaseclasses(rewardItemDb.Id, [BaseClasses.AMMO, BaseClasses.MONEY]))
else if (
_itemHelper.IsOfBaseclasses(rewardItemDb.Id, [BaseClasses.AMMO, BaseClasses.MONEY])
)
{
rootItem.Upd = new Upd
{
StackObjectsCount = GetRandomAmountRewardForScavCase(rewardItemDb, rarity)
StackObjectsCount = GetRandomAmountRewardForScavCase(rewardItemDb, rarity),
};
}
@@ -396,19 +432,23 @@ public class ScavCaseRewardGenerator(
/// <returns>filtered dbItems array</returns>
protected List<TemplateItem> GetFilteredItemsByPrice(
List<TemplateItem> dbItems,
RewardCountAndPriceDetails itemFilters)
RewardCountAndPriceDetails itemFilters
)
{
return dbItems.Where(item =>
return dbItems
.Where(item =>
{
var handbookPrice = _ragfairPriceService.GetStaticPriceForItem(item.Id);
if (
handbookPrice >= itemFilters.MinPriceRub
&& handbookPrice <= itemFilters.MaxPriceRub
)
{
var handbookPrice = _ragfairPriceService.GetStaticPriceForItem(item.Id);
if (handbookPrice >= itemFilters.MinPriceRub && handbookPrice <= itemFilters.MaxPriceRub)
{
return true;
}
return false;
return true;
}
)
return false;
})
.ToList();
}
@@ -417,7 +457,9 @@ public class ScavCaseRewardGenerator(
/// </summary>
/// <param name="scavCaseDetails">production.json/scavRecipes object</param>
/// <returns>ScavCaseRewardCountsAndPrices object</returns>
protected ScavCaseRewardCountsAndPrices GetScavCaseRewardCountsAndPrices(ScavRecipe scavCaseDetails)
protected ScavCaseRewardCountsAndPrices GetScavCaseRewardCountsAndPrices(
ScavRecipe scavCaseDetails
)
{
return new ScavCaseRewardCountsAndPrices
{
@@ -427,22 +469,22 @@ public class ScavCaseRewardGenerator(
MinCount = scavCaseDetails.EndProducts.Common.Min,
MaxCount = scavCaseDetails.EndProducts.Common.Max,
MinPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.Common].Min,
MaxPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.Common].Max
MaxPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.Common].Max,
},
Rare = new RewardCountAndPriceDetails
{
MinCount = scavCaseDetails.EndProducts.Rare.Min,
MaxCount = scavCaseDetails.EndProducts.Rare.Max,
MinPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.Rare].Min,
MaxPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.Rare].Max
MaxPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.Rare].Max,
},
Superrare = new RewardCountAndPriceDetails
{
MinCount = scavCaseDetails.EndProducts.Superrare.Min,
MaxCount = scavCaseDetails.EndProducts.Superrare.Max,
MinPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.SuperRare].Min,
MaxPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.SuperRare].Max
}
MaxPriceRub = _scavCaseConfig.RewardItemValueRangeRub[RewardRarity.SuperRare].Max,
},
};
}
@@ -458,7 +500,7 @@ public class ScavCaseRewardGenerator(
{
BaseClasses.AMMO => GetRandomisedAmmoRewardStackSize(itemToCalculate),
BaseClasses.MONEY => GetRandomisedMoneyRewardStackSize(itemToCalculate, rarity),
_ => 1
_ => 1,
};
}
@@ -501,7 +543,7 @@ public class ScavCaseRewardGenerator(
_scavCaseConfig.MoneyRewards.GpCount.GetByJsonProp<MinMax<int>>(rarity).Min,
_scavCaseConfig.MoneyRewards.GpCount.GetByJsonProp<MinMax<int>>(rarity).Max
),
_ => 1
_ => 1,
};
}
}
@@ -26,7 +26,7 @@ public class BarrelInvetoryMagGen(
// Can't be done by _props.ammoType as grenade launcher shoots grenades with ammoType of "buckshot"
double? randomisedAmmoStackSize;
if (inventoryMagGen.GetAmmoTemplate().Properties.StackMaxRandom == 1)
// Doesn't stack
// Doesn't stack
{
randomisedAmmoStackSize = _randomUtil.GetInt(3, 6);
}
@@ -40,7 +40,7 @@ public class BarrelInvetoryMagGen(
_botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
inventoryMagGen.GetAmmoTemplate().Id,
(int) randomisedAmmoStackSize,
(int)randomisedAmmoStackSize,
inventoryMagGen.GetPmcInventory(),
null
);

Some files were not shown because too many files have changed in this diff Show More