Format Style Fixes

This commit is contained in:
sp-tarkov-bot
2025-07-28 19:39:29 +00:00
parent 51430d55c9
commit b14b74bf24
357 changed files with 7345 additions and 24604 deletions
@@ -9,11 +9,7 @@ using SPTarkov.Server.Core.Services;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class AchievementController(
ProfileHelper profileHelper,
DatabaseService databaseService,
ConfigServer configServer
)
public class AchievementController(ProfileHelper profileHelper, DatabaseService databaseService, ConfigServer configServer)
{
protected readonly CoreConfig coreConfig = configServer.GetConfig<CoreConfig>();
@@ -37,11 +33,7 @@ public class AchievementController(
var stats = new Dictionary<string, int>();
var profiles = profileHelper
.GetProfiles()
.Where(kvp =>
!coreConfig.Features.AchievementProfileIdBlacklist.Contains(
kvp.Value.ProfileInfo.ProfileId
)
)
.Where(kvp => !coreConfig.Features.AchievementProfileIdBlacklist.Contains(kvp.Value.ProfileInfo.ProfileId))
.ToDictionary();
var achievements = databaseService.GetAchievements();
@@ -70,8 +62,7 @@ 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);
@@ -51,9 +51,7 @@ public class BotController(
{
if (!_botConfig.PresetBatch.TryGetValue(type, out var limit))
{
_logger.Warning(
_serverLocalisationService.GetText("bot-bot_preset_count_value_missing", type)
);
_logger.Warning(_serverLocalisationService.GetText("bot-bot_preset_count_value_missing", type));
return 10;
}
@@ -80,38 +78,23 @@ public class BotController(
/// <param name="diffLevel">difficulty level server requested settings for</param>
/// <param name="ignoreRaidSettings">OPTIONAL - should raid settings chosen pre-raid be ignored</param>
/// <returns>Difficulty object</returns>
public DifficultyCategories GetBotDifficulty(
MongoId sessionId,
string type,
string diffLevel,
bool ignoreRaidSettings = false
)
public DifficultyCategories GetBotDifficulty(MongoId sessionId, string type, string diffLevel, bool ignoreRaidSettings = false)
{
var difficulty = diffLevel.ToLowerInvariant();
var raidConfig = _profileActivityService
.GetProfileActivityRaidData(sessionId)
.RaidConfiguration;
var raidConfig = _profileActivityService.GetProfileActivityRaidData(sessionId).RaidConfiguration;
if (!(raidConfig != null || ignoreRaidSettings))
{
_logger.Error(
_serverLocalisationService.GetText(
"bot-missing_application_context",
"RAID_CONFIGURATION"
)
);
_logger.Error(_serverLocalisationService.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().ToLowerInvariant() ?? "asonline";
var botDifficultyDropDownValue = raidConfig?.WavesSettings?.BotDifficulty?.ToString().ToLowerInvariant() ?? "asonline";
if (botDifficultyDropDownValue != "asonline")
{
difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(
botDifficultyDropDownValue
);
difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
}
var botDb = _databaseService.GetBots();
@@ -147,9 +130,7 @@ 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;
@@ -158,9 +139,7 @@ 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;
}
@@ -174,11 +153,7 @@ public class BotController(
}
// Store all difficulty values in dict keyed by difficulty type e.g. easy/normal/hard/impossible
result[botNameKey]
.TryAdd(
difficultyName,
GetBotDifficulty(string.Empty, botNameKey, difficultyName, true)
);
result[botNameKey].TryAdd(difficultyName, GetBotDifficulty(string.Empty, botNameKey, difficultyName, true));
}
}
@@ -205,17 +180,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,
MongoId sessionId
)
protected List<BotBase> GenerateBotWaves(GenerateBotsRequestData request, PmcData? pmcProfile, MongoId sessionId)
{
var generatedBotList = new List<BotBase>();
var raidSettings = GetMostRecentRaidSettings(sessionId);
var allPmcsHaveSameNameAsPlayer = _randomUtil.GetChance100(
_pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance
);
var allPmcsHaveSameNameAsPlayer = _randomUtil.GetChance100(_pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance);
var stopwatch = Stopwatch.StartNew();
// Map conditions to promises for bot generation
@@ -232,12 +201,7 @@ public class BotController(
raidSettings
);
GenerateBotWave(
condition,
botWaveGenerationDetails,
generatedBotList,
sessionId
);
GenerateBotWave(condition, botWaveGenerationDetails, generatedBotList, sessionId);
})
)
.ToArray()
@@ -247,9 +211,7 @@ public class BotController(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
$"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache()"
);
_logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache()");
}
return generatedBotList;
@@ -270,17 +232,12 @@ public class BotController(
MongoId 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
botGenerationDetails.EventRole = generateRequest.Role;
botGenerationDetails.Role = _seasonalEventService.GetBaseRoleForEventBot(
botGenerationDetails.EventRole
);
botGenerationDetails.Role = _seasonalEventService.GetBaseRoleForEventBot(botGenerationDetails.EventRole);
}
var role = botGenerationDetails.EventRole ?? botGenerationDetails.Role;
@@ -308,16 +265,11 @@ public class BotController(
try
{
bot = _botGenerator.PrepareAndGenerateBot(
sessionId,
_cloner.Clone(botGenerationDetails)
);
bot = _botGenerator.PrepareAndGenerateBot(sessionId, _cloner.Clone(botGenerationDetails));
}
catch (Exception e)
{
_logger.Error(
$"Failed to generate bot: {botGenerationDetails.Role} #{i + 1}: {e.Message} {e.StackTrace}"
);
_logger.Error($"Failed to generate bot: {botGenerationDetails.Role} #{i + 1}: {e.Message} {e.StackTrace}");
return;
}
@@ -353,17 +305,11 @@ public class BotController(
/// <returns>GetRaidConfigurationRequestData if it exists</returns>
protected GetRaidConfigurationRequestData? GetMostRecentRaidSettings(MongoId sessionId)
{
var raidConfiguration = _profileActivityService
.GetProfileActivityRaidData(sessionId)
?.RaidConfiguration;
var raidConfiguration = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
if (raidConfiguration is null)
{
_logger.Warning(
_serverLocalisationService.GetText(
"bot-unable_to_load_raid_settings_from_appcontext"
)
);
_logger.Warning(_serverLocalisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
}
return raidConfiguration;
@@ -376,10 +322,7 @@ public class BotController(
/// <returns>MinMax values</returns>
protected MinMax<int> GetPmcLevelRangeForMap(string? location)
{
return _pmcConfig.LocationSpecificPmcLevelOverride!.GetValueOrDefault(
location?.ToLowerInvariant() ?? "",
null
);
return _pmcConfig.LocationSpecificPmcLevelOverride!.GetValueOrDefault(location?.ToLowerInvariant() ?? "", null);
}
/// <summary>
@@ -402,18 +345,13 @@ public class BotController(
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,
@@ -437,12 +375,7 @@ public class BotController(
if (location == "default")
{
_logger.Warning(
_serverLocalisationService.GetText(
"bot-no_bot_cap_found_for_location",
location.ToLowerInvariant()
)
);
_logger.Warning(_serverLocalisationService.GetText("bot-no_bot_cap_found_for_location", location.ToLowerInvariant()));
}
return maxCap;
@@ -41,32 +41,24 @@ public class BuildController(
};
// Ensure the secure container in the default presets match what the player has equipped
var defaultEquipmentPresetsClone = cloner
.Clone(databaseService.GetTemplates().DefaultEquipmentPresets)
.ToList();
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);
if (
playerSecureContainer is not null
&& playerSecureContainer.Template != firstDefaultItemsSecureContainer?.Template
)
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;
@@ -133,9 +125,7 @@ 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.
@@ -150,9 +140,7 @@ public class BuildController(
Items = request.Items.ToList(),
};
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)
{
// Already exists, replace
@@ -198,9 +186,7 @@ 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);
@@ -224,9 +210,7 @@ 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);
@@ -235,9 +219,7 @@ 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);
@@ -246,9 +228,7 @@ 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);
@@ -257,8 +237,6 @@ public class BuildController(
}
// Not found in weapons,equipment or magazines, not good
logger.Error(
serverLocalisationService.GetText("build-unable_to_delete_preset", idToRemove)
);
logger.Error(serverLocalisationService.GetText("build-unable_to_delete_preset", idToRemove));
}
}
@@ -42,18 +42,12 @@ public class CustomizationController(
var matchingSuits = suits?.Where(s => clothing.ContainsKey(s.SuiteId));
matchingSuits = matchingSuits?.Where(s =>
clothing[s.SuiteId]?.Properties?.Side?.Contains(pmcData?.Info?.Side ?? string.Empty)
?? false
clothing[s.SuiteId]?.Properties?.Side?.Contains(pmcData?.Info?.Side ?? string.Empty) ?? false
);
if (matchingSuits == null)
{
throw new Exception(
serverLocalisationService.GetText(
"customisation-unable_to_get_trader_suits",
traderId
)
);
throw new Exception(serverLocalisationService.GetText("customisation-unable_to_get_trader_suits", traderId));
}
return matchingSuits.ToList();
@@ -67,23 +61,14 @@ public class CustomizationController(
/// <param name="buyClothingRequest">Request object</param>
/// <param name="sessionId">Session id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse BuyCustomisation(
PmcData pmcData,
BuyClothingRequestData buyClothingRequest,
MongoId sessionId
)
public ItemEventRouterResponse BuyCustomisation(PmcData pmcData, BuyClothingRequestData buyClothingRequest, MongoId sessionId)
{
var output = eventOutputHolder.GetOutput(sessionId);
var traderOffer = GetTraderClothingOffer(sessionId, buyClothingRequest.Offer);
if (traderOffer is null)
{
logger.Error(
serverLocalisationService.GetText(
"customisation-unable_to_find_suit_by_id",
buyClothingRequest.Offer
)
);
logger.Error(serverLocalisationService.GetText("customisation-unable_to_find_suit_by_id", buyClothingRequest.Offer));
return output;
}
@@ -130,9 +115,7 @@ 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>
@@ -146,12 +129,7 @@ public class CustomizationController(
var foundSuit = GetAllTraderSuits(sessionId).FirstOrDefault(s => s.Id == offerId);
if (foundSuit is null)
{
logger.Error(
serverLocalisationService.GetText(
"customisation-unable_to_find_suit_with_id",
offerId
)
);
logger.Error(serverLocalisationService.GetText("customisation-unable_to_find_suit_with_id", offerId));
}
return foundSuit;
@@ -180,14 +158,7 @@ public class CustomizationController(
{
var options = new ProcessBuyTradeRequestData
{
SchemeItems =
[
new IdWithCount
{
Count = inventoryItemToProcess.Count.Value,
Id = inventoryItemToProcess.Id,
},
],
SchemeItems = [new IdWithCount { Count = inventoryItemToProcess.Count.Value, Id = inventoryItemToProcess.Id }],
TransactionId = Traders.RAGMAN,
Action = "BuyCustomization",
Type = "",
@@ -212,10 +183,7 @@ public class CustomizationController(
foreach (var (traderId, trader) in traders)
{
if (
trader.Base?.CustomizationSeller is not null
&& trader.Base.CustomizationSeller.Value
)
if (trader.Base?.CustomizationSeller is not null && trader.Base.CustomizationSeller.Value)
{
result.AddRange(GetTraderSuits(traderId, sessionId));
}
@@ -241,9 +209,7 @@ public class CustomizationController(
/// <returns></returns>
public List<CustomisationStorage> GetCustomisationStorage(MongoId sessionId)
{
var customisationResultsClone = cloner.Clone(
databaseService.GetTemplates().CustomisationStorage
);
var customisationResultsClone = cloner.Clone(databaseService.GetTemplates().CustomisationStorage);
var profile = profileHelper.GetFullProfile(sessionId);
if (profile is null)
@@ -263,11 +229,7 @@ public class CustomizationController(
/// <param name="request"></param>
/// <param name="pmcData">Players PMC profile</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse SetCustomisation(
MongoId sessionId,
CustomizationSetRequest request,
PmcData pmcData
)
public ItemEventRouterResponse SetCustomisation(MongoId sessionId, CustomizationSetRequest request, PmcData pmcData)
{
foreach (var customisation in request.Customizations)
{
@@ -38,12 +38,7 @@ public class DialogueController(
{
if (_dialogueChatBots.Any(cb => cb.GetChatBot().Id == chatBot.GetChatBot().Id))
{
logger.Error(
serverLocalisationService.GetText(
"dialog-chatbot_id_already_exists",
chatBot.GetChatBot().Id
)
);
logger.Error(serverLocalisationService.GetText("dialog-chatbot_id_already_exists", chatBot.GetChatBot().Id));
}
_dialogueChatBots.Add(chatBot);
@@ -190,11 +185,7 @@ public class DialogueController(
/// <param name="messageType">What type of message is being sent</param>
/// <param name="sessionId">Player id</param>
/// <returns>UserDialogInfo list</returns>
public virtual List<UserDialogInfo> GetDialogueUsers(
Dialogue? dialog,
MessageType? messageType,
MongoId sessionId
)
public virtual List<UserDialogInfo> GetDialogueUsers(Dialogue? dialog, MessageType? messageType, MongoId sessionId)
{
var profile = saveServer.GetProfile(sessionId);
@@ -202,9 +193,7 @@ public class DialogueController(
if (
messageType == MessageType.UserMessage
&& dialog?.Users is not null
&& dialog.Users.All(userDialog =>
userDialog.Id != profile.CharacterData?.PmcData?.SessionId
)
&& dialog.Users.All(userDialog => userDialog.Id != profile.CharacterData?.PmcData?.SessionId)
)
{
dialog.Users.Add(
@@ -218,11 +207,7 @@ 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,
},
}
);
@@ -240,10 +225,7 @@ public class DialogueController(
/// <param name="request">Get dialog request</param>
/// <param name="sessionId">Session id</param>
/// <returns>GetMailDialogViewResponseData object</returns>
public virtual GetMailDialogViewResponseData GenerateDialogueView(
GetMailDialogViewRequestData request,
MongoId sessionId
)
public virtual GetMailDialogViewResponseData GenerateDialogueView(GetMailDialogViewRequestData request, MongoId sessionId)
{
var dialogueId = request.DialogId;
var fullProfile = saveServer.GetProfile(sessionId);
@@ -279,18 +261,11 @@ public class DialogueController(
/// <param name="profile">Player profile</param>
/// <param name="request">get dialog request</param>
/// <returns>Dialogue</returns>
protected Dialogue GetDialogByIdFromProfile(
SptProfile profile,
GetMailDialogViewRequestData request
)
protected Dialogue GetDialogByIdFromProfile(SptProfile profile, 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
@@ -310,9 +285,7 @@ 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)
{
@@ -331,10 +304,7 @@ 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)
@@ -383,10 +353,7 @@ public class DialogueController(
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++;
}
@@ -436,12 +403,7 @@ public class DialogueController(
var profile = saveServer.GetProfile(sessionId);
if (!profile.DialogueRecords.Remove(dialogueId))
{
logger.Error(
serverLocalisationService.GetText(
"dialogue-unable_to_find_in_profile",
new { sessionId, dialogueId }
)
);
logger.Error(serverLocalisationService.GetText("dialogue-unable_to_find_in_profile", new { sessionId, dialogueId }));
}
}
@@ -456,12 +418,7 @@ public class DialogueController(
var dialog = dialogueHelper.GetDialogsForProfile(sessionId).GetValueOrDefault(dialogueId);
if (dialog is null)
{
logger.Error(
serverLocalisationService.GetText(
"dialogue-unable_to_find_in_profile",
new { sessionId, dialogueId }
)
);
logger.Error(serverLocalisationService.GetText("dialogue-unable_to_find_in_profile", new { sessionId, dialogueId }));
return;
}
@@ -480,12 +437,7 @@ public class DialogueController(
var dialogs = dialogueHelper.GetDialogsForProfile(sessionId);
if (dialogs.Any() != true)
{
logger.Error(
serverLocalisationService.GetText(
"dialogue-unable_to_find_dialogs_in_profile",
new { sessionId }
)
);
logger.Error(serverLocalisationService.GetText("dialogue-unable_to_find_dialogs_in_profile", new { sessionId }));
return;
}
@@ -504,10 +456,7 @@ public class DialogueController(
/// <param name="dialogueId">Dialog to get mail attachments from</param>
/// <param name="sessionId">Session id</param>
/// <returns>GetAllAttachmentsResponse or null if dialogue doesn't exist</returns>
public virtual GetAllAttachmentsResponse? GetAllAttachments(
string dialogueId,
MongoId sessionId
)
public virtual GetAllAttachmentsResponse? GetAllAttachments(string dialogueId, MongoId sessionId)
{
var dialogs = dialogueHelper.GetDialogsForProfile(sessionId);
var dialog = dialogs.TryGetValue(dialogueId, out var dialogInfo);
@@ -538,16 +487,11 @@ public class DialogueController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="request"></param>
/// <returns></returns>
public virtual async ValueTask<string> SendMessage(
MongoId sessionId,
SendMessageRequest request
)
public virtual async ValueTask<string> SendMessage(MongoId sessionId, SendMessageRequest request)
{
mailSendService.SendPlayerMessageToNpc(sessionId, request.DialogId, request.Text);
var chatBot = _dialogueChatBots.FirstOrDefault(cb =>
cb.GetChatBot().Id == request.DialogId
);
var chatBot = _dialogueChatBots.FirstOrDefault(cb => cb.GetChatBot().Id == request.DialogId);
if (chatBot is not null)
{
@@ -619,10 +563,7 @@ public class DialogueController(
/// <param name="sessionID">Session/player id</param>
/// <param name="request">Sent friend request</param>
/// <returns></returns>
public virtual FriendRequestSendResponse SendFriendRequest(
MongoId sessionID,
FriendRequestData request
)
public virtual FriendRequestSendResponse SendFriendRequest(MongoId sessionID, FriendRequestData request)
{
// To avoid needing to jump between profiles, auto-accept all friend requests
var friendProfile = profileHelper.GetFullProfile(request.To.Value);
@@ -647,9 +588,7 @@ 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);
},
@@ -687,9 +626,7 @@ 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;
}
@@ -77,14 +77,8 @@ public class GameController(
return;
}
fullProfile.CharacterData!.PmcData!.WishList ??= new DictionaryOrList<MongoId, int>(
new Dictionary<MongoId, int>(),
[]
);
fullProfile.CharacterData.ScavData!.WishList ??= new DictionaryOrList<MongoId, int>(
new Dictionary<MongoId, int>(),
[]
);
fullProfile.CharacterData!.PmcData!.WishList ??= new DictionaryOrList<MongoId, int>(new Dictionary<MongoId, int>(), []);
fullProfile.CharacterData.ScavData!.WishList ??= new DictionaryOrList<MongoId, int>(new Dictionary<MongoId, int>(), []);
if (fullProfile.DialogueRecords is not null)
{
@@ -93,9 +87,7 @@ 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;
@@ -122,20 +114,12 @@ public class GameController(
if (pmcProfile.Hideout is not null)
{
profileFixerService.AddMissingHideoutBonusesToProfile(
pmcProfile,
databaseService.GetHideout().Areas
);
profileFixerService.AddMissingHideoutBonusesToProfile(pmcProfile, databaseService.GetHideout().Areas);
hideoutHelper.SetHideoutImprovementsToCompleted(pmcProfile);
pmcProfile.UnlockHideoutWallInProfile();
// 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);
}
@@ -170,11 +154,8 @@ public class GameController(
{
var profile = profileHelper.GetPmcProfile(sessionId);
var gameTime =
profile
?.Stats?.Eft?.OverallCounters?.Items?.FirstOrDefault(c =>
c.Key!.Contains("LifeTime") && c.Key.Contains("Pmc")
)
?.Value ?? 0D;
profile?.Stats?.Eft?.OverallCounters?.Items?.FirstOrDefault(c => c.Key!.Contains("LifeTime") && c.Key.Contains("Pmc"))?.Value
?? 0D;
var config = new GameConfigResponse
{
@@ -213,11 +194,7 @@ public class GameController(
/// <returns></returns>
public GameModeResponse GetGameMode(MongoId sessionId, GameModeRequestData requestData)
{
return new GameModeResponse
{
GameMode = "pve",
BackendUrl = httpServerHelper.GetBackendUrl(),
};
return new GameModeResponse { GameMode = "pve", BackendUrl = httpServerHelper.GetBackendUrl() };
}
/// <summary>
@@ -247,11 +224,7 @@ public class GameController(
/// <returns></returns>
public CheckVersionResponse GetValidGameVersion(MongoId sessionId)
{
return new CheckVersionResponse
{
IsValid = true,
LatestVersion = _coreConfig.CompatibleTarkovVersion,
};
return new CheckVersionResponse { IsValid = true, LatestVersion = _coreConfig.CompatibleTarkovVersion };
}
/// <summary>
@@ -294,9 +267,7 @@ public class GameController(
var botReloadSkill = pmcProfile.GetSkillFromProfile(SkillTypes.BotReload);
if (botReloadSkill?.Progress > 0)
{
logger.Warning(
serverLocalisationService.GetText("server_start_player_active_botreload_skill")
);
logger.Warning(serverLocalisationService.GetText("server_start_player_active_botreload_skill"));
}
}
@@ -338,9 +309,7 @@ public class GameController(
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;
@@ -348,14 +317,9 @@ 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;
@@ -375,15 +339,9 @@ 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)
@@ -456,11 +414,7 @@ public class GameController(
/// <param name="pmcProfile"></param>
protected void SendMechanicGiftsToNewProfile(PmcData pmcProfile)
{
giftService.SendGiftWithSilentReceivedCheck(
"MechanicGiftDay1",
pmcProfile.SessionId.Value,
1
);
giftService.SendGiftWithSilentReceivedCheck("MechanicGiftDay1", pmcProfile.SessionId.Value, 1);
}
/// <summary>
@@ -475,9 +429,7 @@ 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
)
)
{
@@ -554,9 +506,7 @@ 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()}");
}
@@ -34,24 +34,15 @@ public class HealthController(
/// <param name="request">Healing request</param>
/// <param name="sessionID">Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse OffRaidHeal(
PmcData pmcData,
OffraidHealRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse OffRaidHeal(PmcData pmcData, OffraidHealRequestData request, MongoId 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 = serverLocalisationService.GetText(
"health-healing_item_not_found",
request.Item
);
var errorMessage = serverLocalisationService.GetText("health-healing_item_not_found", request.Item);
logger.Error(errorMessage);
return httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -67,9 +58,7 @@ public class HealthController(
else
{
// Get max healing from db
var maxHp = itemHelper
.GetItem(healingItemToUse.Template)
.Value.Properties.MaxHpResource;
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)
@@ -87,9 +76,7 @@ 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;
}
@@ -112,12 +99,7 @@ public class HealthController(
}
// Check if healing item removes the effect on limb
if (
!healItemEffectDetails.TryGetValue(
effect,
out var matchingEffectFromHealingItem
)
)
if (!healItemEffectDetails.TryGetValue(effect, out var matchingEffectFromHealingItem))
// Healing item doesn't have matching effect, it doesn't remove the effect
{
continue;
@@ -149,11 +131,7 @@ public class HealthController(
/// <param name="request">Eat request</param>
/// <param name="sessionID">Session id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse OffRaidEat(
PmcData pmcData,
OffraidEatRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse OffRaidEat(PmcData pmcData, OffraidEatRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
var resourceLeft = 0d;
@@ -164,16 +142,11 @@ public class HealthController(
{
return httpResponseUtil.AppendErrorToOutput(
output,
serverLocalisationService.GetText(
"health-unable_to_find_item_to_consume",
request.Item
)
serverLocalisationService.GetText("health-unable_to_find_item_to_consume", request.Item)
);
}
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
@@ -181,10 +154,7 @@ public class HealthController(
if (itemToConsume.Upd.FoodDrink is null)
{
itemToConsume.Upd.FoodDrink = new UpdFoodDrink
{
HpPercent = consumedItemMaxResource - request.Count,
};
itemToConsume.Upd.FoodDrink = new UpdFoodDrink { HpPercent = consumedItemMaxResource - request.Count };
}
else
{
@@ -210,21 +180,14 @@ 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;
}
}
@@ -279,11 +242,7 @@ public class HealthController(
/// <param name="healthTreatmentRequest">Request data from client</param>
/// <param name="sessionID">Session id</param>
/// <returns></returns>
public ItemEventRouterResponse HealthTreatment(
PmcData pmcData,
HealthTreatmentRequestData healthTreatmentRequest,
MongoId sessionID
)
public ItemEventRouterResponse HealthTreatment(PmcData pmcData, HealthTreatmentRequestData healthTreatmentRequest, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
var payMoneyRequest = new ProcessBuyTradeRequestData
@@ -44,9 +44,7 @@ public class HideoutController(
ConfigServer configServer
)
{
public static readonly MongoId NameTaskConditionCountersCraftingId = new(
"673f5d6fdd6ed700c703afdc"
);
public static readonly MongoId NameTaskConditionCountersCraftingId = new("673f5d6fdd6ed700c703afdc");
protected readonly FrozenSet<HideoutAreas> _areasWithResources =
[
@@ -66,19 +64,12 @@ public class HideoutController(
/// <param name="request">Start upgrade request</param>
/// <param name="sessionID">Session/player id</param>
/// <param name="output">Client response</param>
public void StartUpgrade(
PmcData pmcData,
HideoutUpgradeRequestData request,
MongoId sessionID,
ItemEventRouterResponse output
)
public void StartUpgrade(PmcData pmcData, HideoutUpgradeRequestData request, MongoId sessionID, ItemEventRouterResponse output)
{
var items = request
.Items.Select(reqItem =>
{
var item = pmcData.Inventory.Items.FirstOrDefault(invItem =>
invItem.Id == reqItem.Id
);
var item = pmcData.Inventory.Items.FirstOrDefault(invItem => invItem.Id == reqItem.Id);
return new { inventoryItem = item, requestedItem = reqItem };
})
.ToList();
@@ -88,12 +79,7 @@ public class HideoutController(
{
if (item.inventoryItem is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_item_in_inventory",
item.requestedItem.Id
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_item_in_inventory", item.requestedItem.Id));
httpResponseUtil.AppendErrorToOutput(output);
return;
@@ -115,38 +101,25 @@ public class HideoutController(
}
// Construction time management
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == request.AreaType
);
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == request.AreaType);
if (profileHideoutArea is null)
{
logger.Error(
serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType));
httpResponseUtil.AppendErrorToOutput(output);
return;
}
var hideoutDataDb = databaseService
.GetTables()
.Hideout.Areas.FirstOrDefault(area => area.Type == request.AreaType);
var hideoutDataDb = databaseService.GetTables().Hideout.Areas.FirstOrDefault(area => area.Type == request.AreaType);
if (hideoutDataDb is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_area_in_database",
request.AreaType
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area_in_database", request.AreaType));
httpResponseUtil.AppendErrorToOutput(output);
return;
}
var ctime = hideoutDataDb
.Stages[(profileHideoutArea.Level + 1).ToString()]
.ConstructionTime;
var ctime = hideoutDataDb.Stages[(profileHideoutArea.Level + 1).ToString()].ConstructionTime;
if (ctime > 0)
{
if (profileHelper.IsDeveloperAccount(sessionID))
@@ -179,14 +152,10 @@ public class HideoutController(
var hideout = databaseService.GetHideout();
var globals = databaseService.GetGlobals();
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == request.AreaType
);
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == request.AreaType);
if (profileHideoutArea is null)
{
logger.Error(
serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType));
httpResponseUtil.AppendErrorToOutput(output);
return;
@@ -197,33 +166,19 @@ public class HideoutController(
profileHideoutArea.CompleteTime = 0;
profileHideoutArea.Constructing = false;
var hideoutData = hideout.Areas.FirstOrDefault(area =>
area.Type == profileHideoutArea.Type
);
var hideoutData = hideout.Areas.FirstOrDefault(area => area.Type == profileHideoutArea.Type);
if (hideoutData is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_area_in_database",
request.AreaType
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area_in_database", request.AreaType));
httpResponseUtil.AppendErrorToOutput(output);
return;
}
// Apply bonuses
if (
!hideoutData.Stages.TryGetValue(
profileHideoutArea.Level.ToString(),
out var hideoutStage
)
)
if (!hideoutData.Stages.TryGetValue(profileHideoutArea.Level.ToString(), out var hideoutStage))
{
logger.Error(
$"Stage level: {profileHideoutArea.Level} not found for area: {request.AreaType}"
);
logger.Error($"Stage level: {profileHideoutArea.Level} not found for area: {request.AreaType}");
return;
}
@@ -239,21 +194,11 @@ public class HideoutController(
// Upgrade includes a container improvement/addition
if (!string.IsNullOrEmpty(hideoutStage.Container))
{
AddContainerImprovementToProfile(
output,
sessionID,
pmcData,
profileHideoutArea,
hideoutData,
hideoutStage
);
AddContainerImprovementToProfile(output, sessionID, pmcData, profileHideoutArea, hideoutData, hideoutStage);
}
// Upgrading water collector / med station
if (
profileHideoutArea.Type == HideoutAreas.WaterCollector
|| profileHideoutArea.Type == HideoutAreas.MedStation
)
if (profileHideoutArea.Type == HideoutAreas.WaterCollector || profileHideoutArea.Type == HideoutAreas.MedStation)
{
SetWallVisibleIfPrereqsMet(pmcData);
}
@@ -278,17 +223,11 @@ public class HideoutController(
/// <param name="pmcData">Player profile</param>
protected void SetWallVisibleIfPrereqsMet(PmcData pmcData)
{
var medStation = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == HideoutAreas.MedStation
);
var waterCollector = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == HideoutAreas.WaterCollector
);
var medStation = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.MedStation);
var waterCollector = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.WaterCollector);
if (medStation?.Level >= 1 && waterCollector?.Level >= 1)
{
var wall = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == HideoutAreas.EmergencyWall
);
var wall = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.EmergencyWall);
if (wall?.Level == 0)
{
wall.Level = 3;
@@ -319,12 +258,7 @@ public class HideoutController(
var keyForHideoutAreaStash = ((int)dbHideoutArea.Type).ToString();
if (!pmcData.Inventory.HideoutAreaStashes.ContainsKey(keyForHideoutAreaStash))
{
if (
!pmcData.Inventory.HideoutAreaStashes.TryAdd(
keyForHideoutAreaStash,
dbHideoutArea.Id
)
)
if (!pmcData.Inventory.HideoutAreaStashes.TryAdd(keyForHideoutAreaStash, dbHideoutArea.Id))
{
logger.Error($"Unable to add key: {dbHideoutArea.Type} to HideoutAreaStashes");
}
@@ -337,27 +271,13 @@ public class HideoutController(
if (dbHideoutArea.Type == HideoutAreas.EquipmentPresetsStand)
// Can have multiple 'standx' children depending on upgrade level
{
AddMissingPresetStandItemsToProfile(
sessionId,
hideoutStage,
pmcData,
dbHideoutArea,
output
);
AddMissingPresetStandItemsToProfile(sessionId, hideoutStage, pmcData, dbHideoutArea, output);
}
AddContainerUpgradeToClientOutput(
sessionId,
keyForHideoutAreaStash,
dbHideoutArea,
hideoutStage,
output
);
AddContainerUpgradeToClientOutput(sessionId, keyForHideoutAreaStash, dbHideoutArea, hideoutStage, output);
// Some hideout areas (Gun stand) have child areas linked to it
var childDbArea = databaseService
.GetHideout()
.Areas.FirstOrDefault(area => area.ParentArea == dbHideoutArea.Id);
var childDbArea = databaseService.GetHideout().Areas.FirstOrDefault(area => area.ParentArea == dbHideoutArea.Id);
if (childDbArea is null)
{
// No child db area, we're complete
@@ -372,23 +292,14 @@ public class HideoutController(
}
// Set child area level to same as parent area
pmcData
.Hideout.Areas.FirstOrDefault(hideoutArea => hideoutArea.Type == childDbArea.Type)
.Level = pmcData
pmcData.Hideout.Areas.FirstOrDefault(hideoutArea => hideoutArea.Type == childDbArea.Type).Level = pmcData
.Hideout.Areas.FirstOrDefault(area => area.Type == profileParentHideoutArea.Type)
.Level;
// Add/upgrade stash item in player inventory
if (
!childDbArea.Stages.TryGetValue(
profileParentHideoutArea.Level.ToString(),
out var childDbAreaStage
)
)
if (!childDbArea.Stages.TryGetValue(profileParentHideoutArea.Level.ToString(), out var childDbAreaStage))
{
logger.Error(
$"Unable to find stage: {profileParentHideoutArea.Level} of area: {dbHideoutArea.Id}"
);
logger.Error($"Unable to find stage: {profileParentHideoutArea.Level} of area: {dbHideoutArea.Id}");
return;
}
@@ -396,13 +307,7 @@ public class HideoutController(
AddUpdateInventoryItemToProfile(sessionId, pmcData, childDbArea, childDbAreaStage);
// Inform client of the changes
AddContainerUpgradeToClientOutput(
sessionId,
childAreaTypeKey,
childDbArea,
childDbAreaStage,
output
);
AddContainerUpgradeToClientOutput(sessionId, childAreaTypeKey, childDbArea, childDbAreaStage, output);
}
/// <summary>
@@ -412,16 +317,9 @@ public class HideoutController(
/// <param name="pmcData">Players PMC profile</param>
/// <param name="dbHideoutArea">Hideout area from db being upgraded</param>
/// <param name="hideoutStage">Stage area upgraded to</param>
protected void AddUpdateInventoryItemToProfile(
MongoId sessionId,
PmcData pmcData,
HideoutArea dbHideoutArea,
Stage hideoutStage
)
protected void AddUpdateInventoryItemToProfile(MongoId sessionId, PmcData pmcData, HideoutArea dbHideoutArea, Stage hideoutStage)
{
var existingInventoryItem = pmcData.Inventory.Items.FirstOrDefault(item =>
item.Id == dbHideoutArea.Id
);
var existingInventoryItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == dbHideoutArea.Id);
if (existingInventoryItem is not null)
{
// Update existing items container tpl to point to new id (tpl)
@@ -431,11 +329,7 @@ public class HideoutController(
}
// Add new item as none exists (don't inform client of newContainerItem, will be done in `profileChanges.changedHideoutStashes`)
var newContainerItem = new Item
{
Id = dbHideoutArea.Id,
Template = hideoutStage.Container.Value,
};
var newContainerItem = new Item { Id = dbHideoutArea.Id, Template = hideoutStage.Container.Value };
pmcData.Inventory.Items.Add(newContainerItem);
}
@@ -456,12 +350,14 @@ public class HideoutController(
)
{
// Ensure ChangedHideoutStashes isn't null
output.ProfileChanges[sessionId].ChangedHideoutStashes ??=
new Dictionary<string, HideoutStashItem>();
output.ProfileChanges[sessionId].ChangedHideoutStashes ??= new Dictionary<string, HideoutStashItem>();
// Inform client of changes
output.ProfileChanges[sessionId].ChangedHideoutStashes[changedHideoutStashesKey] =
new HideoutStashItem { Id = hideoutDbData.Id, Template = hideoutStage.Container };
output.ProfileChanges[sessionId].ChangedHideoutStashes[changedHideoutStashesKey] = new HideoutStashItem
{
Id = hideoutDbData.Id,
Template = hideoutStage.Container,
};
}
/// <summary>
@@ -482,9 +378,7 @@ public class HideoutController(
var itemsToAdd = addItemToHideoutRequest.Items.Select(kvp =>
{
var item = pmcData.Inventory.Items.FirstOrDefault(invItem =>
invItem.Id == kvp.Value.Id
);
var item = pmcData.Inventory.Items.FirstOrDefault(invItem => invItem.Id == kvp.Value.Id);
return new
{
inventoryItem = item,
@@ -493,17 +387,10 @@ public class HideoutController(
};
});
var hideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == addItemToHideoutRequest.AreaType
);
var hideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == addItemToHideoutRequest.AreaType);
if (hideoutArea is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_area_in_database",
addItemToHideoutRequest.AreaType
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area_in_database", addItemToHideoutRequest.AreaType));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -522,9 +409,7 @@ public class HideoutController(
// Add item to area.slots
var destinationLocationIndex = int.Parse(item.slot);
var hideoutSlotIndex = hideoutArea.Slots.FindIndex(slot =>
slot.LocationIndex == destinationLocationIndex
);
var hideoutSlotIndex = hideoutArea.Slots.FindIndex(slot => slot.LocationIndex == destinationLocationIndex);
if (hideoutSlotIndex == -1)
{
logger.Error(
@@ -560,33 +445,20 @@ public class HideoutController(
/// <param name="request">Take item out of area request</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse TakeItemsFromAreaSlots(
PmcData pmcData,
HideoutTakeItemOutRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse TakeItemsFromAreaSlots(PmcData pmcData, HideoutTakeItemOutRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
var hideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == request.AreaType
);
var hideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == request.AreaType);
if (hideoutArea is null)
{
logger.Error(
serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType));
return httpResponseUtil.AppendErrorToOutput(output);
}
if (hideoutArea.Slots is null || hideoutArea.Slots.Count == 0)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_item_to_remove_from_area",
hideoutArea.Type
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.Type));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -600,12 +472,7 @@ public class HideoutController(
return response;
}
throw new Exception(
serverLocalisationService.GetText(
"hideout-unhandled_remove_item_from_area_request",
hideoutArea.Type
)
);
throw new Exception(serverLocalisationService.GetText("hideout-unhandled_remove_item_from_area_request", hideoutArea.Type));
}
/// <summary>
@@ -636,9 +503,7 @@ public class HideoutController(
}
// Assume only one item in slot
var itemToReturn = hideoutArea
.Slots?.FirstOrDefault(slot => slot.LocationIndex == slotIndexToRemove)
?.Items?.FirstOrDefault();
var itemToReturn = hideoutArea.Slots?.FirstOrDefault(slot => slot.LocationIndex == slotIndexToRemove)?.Items?.FirstOrDefault();
if (itemToReturn is null)
{
logger.Error(
@@ -665,9 +530,7 @@ public class HideoutController(
}
// Remove items from slot, keep locationIndex object
var hideoutSlotIndex = hideoutArea.Slots.FindIndex(slot =>
slot.LocationIndex == slotIndexToRemove
);
var hideoutSlotIndex = hideoutArea.Slots.FindIndex(slot => slot.LocationIndex == slotIndexToRemove);
hideoutArea.Slots[hideoutSlotIndex].Items = null;
return output;
@@ -681,25 +544,17 @@ public class HideoutController(
/// <param name="request">Toggle area request</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse ToggleArea(
PmcData pmcData,
HideoutToggleAreaRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse ToggleArea(PmcData pmcData, HideoutToggleAreaRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
hideoutHelper.UpdatePlayerHideout(sessionID);
var hideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == request.AreaType
);
var hideoutArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == request.AreaType);
if (hideoutArea is null)
{
logger.Error(
serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -725,14 +580,10 @@ public class HideoutController(
hideoutHelper.RegisterProduction(pmcData, request, sessionID);
// Find the recipe of the production
var recipe = databaseService
.GetHideout()
.Production.Recipes.FirstOrDefault(production => production.Id == request.RecipeId);
var recipe = databaseService.GetHideout().Production.Recipes.FirstOrDefault(production => production.Id == request.RecipeId);
// Find the actual amount of items we need to remove because body can send weird data
var recipeRequirementsClone = cloner.Clone(
recipe.Requirements.Where(r => r.Type == "Item" || r.Type == "Tool")
);
var recipeRequirementsClone = cloner.Clone(recipe.Requirements.Where(r => r.Type == "Item" || r.Type == "Tool"));
List<IdWithCount> itemsToDelete = [];
var output = eventOutputHolder.GetOutput(sessionID);
@@ -742,9 +593,7 @@ public class HideoutController(
foreach (var itemToDelete in itemsToDelete)
{
var itemToCheck = pmcData.Inventory.Items.FirstOrDefault(i => i.Id == itemToDelete.Id);
var requirement = recipeRequirementsClone.FirstOrDefault(requirement =>
requirement.TemplateId == itemToCheck.Template
);
var requirement = recipeRequirementsClone.FirstOrDefault(requirement => requirement.TemplateId == itemToCheck.Template);
// Handle tools not having a `count`, but always only requiring 1
var requiredCount = requirement.Count ?? 1;
@@ -753,13 +602,7 @@ public class HideoutController(
continue;
}
inventoryHelper.RemoveItemByCount(
pmcData,
itemToDelete.Id,
requiredCount,
sessionID,
output
);
inventoryHelper.RemoveItemByCount(pmcData, itemToDelete.Id, requiredCount, sessionID, output);
// Tools don't have a count
if (requirement.Type != "Tool")
@@ -779,19 +622,13 @@ public class HideoutController(
/// <param name="request"></param>
/// <param name="sessionID">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse ScavCaseProductionStart(
PmcData pmcData,
HideoutScavCaseStartRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse ScavCaseProductionStart(PmcData pmcData, HideoutScavCaseStartRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
foreach (var requestedItem in request.Items)
{
var inventoryItem = pmcData.Inventory.Items.FirstOrDefault(item =>
item.Id == requestedItem.Id
);
var inventoryItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == requestedItem.Id);
if (inventoryItem is null)
{
logger.Error(
@@ -803,10 +640,7 @@ public class HideoutController(
return httpResponseUtil.AppendErrorToOutput(output);
}
if (
inventoryItem.Upd?.StackObjectsCount is not null
&& inventoryItem.Upd.StackObjectsCount > requestedItem.Count
)
if (inventoryItem.Upd?.StackObjectsCount is not null && inventoryItem.Upd.StackObjectsCount > requestedItem.Count)
{
inventoryItem.Upd.StackObjectsCount -= requestedItem.Count;
}
@@ -816,17 +650,10 @@ public class HideoutController(
}
}
var recipe = databaseService
.GetHideout()
.Production?.ScavRecipes?.FirstOrDefault(r => r.Id == request.RecipeId);
var recipe = databaseService.GetHideout().Production?.ScavRecipes?.FirstOrDefault(r => r.Id == request.RecipeId);
if (recipe is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_scav_case_recipie_in_database",
request.RecipeId
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_scav_case_recipie_in_database", request.RecipeId));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -840,9 +667,7 @@ public class HideoutController(
pmcData,
recipe.ProductionTime ?? 0,
SkillTypes.Crafting,
databaseService
.GetGlobals()
.Configuration.SkillsSettings.Crafting.CraftTimeReductionPerLevel
databaseService.GetGlobals().Configuration.SkillsSettings.Crafting.CraftTimeReductionPerLevel
);
var modifiedScavCaseTime = GetScavCaseTime(pmcData, adjustedCraftTime);
@@ -900,11 +725,7 @@ public class HideoutController(
/// <param name="request">Remove production from area request</param>
/// <param name="sessionID">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse TakeProduction(
PmcData pmcData,
HideoutTakeProductionRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse TakeProduction(PmcData pmcData, HideoutTakeProductionRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
var hideoutDb = databaseService.GetHideout();
@@ -926,9 +747,7 @@ public class HideoutController(
return output;
}
var scavCase = hideoutDb.Production.ScavRecipes.FirstOrDefault(r =>
r.Id == request.RecipeId
);
var scavCase = hideoutDb.Production.ScavRecipes.FirstOrDefault(r => r.Id == request.RecipeId);
if (scavCase is not null)
{
HandleScavCase(sessionID, pmcData, request, output);
@@ -936,12 +755,7 @@ public class HideoutController(
return output;
}
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.RecipeId
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.RecipeId));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -986,19 +800,11 @@ public class HideoutController(
// If we're unable to find the production, send an error to the client
if (prodId is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.RecipeId
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.RecipeId));
httpResponseUtil.AppendErrorToOutput(
output,
serverLocalisationService.GetText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.RecipeId
)
serverLocalisationService.GetText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.RecipeId)
);
return;
@@ -1029,10 +835,7 @@ public class HideoutController(
{
itemHelper.AddUpdObjectToItem(reward.FirstOrDefault());
reward.FirstOrDefault().Upd.RecodableComponent = new UpdRecodableComponent
{
IsEncoded = true,
};
reward.FirstOrDefault().Upd.RecodableComponent = new UpdRecodableComponent { IsEncoded = true };
}
}
@@ -1061,9 +864,7 @@ public class HideoutController(
if (totalCraftingHours / _hideoutConfig.HoursForSkillCrafting >= 1)
{
// Spent enough time crafting to get a bonus xp multiplier
var multiplierCrafting = Math.Floor(
totalCraftingHours.Value / _hideoutConfig.HoursForSkillCrafting
);
var multiplierCrafting = Math.Floor(totalCraftingHours.Value / _hideoutConfig.HoursForSkillCrafting);
craftingExpAmount += (int)(1 * multiplierCrafting);
totalCraftingHours -= _hideoutConfig.HoursForSkillCrafting * multiplierCrafting;
}
@@ -1136,11 +937,7 @@ public class HideoutController(
var intellectAmountToGive = 0.5 * Math.Round((double)(craftingExpAmount / 15));
if (intellectAmountToGive > 0)
{
profileHelper.AddSkillPointsToPlayer(
pmcData,
SkillTypes.Intellect,
intellectAmountToGive
);
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Intellect, intellectAmountToGive);
}
}
@@ -1157,11 +954,7 @@ public class HideoutController(
// production.json is set to
if (recipe.Continuous.GetValueOrDefault(false))
{
hideoutProduction.ProductionTime = hideoutHelper.GetAdjustedCraftTimeWithSkills(
pmcData,
recipe.Id,
true
);
hideoutProduction.ProductionTime = hideoutHelper.GetAdjustedCraftTimeWithSkills(pmcData, recipe.Id, true);
}
// Flag normal (not continuous) crafts as complete
@@ -1177,11 +970,7 @@ public class HideoutController(
/// <param name="recipe"></param>
/// <param name="itemAndChildrenToSendToPlayer"></param>
/// <param name="rewardIsPreset">Reward is a preset</param>
protected void HandleStackableState(
HideoutProduction recipe,
List<List<Item>> itemAndChildrenToSendToPlayer,
bool rewardIsPreset
)
protected void HandleStackableState(HideoutProduction recipe, List<List<Item>> itemAndChildrenToSendToPlayer, bool rewardIsPreset)
{
var rewardIsStackable = itemHelper.IsItemTplStackable(recipe.EndProduct);
if (rewardIsStackable.GetValueOrDefault(false))
@@ -1206,9 +995,7 @@ public class HideoutController(
// Add the first reward item to array when not a preset (first preset added above earlier)
if (!rewardIsPreset)
{
itemAndChildrenToSendToPlayer.Add(
[new Item { Id = new MongoId(), Template = recipe.EndProduct }]
);
itemAndChildrenToSendToPlayer.Add([new Item { Id = new MongoId(), Template = recipe.EndProduct }]);
}
// Add multiple of item if recipe requests it
@@ -1216,10 +1003,7 @@ public class HideoutController(
var countOfItemsToReward = recipe.Count;
for (var index = 1; index < countOfItemsToReward; index++)
{
var firstItemWithChildrenClone = cloner
.Clone(itemAndChildrenToSendToPlayer.FirstOrDefault())
.ReplaceIDs()
.ToList();
var firstItemWithChildrenClone = cloner.Clone(itemAndChildrenToSendToPlayer.FirstOrDefault()).ReplaceIDs().ToList();
itemAndChildrenToSendToPlayer.AddRange([firstItemWithChildrenClone]);
}
@@ -1249,10 +1033,7 @@ public class HideoutController(
/// <param name="pmcData">Profile to get counter from</param>
/// <param name="recipe">Recipe being crafted</param>
/// <returns>TaskConditionCounter</returns>
protected TaskConditionCounter GetCustomSptHoursCraftingTaskConditionCounter(
PmcData pmcData,
HideoutProduction recipe
)
protected TaskConditionCounter GetCustomSptHoursCraftingTaskConditionCounter(PmcData pmcData, HideoutProduction recipe)
{
// Add if doesn't exist
pmcData.TaskConditionCounters.TryAdd(
@@ -1297,12 +1078,7 @@ public class HideoutController(
if (prodId == null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_production_in_profile_by_recipie_id",
request.RecipeId
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_production_in_profile_by_recipie_id", request.RecipeId));
httpResponseUtil.AppendErrorToOutput(output);
@@ -1344,12 +1120,7 @@ public class HideoutController(
/// <param name="pmcData">Players PMC profile</param>
/// <param name="request">QTE result object</param>
/// <param name="output">Client response</param>
public void HandleQTEEventOutcome(
MongoId sessionId,
PmcData pmcData,
HandleQTEEventRequestData request,
ItemEventRouterResponse output
)
public void HandleQTEEventOutcome(MongoId sessionId, PmcData pmcData, HandleQTEEventRequestData request, ItemEventRouterResponse output)
{
// {
// "Action": "HideoutQuickTimeEvent",
@@ -1368,22 +1139,14 @@ public class HideoutController(
if (outcome)
{
// Success
pmcData.Health.Energy.Current += relevantQte
.Results[QteEffectType.singleSuccessEffect]
.Energy;
pmcData.Health.Hydration.Current += relevantQte
.Results[QteEffectType.singleSuccessEffect]
.Hydration;
pmcData.Health.Energy.Current += relevantQte.Results[QteEffectType.singleSuccessEffect].Energy;
pmcData.Health.Hydration.Current += relevantQte.Results[QteEffectType.singleSuccessEffect].Hydration;
}
else
{
// Failed
pmcData.Health.Energy.Current += relevantQte
.Results[QteEffectType.singleFailEffect]
.Energy;
pmcData.Health.Hydration.Current += relevantQte
.Results[QteEffectType.singleFailEffect]
.Hydration;
pmcData.Health.Energy.Current += relevantQte.Results[QteEffectType.singleFailEffect].Energy;
pmcData.Health.Hydration.Current += relevantQte.Results[QteEffectType.singleFailEffect].Hydration;
}
}
@@ -1408,21 +1171,17 @@ public class HideoutController(
protected void HandleMusclePain(PmcData pmcData, QteResult finishEffect)
{
var hasMildPain = pmcData.Health.BodyParts["Chest"].Effects?.ContainsKey("MildMusclePain");
var hasSeverePain = pmcData
.Health.BodyParts["Chest"]
.Effects?.ContainsKey("SevereMusclePain");
var hasSeverePain = pmcData.Health.BodyParts["Chest"].Effects?.ContainsKey("SevereMusclePain");
// Has no muscle pain at all, add mild
if (!hasMildPain.GetValueOrDefault(false) && !hasSeverePain.GetValueOrDefault(false))
{
// Nullguard
pmcData.Health.BodyParts["Chest"].Effects ??=
new Dictionary<string, BodyPartEffectProperties>();
pmcData.Health.BodyParts["Chest"].Effects["MildMusclePain"] =
new BodyPartEffectProperties
{
Time = finishEffect.RewardEffects.FirstOrDefault().Time, // TODO - remove hard coded access, get value properly
};
pmcData.Health.BodyParts["Chest"].Effects ??= new Dictionary<string, BodyPartEffectProperties>();
pmcData.Health.BodyParts["Chest"].Effects["MildMusclePain"] = new BodyPartEffectProperties
{
Time = finishEffect.RewardEffects.FirstOrDefault().Time, // TODO - remove hard coded access, get value properly
};
return;
}
@@ -1432,11 +1191,10 @@ public class HideoutController(
// Already has mild pain, remove mild and add severe
pmcData.Health.BodyParts["Chest"].Effects.Remove("MildMusclePain");
pmcData.Health.BodyParts["Chest"].Effects["SevereMusclePain"] =
new BodyPartEffectProperties
{
Time = finishEffect.RewardEffects.FirstOrDefault().Time,
};
pmcData.Health.BodyParts["Chest"].Effects["SevereMusclePain"] = new BodyPartEffectProperties
{
Time = finishEffect.RewardEffects.FirstOrDefault().Time,
};
}
}
@@ -1446,26 +1204,18 @@ public class HideoutController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="pmcData">Players PMC profile</param>
/// <param name="request">shooting range score request></param>
public void RecordShootingRangePoints(
MongoId sessionId,
PmcData pmcData,
RecordShootingRangePoints request
)
public void RecordShootingRangePoints(MongoId sessionId, PmcData pmcData, RecordShootingRangePoints request)
{
const string shootingRangeKey = "ShootingRangePoints";
var overallCounterItems = pmcData.Stats.Eft.OverallCounters.Items;
// Find counter by key
var shootingRangeHighScore = overallCounterItems.FirstOrDefault(counter =>
counter.Key.Contains(shootingRangeKey)
);
var shootingRangeHighScore = overallCounterItems.FirstOrDefault(counter => counter.Key.Contains(shootingRangeKey));
if (shootingRangeHighScore is null)
{
// Counter not found, add blank one
overallCounterItems.Add(new CounterKeyValue { Key = [shootingRangeKey], Value = 0 });
shootingRangeHighScore = overallCounterItems.FirstOrDefault(counter =>
counter.Key.Contains(shootingRangeKey)
);
shootingRangeHighScore = overallCounterItems.FirstOrDefault(counter => counter.Key.Contains(shootingRangeKey));
}
shootingRangeHighScore.Value = request.Points;
@@ -1478,11 +1228,7 @@ public class HideoutController(
/// <param name="pmcData">Players PMC profile</param>
/// <param name="request">Improve area request</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse ImproveArea(
MongoId sessionId,
PmcData pmcData,
HideoutImproveAreaRequestData request
)
public ItemEventRouterResponse ImproveArea(MongoId sessionId, PmcData pmcData, HideoutImproveAreaRequestData request)
{
var output = eventOutputHolder.GetOutput(sessionId);
@@ -1498,12 +1244,7 @@ public class HideoutController(
{
if (item.inventoryItem is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_item_in_inventory",
item.requestedItem.Id
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_item_in_inventory", item.requestedItem.Id));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -1522,28 +1263,17 @@ public class HideoutController(
}
}
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(x =>
x.Type == request.AreaType
);
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(x => x.Type == request.AreaType);
if (profileHideoutArea is null)
{
logger.Error(
serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area", request.AreaType));
return httpResponseUtil.AppendErrorToOutput(output);
}
var hideoutDbData = databaseService
.GetHideout()
.Areas.FirstOrDefault(area => area.Type == request.AreaType);
var hideoutDbData = databaseService.GetHideout().Areas.FirstOrDefault(area => area.Type == request.AreaType);
if (hideoutDbData is null)
{
logger.Error(
serverLocalisationService.GetText(
"hideout-unable_to_find_area_in_database",
request.AreaType
)
);
logger.Error(serverLocalisationService.GetText("hideout-unable_to_find_area_in_database", request.AreaType));
return httpResponseUtil.AppendErrorToOutput(output);
}
@@ -1577,11 +1307,7 @@ public class HideoutController(
/// <param name="pmcData">Players PMC profile</param>
/// <param name="request">Cancel production request data</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse CancelProduction(
MongoId sessionId,
PmcData pmcData,
HideoutCancelProductionRequestData request
)
public ItemEventRouterResponse CancelProduction(MongoId sessionId, PmcData pmcData, HideoutCancelProductionRequestData request)
{
var output = eventOutputHolder.GetOutput(sessionId);
@@ -1647,21 +1373,15 @@ public class HideoutController(
{
var output = eventOutputHolder.GetOutput(sessionId);
var itemDetails = databaseService
.GetHideout()
.Customisation.Globals.FirstOrDefault(cust => cust.Id == request.OfferId);
var itemDetails = databaseService.GetHideout().Customisation.Globals.FirstOrDefault(cust => cust.Id == request.OfferId);
if (itemDetails is null)
{
logger.Error(
$"Unable to find customisation: {request.OfferId} in db, cannot apply to hideout"
);
logger.Error($"Unable to find customisation: {request.OfferId} in db, cannot apply to hideout");
return output;
}
pmcData.Hideout.Customization[GetHideoutCustomisationType(itemDetails.Type)] = itemDetails
.ItemId
.Value;
pmcData.Hideout.Customization[GetHideoutCustomisationType(itemDetails.Type)] = itemDetails.ItemId.Value;
return output;
}
@@ -1734,9 +1454,7 @@ public class HideoutController(
{
Id = new MongoId(),
Template = pmcData
.Inventory.Items.FirstOrDefault(item =>
item.SlotId == "Pockets" && item.ParentId == pmcData.Inventory.Equipment
)
.Inventory.Items.FirstOrDefault(item => item.SlotId == "Pockets" && item.ParentId == pmcData.Inventory.Equipment)
.Template, // Same pocket tpl as players profile (unheard get bigger, matching pockets etc)
ParentId = standId,
SlotId = "Pockets",
@@ -1763,9 +1481,7 @@ public class HideoutController(
{
if (request.Poses is null)
{
logger.Warning(
"this really shouldnt be possible, but a request has come in with a pose change without poses"
);
logger.Warning("this really shouldnt be possible, but a request has come in with a pose change without poses");
return eventOutputHolder.GetOutput(sessionId);
}
@@ -1800,10 +1516,7 @@ public class HideoutController(
{
if (
profile.CharacterData.PmcData.Hideout is not null
&& profileActivityService.ActiveWithinLastMinutes(
sessionId,
_hideoutConfig.UpdateProfileHideoutWhenActiveWithinMinutes
)
&& profileActivityService.ActiveWithinLastMinutes(sessionId, _hideoutConfig.UpdateProfileHideoutWhenActiveWithinMinutes)
)
{
hideoutHelper.UpdatePlayerHideout(sessionId);
@@ -36,10 +36,7 @@ public class InRaidController(
/// </summary>
/// <param name="offRaidProfileData"></param>
/// <param name="sessionId">Session/Player id</param>
public void SavePostRaidProfileForScav(
ScavSaveRequestData offRaidProfileData,
MongoId sessionId
)
public void SavePostRaidProfileForScav(ScavSaveRequestData offRaidProfileData, MongoId sessionId)
{
var serverScavProfile = profileHelper.GetScavProfile(sessionId);
@@ -90,9 +90,7 @@ 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}");
}
}
@@ -171,9 +169,7 @@ public class InsuranceController(
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}");
}
}
@@ -190,11 +186,7 @@ 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 = insured.Items.GenerateItemsMap();
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));
@@ -278,11 +270,7 @@ public class InsuranceController(
logger.Warning(
serverLocalisationService.GetText(
"insurance-unable_to_find_attachment_in_db",
new
{
insuredItemId = insuredItem.Id,
insuredItemTpl = insuredItem.Template,
}
new { insuredItemId = insuredItem.Id, insuredItemTpl = insuredItem.Template }
)
);
@@ -384,11 +372,7 @@ 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<MongoId> toDelete,
Dictionary<MongoId, List<Item>> parentAttachmentsMap
)
protected void ProcessRegularItems(Insurance insured, HashSet<MongoId> toDelete, Dictionary<MongoId, List<Item>> parentAttachmentsMap)
{
foreach (var insuredItem in insured.Items)
{
@@ -471,35 +455,23 @@ 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(
IEnumerable<Item> attachments,
MongoId traderId,
HashSet<MongoId> toDelete
)
protected void ProcessAttachmentByParent(IEnumerable<Item> attachments, MongoId traderId, HashSet<MongoId> 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<MongoId, double?>(cloner);
foreach (var (itemTpl, price) in weightedAttachmentByPrice)
{
attachmentsProbabilityArray.Add(
new ProbabilityObject<MongoId, double?>(itemTpl, price, null)
);
attachmentsProbabilityArray.Add(new ProbabilityObject<MongoId, double?>(itemTpl, price, null));
}
// 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);
@@ -570,10 +542,7 @@ 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<MongoId, double> weightedAttachmentByPrice,
MongoId traderId
)
protected double GetAttachmentCountToRemove(Dictionary<MongoId, double> weightedAttachmentByPrice, MongoId traderId)
{
const int removeCount = 0;
@@ -584,9 +553,7 @@ 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);
}
@@ -625,12 +592,7 @@ public class InsuranceController(
else if (insurance.Items?.Count == 0)
// 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);
}
@@ -656,11 +618,8 @@ 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 ?? false);
return string.Equals(insurance.SystemData?.Location, labsId, StringComparison.OrdinalIgnoreCase)
&& !(databaseService.GetLocation(labsId)?.Base?.Insurance ?? false);
}
/// <summary>
@@ -669,16 +628,10 @@ public class InsuranceController(
/// <param name="insurance">The insured items to process</param>
/// <param name="labyrinthId">OPTIONAL - id of labyrinth 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 ?? false);
return string.Equals(insurance.SystemData?.Location, labyrinthId, StringComparison.OrdinalIgnoreCase)
&& !(databaseService.GetLocation(labyrinthId)?.Base?.Insurance ?? false);
}
/// <summary>
@@ -686,10 +639,7 @@ 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
if (!traderDialogMessages.TryGetValue("insuranceFailedLabs", out var responseMessageIds))
@@ -708,17 +658,9 @@ 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)
{
if (
!traderDialogMessages.TryGetValue(
"insuranceFailedLabyrinth",
out var responseMessageIds
)
)
if (!traderDialogMessages.TryGetValue("insuranceFailedLabyrinth", out var responseMessageIds))
{
traderDialogMessages.TryGetValue("insuranceFailed", out responseMessageIds);
}
@@ -751,15 +693,11 @@ 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;
@@ -772,11 +710,7 @@ 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,
MongoId sessionId
)
public ItemEventRouterResponse Insure(PmcData pmcData, InsureRequestData request, MongoId sessionId)
{
var output = eventOutputHolder.GetOutput(sessionId);
var itemsToInsureCount = request.Items.Count;
@@ -792,11 +726,7 @@ public class InsuranceController(
new IdWithCount
{
Id = Money.ROUBLES, // TODO: update to handle different currencies
Count = insuranceService.GetRoublePriceToInsureItemWithTrader(
pmcData,
inventoryItemsHash[key],
request.TransactionId
),
Count = insuranceService.GetRoublePriceToInsureItemWithTrader(pmcData, inventoryItemsHash[key], request.TransactionId),
}
);
}
@@ -824,9 +754,7 @@ public class InsuranceController(
foreach (var key in request.Items)
{
var inventoryItem = inventoryItemsHash.GetValueOrDefault(key);
pmcData.InsuredItems.Add(
new InsuredItem { TId = request.TransactionId, ItemId = inventoryItem.Id }
);
pmcData.InsuredItems.Add(new InsuredItem { TId = request.TransactionId, ItemId = inventoryItem.Id });
// If Item is Helmet or Body Armour -> Handle insurance of soft inserts
if (itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(inventoryItem.Template))
{
@@ -834,11 +762,7 @@ public class InsuranceController(
}
}
profileHelper.AddSkillPointsToPlayer(
pmcData,
SkillTypes.Charisma,
itemsToInsureCount * 0.01
);
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Charisma, itemsToInsureCount * 0.01);
return output;
}
@@ -849,15 +773,10 @@ 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.ToLowerInvariant())
item.ParentId == itemWithSoftInserts.Id && itemHelper.IsSoftInsertId(item.SlotId.ToLowerInvariant())
);
foreach (var softInsertSlot in softInsertSlots)
@@ -867,9 +786,7 @@ public class InsuranceController(
logger.Debug($"SoftInsertSlots: {softInsertSlot.SlotId}");
}
pmcData.InsuredItems.Add(
new InsuredItem { TId = request.TransactionId, ItemId = softInsertSlot.Id }
);
pmcData.InsuredItems.Add(new InsuredItem { TId = request.TransactionId, ItemId = softInsertSlot.Id });
}
}
@@ -900,9 +817,7 @@ 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;
@@ -911,11 +826,7 @@ public class InsuranceController(
if (
!traderItems.TryAdd(
inventoryItem.Template,
insuranceService.GetRoublePriceToInsureItemWithTrader(
pmcData,
inventoryItem,
traderId
)
insuranceService.GetRoublePriceToInsureItemWithTrader(pmcData, inventoryItem, traderId)
)
)
{
@@ -44,12 +44,7 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void MoveItem(PmcData pmcData, InventoryMoveRequestData moveRequest, MongoId sessionId, ItemEventRouterResponse output)
{
if (output.Warnings?.Count > 0)
{
@@ -57,27 +52,18 @@ public class InventoryController(
}
// Changes made to result apply to character inventory
var ownerInventoryItems = inventoryHelper.GetOwnerInventoryItems(
moveRequest,
moveRequest.Item.Value,
sessionId
);
var ownerInventoryItems = inventoryHelper.GetOwnerInventoryItems(moveRequest, moveRequest.Item.Value, 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
@@ -87,12 +73,7 @@ public class InventoryController(
var originalLocationSlotId = originalItemLocation.SlotId;
var moveResult = inventoryHelper.MoveItemInternal(
pmcData,
ownerInventoryItems.From ?? [],
moveRequest,
out var errorMessage
);
var moveResult = inventoryHelper.MoveItemInternal(pmcData, ownerInventoryItems.From ?? [], moveRequest, out var errorMessage);
if (!moveResult)
{
httpResponseUtil.AppendErrorToOutput(output, errorMessage);
@@ -103,14 +84,8 @@ public class InventoryController(
if (
moveRequest.To?.Container != null
&& (
moveRequest.To.Container.StartsWith(
"dogtag",
StringComparison.OrdinalIgnoreCase
)
|| originalLocationSlotId.StartsWith(
"dogtag",
StringComparison.OrdinalIgnoreCase
)
moveRequest.To.Container.StartsWith("dogtag", StringComparison.OrdinalIgnoreCase)
|| originalLocationSlotId.StartsWith("dogtag", StringComparison.OrdinalIgnoreCase)
)
)
{
@@ -119,11 +94,7 @@ public class InventoryController(
}
else
{
inventoryHelper.MoveItemToProfile(
ownerInventoryItems.From ?? [],
ownerInventoryItems.To ?? [],
moveRequest
);
inventoryHelper.MoveItemToProfile(ownerInventoryItems.From ?? [], ownerInventoryItems.To ?? [], moveRequest);
}
}
@@ -148,21 +119,12 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void PinOrLock(PmcData pmcData, PinOrLockItemRequest request, MongoId 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.Value.ToString()} to: {request.State} on player: {sessionId} to: "
);
logger.Error($"Unable find item: {request.Item.Value.ToString()} to: {request.State} on player: {sessionId} to: ");
return;
}
@@ -192,11 +154,7 @@ 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,
MongoId sessionId
)
public void RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData request, MongoId sessionId)
{
var fullProfile = profileHelper.GetFullProfile(sessionId);
foreach (var rewardEvent in request.Events)
@@ -204,28 +162,20 @@ 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;
@@ -235,9 +185,7 @@ public class InventoryController(
break;
case "SkillPoints":
{
var profileSkill = pmcData.Skills.Common.FirstOrDefault(x =>
x.Id == Enum.Parse<SkillTypes>(mailEvent.Entity)
);
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}");
@@ -250,10 +198,7 @@ public class InventoryController(
}
case "ExamineAllItems":
{
var itemIds = databaseService
.GetItems()
.Where(x => x.Value.Type != "Node")
.Select(x => x.Key);
var itemIds = databaseService.GetItems().Where(x => x.Value.Type != "Node").Select(x => x.Key);
FlagItemsAsInspectedAndRewardXp(itemIds, fullProfile);
logger.Success($"Flagged {itemIds.Count()} items as examined");
@@ -276,9 +221,7 @@ public class InventoryController(
var newValue = mailEvent.Value;
var hideoutAreaType = Enum.Parse<HideoutAreas>(areaName ?? "NotSet");
var desiredArea = pmcData.Hideout.Areas.FirstOrDefault(area =>
area.Type == hideoutAreaType
);
var desiredArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == hideoutAreaType);
if (desiredArea is not null)
{
desiredArea.Level = (int?)newValue;
@@ -299,43 +242,27 @@ public class InventoryController(
/// </summary>
/// <param name="itemTpls">Inspected item tpls</param>
/// <param name="fullProfile">Profile to add xp to</param>
protected void FlagItemsAsInspectedAndRewardXp(
IEnumerable<MongoId> itemTpls,
SptProfile fullProfile
)
protected void FlagItemsAsInspectedAndRewardXp(IEnumerable<MongoId> itemTpls, SptProfile fullProfile)
{
foreach (var itemTpl in itemTpls)
{
var item = itemHelper.GetItem(itemTpl);
if (!item.Key)
{
logger.Warning(
serverLocalisationService.GetText(
"inventory-unable_to_inspect_item_not_in_db",
itemTpl
)
);
logger.Warning(serverLocalisationService.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;
}
// TODO: update this with correct calculation using values from globals json
profileHelper.AddSkillPointsToPlayer(
fullProfile.CharacterData.PmcData,
SkillTypes.Intellect,
0.05 * itemTpls.Count()
);
profileHelper.AddSkillPointsToPlayer(fullProfile.CharacterData.PmcData, SkillTypes.Intellect, 0.05 * itemTpls.Count());
}
/// <summary>
@@ -379,14 +306,10 @@ 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
{
@@ -427,12 +350,7 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData request, MongoId sessionId, ItemEventRouterResponse output)
{
var mapItem = mapMarkerService.EditMarkerOnMap(pmcData, request);
@@ -480,26 +398,14 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void SortInventory(PmcData pmcData, InventorySortRequestData request, MongoId 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(
serverLocalisationService.GetText(
"inventory-unable_to_sort_inventory_restart_game",
change.Id
)
);
logger.Error(serverLocalisationService.GetText("inventory-unable_to_sort_inventory_restart_game", change.Id));
continue;
}
@@ -517,11 +423,7 @@ public class InventoryController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse ReadEncyclopedia(
PmcData pmcData,
InventoryReadEncyclopediaRequestData request,
MongoId sessionId
)
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData request, MongoId sessionId)
{
foreach (var id in request.Ids)
{
@@ -538,12 +440,7 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void ExamineItem(PmcData pmcData, InventoryExamineRequestData request, MongoId sessionId, ItemEventRouterResponse output)
{
MongoId? itemId = null;
if (request.FromOwner is not null)
@@ -554,12 +451,7 @@ public class InventoryController(
}
catch
{
logger.Error(
serverLocalisationService.GetText(
"inventory-examine_item_does_not_exist",
request.ItemId
)
);
logger.Error(serverLocalisationService.GetText("inventory-examine_item_does_not_exist", request.ItemId));
}
}
@@ -605,10 +497,7 @@ public class InventoryController(
if (request.FromOwner.Id == Traders.FENCE)
// Get tpl from fence assorts
{
return fenceService
.GetRawFenceAssorts()
.Items.FirstOrDefault(x => x.Id == request.ItemId)
?.Template;
return fenceService.GetRawFenceAssorts().Items.FirstOrDefault(x => x.Id == request.ItemId)?.Template;
}
if (request.FromOwner.Type == "Trader")
@@ -632,22 +521,17 @@ public class InventoryController(
// Try alternate way of getting offer if first approach fails
var offer =
ragfairOfferService.GetOfferByOfferId(request.ItemId)
?? ragfairOfferService.GetOfferByOfferId(request.FromOwner.Id.Value);
ragfairOfferService.GetOfferByOfferId(request.ItemId) ?? ragfairOfferService.GetOfferByOfferId(request.FromOwner.Id.Value);
// Try find examine item inside offer items array
var matchingItem = offer.Items.FirstOrDefault(offerItem =>
offerItem.Id == request.ItemId
);
var matchingItem = offer.Items.FirstOrDefault(offerItem => offerItem.Id == request.ItemId);
if (matchingItem is not null)
{
return matchingItem.Template;
}
// Unable to find item in database or ragfair
logger.Warning(
serverLocalisationService.GetText("inventory-unable_to_find_item", request.ItemId)
);
logger.Warning(serverLocalisationService.GetText("inventory-unable_to_find_item", request.ItemId));
}
// get hideout item
@@ -662,9 +546,7 @@ 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
@@ -689,12 +571,7 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void UnBindItem(PmcData pmcData, InventoryBindRequestData request, MongoId sessionId, ItemEventRouterResponse output)
{
// Remove kvp from requested fast panel index
@@ -710,12 +587,7 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void BindItem(PmcData pmcData, InventoryBindRequestData bindRequest, MongoId sessionId, ItemEventRouterResponse output)
{
// Remove link
pmcData.Inventory.FastPanel.Remove(bindRequest.Index);
@@ -731,18 +603,12 @@ public class InventoryController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse TagItem(
PmcData pmcData,
InventoryTagRequestData request,
MongoId sessionId
)
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData request, MongoId sessionId)
{
var itemToTag = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
if (itemToTag is null)
{
logger.Warning(
$"Unable to tag item: {request.Item} as it cannot be found in player {sessionId} inventory"
);
logger.Warning($"Unable to tag item: {request.Item} as it cannot be found in player {sessionId} inventory");
return new ItemEventRouterResponse { Warnings = [] };
}
@@ -762,11 +628,7 @@ 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,
MongoId sessionId
)
public ItemEventRouterResponse ToggleItem(PmcData pmcData, InventoryToggleRequestData request, MongoId sessionId)
{
// May need to reassign to scav profile
var playerData = pmcData;
@@ -782,10 +644,7 @@ public class InventoryController(
{
itemHelper.AddUpdObjectToItem(
itemToToggle,
serverLocalisationService.GetText(
"inventory-item_to_toggle_missing_upd",
itemToToggle.Id
)
serverLocalisationService.GetText("inventory-item_to_toggle_missing_upd", itemToToggle.Id)
);
itemToToggle.Upd.Togglable = new UpdTogglable { On = request.Value };
@@ -793,12 +652,7 @@ public class InventoryController(
return eventOutputHolder.GetOutput(sessionId);
}
logger.Warning(
serverLocalisationService.GetText(
"inventory-unable_to_toggle_item_not_found",
request.Item
)
);
logger.Warning(serverLocalisationService.GetText("inventory-unable_to_toggle_item_not_found", request.Item));
return new ItemEventRouterResponse { Warnings = [] };
}
@@ -810,11 +664,7 @@ 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,
MongoId sessionId
)
public ItemEventRouterResponse FoldItem(PmcData pmcData, InventoryFoldRequestData request, MongoId sessionId)
{
// May need to reassign to scav profile
var playerData = pmcData;
@@ -829,12 +679,7 @@ public class InventoryController(
if (itemToFold is null)
{
// Item not found
logger.Warning(
serverLocalisationService.GetText(
"inventory-unable_to_fold_item_not_found_in_inventory",
request.Item
)
);
logger.Warning(serverLocalisationService.GetText("inventory-unable_to_fold_item_not_found_in_inventory", request.Item));
return new ItemEventRouterResponse { Warnings = [] };
}
@@ -856,11 +701,7 @@ 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,
MongoId sessionId
)
public ItemEventRouterResponse SwapItem(PmcData pmcData, InventorySwapRequestData request, MongoId sessionId)
{
// During post-raid scav transfer, the swap may be in the scav inventory
var playerData = pmcData;
@@ -917,19 +758,10 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void TransferItem(PmcData pmcData, InventoryTransferRequestData request, MongoId 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);
@@ -981,26 +813,16 @@ 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,
MongoId sessionID,
ItemEventRouterResponse output
)
public void MergeItem(PmcData pmcData, InventoryMergeRequestData request, MongoId 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);
@@ -1012,8 +834,7 @@ 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);
@@ -1038,24 +859,18 @@ public class InventoryController(
}
// 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 DeletedItem { Id = sourceItem.Id }); // Inform client source item being deleted
output.ProfileChanges[sessionID].Items.DeletedItems.Add(new DeletedItem { 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);
@@ -1073,19 +888,10 @@ 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,
MongoId sessionID,
ItemEventRouterResponse output
)
public void SplitItem(PmcData pmcData, InventorySplitRequestData request, MongoId sessionID, ItemEventRouterResponse output)
{
// Changes made to result apply to character inventory
var inventoryItems = inventoryHelper.GetOwnerInventoryItems(
request,
request.NewItem.Value,
sessionID
);
var inventoryItems = inventoryHelper.GetOwnerInventoryItems(request, request.NewItem.Value, sessionID);
// Handle cartridge edge-case
if (request.Container.Location is null && request.Container.ContainerName == "cartridges")
@@ -1098,8 +904,7 @@ 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);
@@ -1148,12 +953,7 @@ 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,
MongoId sessionId,
ItemEventRouterResponse output
)
public void DiscardItem(PmcData pmcData, InventoryRemoveRequestData request, MongoId sessionId, ItemEventRouterResponse output)
{
if (request.FromOwner?.Type == "Mail")
{
@@ -38,9 +38,7 @@ public class LauncherController(
// Get all possible profile types + filter out any that are blacklisted
var profileTemplates = databaseService
.GetProfileTemplates()
.Where(profile =>
!_coreConfig.Features.CreateNewProfileTypesBlacklist.Contains(profile.Key)
)
.Where(profile => !_coreConfig.Features.CreateNewProfileTypesBlacklist.Contains(profile.Key))
.ToDictionary();
return new ConnectResponse
@@ -57,17 +55,12 @@ 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)
{
result.TryAdd(
profileKey,
serverLocalisationService.GetText(profile.DescriptionLocaleKey)
);
result.TryAdd(profileKey, serverLocalisationService.GetText(profile.DescriptionLocaleKey));
}
return result;
@@ -79,9 +72,7 @@ public class LauncherController(
/// <returns></returns>
public Info? Find(MongoId sessionId)
{
return saveServer.GetProfiles().TryGetValue(sessionId, out var profile)
? profile.ProfileInfo
: null;
return saveServer.GetProfiles().TryGetValue(sessionId, out var profile) ? profile.ProfileInfo : null;
}
/// <summary>
@@ -215,10 +206,7 @@ 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>
@@ -47,10 +47,7 @@ public class LauncherV2Controller(
foreach (var (templateName, template) in dbProfiles)
{
result.TryAdd(
templateName,
serverLocalisationService.GetText(template.DescriptionLocaleKey)
);
result.TryAdd(templateName, serverLocalisationService.GetText(template.DescriptionLocaleKey));
}
return result;
@@ -149,10 +146,7 @@ public class LauncherV2Controller(
/// <returns></returns>
public Dictionary<string, AbstractModMetadata> LoadedMods()
{
return loadedMods.ToDictionary(
sptMod => sptMod.ModMetadata.Name,
sptMod => sptMod.ModMetadata
);
return loadedMods.ToDictionary(sptMod => sptMod.ModMetadata.Name, sptMod => sptMod.ModMetadata);
}
/// <summary>
@@ -187,10 +181,7 @@ public class LauncherV2Controller(
{
foreach (var (sessionId, profile) in saveServer.GetProfiles())
{
if (
info.Username == profile.ProfileInfo!.Username
&& info.Password == profile.ProfileInfo.Password
)
if (info.Username == profile.ProfileInfo!.Username && info.Password == profile.ProfileInfo.Password)
{
return sessionId;
}
@@ -10,11 +10,7 @@ using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class LocationController(
ISptLogger<LocationController> logger,
DatabaseService databaseService,
AirdropService airdropService
)
public class LocationController(ISptLogger<LocationController> logger, DatabaseService databaseService, AirdropService airdropService)
{
/// <summary>
/// Handle client/locations
@@ -49,11 +45,7 @@ public class LocationController(
locationResult.Add(mapBase.IdField, mapBase);
}
return new LocationsGenerateAllResponse
{
Locations = locationResult,
Paths = locationsFromDb.Base!.Paths,
};
return new LocationsGenerateAllResponse { Locations = locationResult, Paths = locationsFromDb.Base!.Paths };
}
/// <summary>
@@ -104,9 +104,7 @@ public class MatchController(
// Set pmcs to difficulty set in pre-raid screen if override in bot config isnt enabled
if (!_pmcConfig.UseDifficultyOverride)
{
_pmcConfig.Difficulty = ConvertDifficultyDropdownIntoBotDifficulty(
request.WavesSettings.BotDifficulty.ToString()
);
_pmcConfig.Difficulty = ConvertDifficultyDropdownIntoBotDifficulty(request.WavesSettings.BotDifficulty.ToString());
}
}
@@ -132,10 +130,7 @@ public class MatchController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="request">Start raid request</param>
/// <returns>StartLocalRaidResponseData</returns>
public StartLocalRaidResponseData StartLocalRaid(
MongoId sessionId,
StartLocalRaidRequestData request
)
public StartLocalRaidResponseData StartLocalRaid(MongoId sessionId, StartLocalRaidRequestData request)
{
return locationLifecycleService.StartLocalRaid(sessionId, request);
}
@@ -16,11 +16,7 @@ public class NoteController(EventOutputHolder eventOutputHolder)
/// <param name="request">Add note request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse AddNote(
PmcData pmcData,
NoteActionRequest request,
MongoId sessionId
)
public ItemEventRouterResponse AddNote(PmcData pmcData, NoteActionRequest request, MongoId sessionId)
{
var newNote = new Note { Time = request.Note.Time, Text = request.Note.Text };
pmcData.Notes.DataNotes.Add(newNote);
@@ -34,11 +30,7 @@ public class NoteController(EventOutputHolder eventOutputHolder)
/// <param name="request">Edit note request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse EditNote(
PmcData pmcData,
NoteActionRequest request,
MongoId sessionId
)
public ItemEventRouterResponse EditNote(PmcData pmcData, NoteActionRequest request, MongoId sessionId)
{
var noteToEdit = pmcData.Notes.DataNotes[request.Index!.Value];
noteToEdit.Time = request.Note.Time;
@@ -53,11 +45,7 @@ public class NoteController(EventOutputHolder eventOutputHolder)
/// <param name="request">Delete note request</param>
/// <param name="sessionId">Session/Player id</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse DeleteNote(
PmcData pmcData,
NoteActionRequest request,
MongoId sessionId
)
public ItemEventRouterResponse DeleteNote(PmcData pmcData, NoteActionRequest request, MongoId sessionId)
{
pmcData.Notes?.DataNotes?.RemoveAt(request.Index!.Value);
@@ -8,11 +8,7 @@ using SPTarkov.Server.Core.Services;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class NotifierController(
HttpServerHelper httpServerHelper,
NotifierHelper notifierHelper,
NotificationService notificationService
)
public class NotifierController(HttpServerHelper httpServerHelper, NotifierHelper notifierHelper, NotificationService notificationService)
{
protected const int PollInterval = 300;
protected const int Timeout = 15000;
@@ -8,11 +8,7 @@ using SPTarkov.Server.Core.Services;
namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class PresetController(
ISptLogger<PresetController> logger,
PresetHelper presetHelper,
DatabaseService databaseService
)
public class PresetController(ISptLogger<PresetController> logger, PresetHelper presetHelper, DatabaseService databaseService)
{
/// <summary>
/// Keyed by item tpl, value = collection of preset ids
@@ -44,9 +44,7 @@ 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;
@@ -82,8 +80,7 @@ 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 ?? "",
@@ -127,10 +124,7 @@ 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,
MongoId sessionId
)
public virtual async ValueTask<string> CreateProfile(ProfileCreateRequestData request, MongoId sessionId)
{
return await createProfileService.CreateProfile(sessionId, request);
}
@@ -152,10 +146,7 @@ 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,
MongoId sessionId
)
public virtual NicknameValidationResult ValidateNickname(ValidateNicknameRequestData request, MongoId sessionId)
{
if (request.Nickname?.Length < 3)
{
@@ -177,15 +168,9 @@ public class ProfileController(
/// <param name="request">Change nickname request</param>
/// <param name="sessionId">Player id</param>
/// <returns></returns>
public virtual NicknameValidationResult ChangeNickname(
ProfileChangeNicknameRequestData request,
MongoId sessionId
)
public virtual NicknameValidationResult ChangeNickname(ProfileChangeNicknameRequestData request, MongoId sessionId)
{
var output = ValidateNickname(
new ValidateNicknameRequestData { Nickname = request.Nickname },
sessionId
);
var output = ValidateNickname(new ValidateNicknameRequestData { Nickname = request.Nickname }, sessionId);
if (output == NicknameValidationResult.Valid)
{
@@ -215,10 +200,7 @@ 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,
MongoId sessionID
)
public virtual List<SearchFriendResponse> SearchProfiles(SearchProfilesRequestData request, MongoId sessionID)
{
var result = new List<SearchFriendResponse>();
@@ -228,10 +210,7 @@ public class ProfileController(
foreach (var profile in allProfiles)
{
var pmcProfile = profile?.CharacterData?.PmcData;
if (
!pmcProfile?.Info?.LowerNickname?.Contains(request.Nickname.ToLowerInvariant())
?? false
)
if (!pmcProfile?.Info?.LowerNickname?.Contains(request.Nickname.ToLowerInvariant()) ?? false)
{
continue;
}
@@ -285,21 +264,13 @@ 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(
MongoId sessionId,
GetOtherProfileRequest request
)
public virtual GetOtherProfileResponse GetOtherProfile(MongoId 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);
}
@@ -312,16 +283,12 @@ 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 = profileToViewPmc.Inventory.Items.GetItemWithChildren(
rootItems.Id
);
var itemWithChildren = profileToViewPmc.Inventory.Items.GetItemWithChildren(rootItems.Id);
itemsToReturn.AddRange(itemWithChildren);
}
@@ -334,9 +301,7 @@ 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,
@@ -351,11 +316,7 @@ public class ProfileController(
Voice = profileToViewPmc.Customization.Voice,
},
Skills = profileToViewPmc.Skills,
Equipment = new OtherProfileEquipment
{
Id = profileToViewPmc.Inventory.Equipment,
Items = profileToViewPmc.Inventory.Items,
},
Equipment = new OtherProfileEquipment { Id = profileToViewPmc.Inventory.Equipment, Items = profileToViewPmc.Inventory.Items },
Achievements = profileToViewPmc.Achievements,
FavoriteItems = profileHelper.GetOtherProfileFavorites(profileToViewPmc),
PmcStats = new OtherProfileStats
@@ -50,19 +50,13 @@ 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,
MongoId sessionID
)
public ItemEventRouterResponse AcceptQuest(PmcData pmcData, AcceptQuestRequestData acceptedQuest, MongoId 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
@@ -74,11 +68,7 @@ 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);
}
@@ -88,18 +78,11 @@ public class QuestController(
if (questFromDb.Conditions?.AvailableForFinish is not null)
{
AddTaskConditionCountersToProfile(
questFromDb.Conditions.AvailableForFinish,
pmcData,
acceptedQuest.QuestId
);
AddTaskConditionCountersToProfile(questFromDb.Conditions.AvailableForFinish, pmcData, acceptedQuest.QuestId);
}
// Get messageId of text to send to player as text message in game
var messageId = questHelper.GetMessageIdForQuestStart(
questFromDb.StartedMessageText,
questFromDb.Description
);
var messageId = questHelper.GetMessageIdForQuestStart(questFromDb.StartedMessageText, questFromDb.Description);
// Apply non-item rewards to profile + return item rewards
var startedQuestRewardItems = questRewardHelper.ApplyQuestReward(
@@ -117,16 +100,11 @@ 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
var newlyAccessibleQuests = questHelper.GetNewlyAccessibleQuestsWhenStartingQuest(
acceptedQuest.QuestId,
sessionID
);
var newlyAccessibleQuests = questHelper.GetNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.QuestId, sessionID);
if (newlyAccessibleQuests.Count > 0)
{
acceptQuestResponse.ProfileChanges[sessionID].Quests.AddRange(newlyAccessibleQuests);
@@ -142,17 +120,9 @@ public class QuestController(
/// <param name="questConditionsToAdd">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(
IEnumerable<QuestCondition> questConditionsToAdd,
PmcData pmcData,
MongoId questId
)
protected void AddTaskConditionCountersToProfile(IEnumerable<QuestCondition> questConditionsToAdd, PmcData pmcData, MongoId questId)
{
foreach (
var condition in questConditionsToAdd.Where(condition =>
condition.ConditionType == "SellItemToTrader"
)
)
foreach (var condition in questConditionsToAdd.Where(condition => condition.ConditionType == "SellItemToTrader"))
{
if (pmcData.TaskConditionCounters.TryGetValue(condition.Id, out _))
{
@@ -185,11 +155,7 @@ 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,
MongoId sessionId
)
public ItemEventRouterResponse CompleteQuest(PmcData pmcData, CompleteQuestRequestData request, MongoId sessionId)
{
return questHelper.CompleteQuest(pmcData, request, sessionId);
}
@@ -202,11 +168,7 @@ 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,
MongoId sessionID
)
public ItemEventRouterResponse HandoverQuest(PmcData pmcData, HandoverQuestRequestData request, MongoId sessionID)
{
var quest = questHelper.GetQuestFromDb(request.QuestId, pmcData);
HashSet<string> handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
@@ -217,11 +179,7 @@ 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))
@@ -261,51 +219,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)
)
)
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,
matchingItemInProfile,
handoverRequirements,
output
);
return ShowQuestItemHandoverMatchError(request, matchingItemInProfile, handoverRequirements, output);
}
// 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)
{
// Remove single item with no children
questHelper.ChangeItemStack(
pmcData,
itemHandover.Id,
(int)(itemHandover.Count - itemCountToRemove),
sessionID,
output
);
questHelper.ChangeItemStack(pmcData, itemHandover.Id, (int)(itemHandover.Count - itemCountToRemove), sessionID, output);
// Complete - handedInCount == totalItemCountToRemove
if (Math.Abs(totalItemCountToRemove - handedInCount) < 0.01)
@@ -316,15 +249,11 @@ public class QuestController(
else
{
// Remove item with children
var toRemove = pmcData
.Inventory.Items.GetItemWithChildrenTpls(itemHandover.Id)
.ToHashSet();
var toRemove = pmcData.Inventory.Items.GetItemWithChildrenTpls(itemHandover.Id).ToHashSet();
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 DeletedItem { Id = itemHandover.Id });
output.ProfileChanges[sessionID].Items.DeletedItems.Add(new DeletedItem { Id = itemHandover.Id });
// Important: loop backward when removing items from the array we're looping on
while (index-- > 0)
@@ -339,9 +268,7 @@ public class QuestController(
// element `location` properties of the parent so they are sequential, while retaining order
if (removedItem.Location?.GetType() == typeof(int))
{
var childItems = pmcData.Inventory.Items.GetItemWithChildren(
removedItem.ParentId
);
var childItems = pmcData.Inventory.Items.GetItemWithChildren(removedItem.ParentId);
childItems.RemoveAt(0); // Remove the parent
// Sort by the current `location` and update
@@ -357,12 +284,7 @@ public class QuestController(
}
}
UpdateProfileTaskConditionCounterValue(
pmcData,
request.ConditionId,
request.QuestId,
totalItemCountToRemove
);
UpdateProfileTaskConditionCounterValue(pmcData, request.ConditionId, request.QuestId, totalItemCountToRemove);
return output;
}
@@ -426,12 +348,7 @@ 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,
MongoId conditionId,
MongoId questId,
double counterValue
)
protected void UpdateProfileTaskConditionCounterValue(PmcData pmcData, MongoId conditionId, MongoId questId, double counterValue)
{
if (pmcData.TaskConditionCounters.GetValueOrDefault(conditionId) != null)
{
@@ -62,8 +62,7 @@ public class RagfairController(
var pmcProfile = profile.CharacterData.PmcData;
if (
pmcProfile.RagfairInfo is not null
&& pmcProfile.Info.Level
>= databaseService.GetGlobals().Configuration.RagFair.MinUserLevel
&& pmcProfile.Info.Level >= databaseService.GetGlobals().Configuration.RagFair.MinUserLevel
)
{
ragfairOfferHelper.ProcessOffersOnProfile(sessionId);
@@ -91,21 +90,12 @@ public class RagfairController(
};
// 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
@@ -217,10 +207,7 @@ public class RagfairController(
var assortId = offerRootItem.Id;
// No trader found in profile, create a blank record for them
var existsInProfile = !fullProfile.TraderPurchases.TryAdd(
offer.User.Id,
new Dictionary<MongoId, TraderPurchaseData>()
);
var existsInProfile = !fullProfile.TraderPurchases.TryAdd(offer.User.Id, new Dictionary<MongoId, TraderPurchaseData>());
if (!existsInProfile)
{
// Not purchased by player before, use value from assort data
@@ -266,16 +253,10 @@ 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<MongoId, int> GetSpecificCategories(
PmcData pmcProfile,
SearchRequestData searchRequest,
List<RagfairOffer> offers
)
protected Dictionary<MongoId, int> GetSpecificCategories(PmcData pmcProfile, SearchRequestData searchRequest, List<RagfairOffer> offers)
{
// Linked/required search categories
var playerHasFleaUnlocked =
pmcProfile.Info.Level
>= databaseService.GetGlobals().Configuration.RagFair.MinUserLevel;
var playerHasFleaUnlocked = pmcProfile.Info.Level >= databaseService.GetGlobals().Configuration.RagFair.MinUserLevel;
List<RagfairOffer> offerPool;
if (IsLinkedSearch(searchRequest) || IsRequiredSearch(searchRequest))
{
@@ -297,11 +278,7 @@ public class RagfairController(
return [];
}
return ragfairServer.GetAllActiveCategories(
playerHasFleaUnlocked,
searchRequest,
offerPool
);
return ragfairServer.GetAllActiveCategories(playerHasFleaUnlocked, searchRequest, offerPool);
}
/// <summary>
@@ -342,12 +319,7 @@ public class RagfairController(
// 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 != null && !searchRequest.NeededSearchId.Value.IsEmpty())
@@ -356,12 +328,7 @@ public class RagfairController(
}
// Searching for general items
return ragfairOfferHelper.GetValidOffers(
searchRequest,
itemsToAdd,
traderAssorts,
pmcProfile
);
return ragfairOfferHelper.GetValidOffers(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
}
/// <summary>
@@ -370,10 +337,7 @@ 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);
@@ -411,11 +375,7 @@ public class RagfairController(
};
}
protected double GetAveragePriceFromOffers(
IEnumerable<RagfairOffer> offers,
MinMax<double> minMax,
bool ignoreTraderOffers
)
protected double GetAveragePriceFromOffers(IEnumerable<RagfairOffer> offers, MinMax<double> minMax, bool ignoreTraderOffers)
{
var sum = 0d;
var totalOfferCount = 0;
@@ -434,9 +394,7 @@ public class RagfairController(
}
// Figure out how many items the requirementsCost is applying to, and what the per-item price is
var offerItemCount = offer.SellInOnePiece.GetValueOrDefault(false)
? offer.Items.First().Upd?.StackObjectsCount ?? 1
: 1;
var offerItemCount = offer.SellInOnePiece.GetValueOrDefault(false) ? offer.Items.First().Upd?.StackObjectsCount ?? 1 : 1;
var perItemPrice = offer.RequirementsCost / offerItemCount;
// Handle min/max calculations based on the per-item price
@@ -468,30 +426,20 @@ 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,
MongoId sessionID
)
public ItemEventRouterResponse AddPlayerOffer(PmcData pmcData, AddOfferRequestData offerRequest, MongoId 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);
if (typeOfOffer == FleaOfferType.UNKNOWN)
{
return httpResponseUtil.AppendErrorToOutput(
output,
$"Unknown offer type: {typeOfOffer}, cannot list item on flea"
);
return httpResponseUtil.AppendErrorToOutput(output, $"Unknown offer type: {typeOfOffer}, cannot list item on flea");
}
switch (typeOfOffer)
@@ -504,10 +452,7 @@ public class RagfairController(
return CreatePackOffer(sessionID, offerRequest, fullProfile, output);
case FleaOfferType.UNKNOWN:
default:
return httpResponseUtil.AppendErrorToOutput(
output,
$"Unknown offer type: {typeOfOffer}, cannot list item on flea"
);
return httpResponseUtil.AppendErrorToOutput(output, $"Unknown offer type: {typeOfOffer}, cannot list item on flea");
}
}
@@ -527,9 +472,7 @@ 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;
}
@@ -613,9 +556,7 @@ public class RagfairController(
// Average offer price for single item (or whole weapon)
// MUST occur prior to CreatePlayerOffer(), otherwise offer ends up in averages calculation
var averages = GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData { TemplateId = firstInventoryItem.Template }
);
var averages = GetItemMinAvgMaxFleaPriceValues(new GetMarketPriceRequestData { TemplateId = firstInventoryItem.Template });
// Create flea object
var offer = CreatePlayerOffer(sessionID, offerRequest.Requirements, inventoryItems, false);
@@ -625,12 +566,7 @@ public class RagfairController(
// 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;
}
@@ -645,11 +581,7 @@ public class RagfairController(
var playerListedPriceInRub = CalculateRequirementsPriceInRub(offerRequest.Requirements);
// Roll sale chance
var sellChancePercent = ragfairSellHelper.CalculateSellChance(
averageOfferPrice.Value,
playerListedPriceInRub,
qualityMultiplier
);
var sellChancePercent = ragfairSellHelper.CalculateSellChance(averageOfferPrice.Value, playerListedPriceInRub, qualityMultiplier);
// Create array of sell times for items listed
offer.SellResults = ragfairSellHelper.RollForSale(sellChancePercent, (int)stackCountTotal);
@@ -707,9 +639,7 @@ public class RagfairController(
// multi-offers are all the same item,
// Get first item and its children and use as template
var firstInventoryItemAndChildren = pmcData.Inventory.Items.GetItemWithChildren(
offerRequest.Items.FirstOrDefault()
);
var firstInventoryItemAndChildren = pmcData.Inventory.Items.GetItemWithChildren(offerRequest.Items.FirstOrDefault());
// Find items to be listed on flea (+ children) from player inventory
var result = GetItemsToListOnFleaFromInventory(pmcData, offerRequest.Items);
@@ -729,29 +659,17 @@ public class RagfairController(
// Single price for an item
// MUST occur prior to CreatePlayerOffer(), otherwise offer ends up in averages calculation
var averages = GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData { TemplateId = firstInventoryItem.Template }
);
var averages = GetItemMinAvgMaxFleaPriceValues(new GetMarketPriceRequestData { TemplateId = firstInventoryItem.Template });
var singleItemPrice = averages.Avg;
// Create flea object
var offer = CreatePlayerOffer(
sessionID,
offerRequest.Requirements,
firstInventoryItemAndChildren,
true
);
var offer = CreatePlayerOffer(sessionID, offerRequest.Requirements, firstInventoryItemAndChildren, 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
// 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;
}
@@ -773,11 +691,7 @@ 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)
@@ -831,10 +745,7 @@ public class RagfairController(
// 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);
}
@@ -846,9 +757,7 @@ public class RagfairController(
// Average offer price for single item (or whole weapon)
// MUST occur prior to CreatePlayerOffer(), otherwise offer ends up in averages calculation
var averages = GetItemMinAvgMaxFleaPriceValues(
new GetMarketPriceRequestData { TemplateId = firstItemToSell.Template }
);
var averages = GetItemMinAvgMaxFleaPriceValues(new GetMarketPriceRequestData { TemplateId = firstItemToSell.Template });
var averageOfferPriceSingleItem = averages.Avg;
// Checks are done, create offer
@@ -866,12 +775,7 @@ public class RagfairController(
var qualityMultiplier = itemHelper.GetItemQualityModifierForItems(offer.Items, true);
// 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;
}
@@ -941,9 +845,7 @@ public class RagfairController(
{
// Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate)
var requestRootItemId = offerRequest.Items.FirstOrDefault();
var storedClientTaxValue = ragfairTaxService.GetStoredClientOfferTaxValueById(
requestRootItemId
);
var storedClientTaxValue = ragfairTaxService.GetStoredClientOfferTaxValueById(requestRootItemId);
var tax = storedClientTaxValue is not null
? storedClientTaxValue.Fee
: ragfairTaxService.CalculateTax(
@@ -956,9 +858,7 @@ 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
@@ -968,10 +868,7 @@ public class RagfairController(
paymentService.PayMoney(pmcData, buyTradeRequest, sessionId, output);
if (output.Warnings.Count > 0)
{
httpResponseUtil.AppendErrorToOutput(
output,
localisationService.GetText("ragfair-unable_to_pay_commission_fee", tax)
);
httpResponseUtil.AppendErrorToOutput(output, localisationService.GetText("ragfair-unable_to_pay_commission_fee", tax));
return true; // Fee failed
}
@@ -986,12 +883,7 @@ 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(
MongoId sessionId,
List<Requirement> requirements,
List<Item> items,
bool sellInOnePiece
)
protected RagfairOffer CreatePlayerOffer(MongoId sessionId, List<Requirement> requirements, List<Item> items, bool sellInOnePiece)
{
const int loyalLevel = 1;
var formattedItems = items.Select(item =>
@@ -1039,19 +931,14 @@ public class RagfairController(
{
return requirements.Sum(requirement =>
{
if (
requirement.Template.IsEmpty()
|| !requirement.Count.HasValue
|| requirement.Count == 0
)
if (requirement.Template.IsEmpty() || !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;
: itemHelper.GetDynamicItemPrice(requirement.Template).Value * requirement.Count.Value;
});
}
@@ -1075,17 +962,10 @@ public class RagfairController(
var rootItem = pmcData.Inventory?.Items?.FirstOrDefault(i => i.Id == itemId);
if (rootItem is null)
{
errorMessage = localisationService.GetText(
"ragfair-unable_to_find_item_in_inventory",
new { id = itemId.ToString() }
);
errorMessage = localisationService.GetText("ragfair-unable_to_find_item_in_inventory", new { id = itemId.ToString() });
logger.Error(errorMessage);
return new GetItemsToListOnFleaFromInventoryResult
{
Items = itemsToReturn,
ErrorMessage = errorMessage,
};
return new GetItemsToListOnFleaFromInventoryResult { Items = itemsToReturn, ErrorMessage = errorMessage };
}
rootItem.FixItemStackCount();
@@ -1095,19 +975,13 @@ public class RagfairController(
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
{
Items = itemsToReturn,
ErrorMessage = errorMessage,
};
return new GetItemsToListOnFleaFromInventoryResult { Items = itemsToReturn, ErrorMessage = errorMessage };
}
/// <summary>
@@ -1126,10 +1000,7 @@ public class RagfairController(
if (playerProfileOffers is null)
{
logger.Warning(
localisationService.GetText(
"ragfair-unable_to_remove_offer_not_found_in_profile",
new { profileId = sessionId, offerId }
)
localisationService.GetText("ragfair-unable_to_remove_offer_not_found_in_profile", new { profileId = sessionId, offerId })
);
pmcData.RagfairInfo.Offers = [];
@@ -1138,14 +1009,9 @@ public class RagfairController(
var playerOffer = playerProfileOffers?.FirstOrDefault(x => x.Id == offerId);
if (playerOffer is null)
{
logger.Error(
localisationService.GetText("ragfair-offer_not_found_in_profile", new { offerId })
);
logger.Error(localisationService.GetText("ragfair-offer_not_found_in_profile", new { offerId }));
return httpResponseUtil.AppendErrorToOutput(
output,
localisationService.GetText("ragfair-offer_not_found_in_profile_short")
);
return httpResponseUtil.AppendErrorToOutput(output, localisationService.GetText("ragfair-offer_not_found_in_profile_short"));
}
// Only reduce time to end if time remaining is greater than what we would set it to
@@ -1157,9 +1023,7 @@ public class RagfairController(
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;
}
@@ -1170,10 +1034,7 @@ 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,
MongoId sessionId
)
public ItemEventRouterResponse ExtendOffer(ExtendOfferRequestData extendRequest, MongoId sessionId)
{
var output = eventOutputHolder.GetOutput(sessionId);
@@ -1184,16 +1045,8 @@ public class RagfairController(
if (playerOfferIndex == -1)
{
logger.Warning(
localisationService.GetText(
"ragfair-offer_not_found_in_profile",
new { offerId = extendRequest.OfferId }
)
);
return httpResponseUtil.AppendErrorToOutput(
output,
localisationService.GetText("ragfair-offer_not_found_in_profile_short")
);
logger.Warning(localisationService.GetText("ragfair-offer_not_found_in_profile", new { offerId = extendRequest.OfferId }));
return httpResponseUtil.AppendErrorToOutput(output, localisationService.GetText("ragfair-offer_not_found_in_profile_short"));
}
var playerOffer = playerOffers[playerOfferIndex];
@@ -1205,8 +1058,7 @@ 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(
@@ -1221,10 +1073,7 @@ public class RagfairController(
paymentService.PayMoney(pmcData, request, sessionId, output);
if (output.Warnings.Count > 0)
{
return httpResponseUtil.AppendErrorToOutput(
output,
localisationService.GetText("ragfair-unable_to_pay_commission_fee")
);
return httpResponseUtil.AppendErrorToOutput(output, localisationService.GetText("ragfair-unable_to_pay_commission_fee"));
}
}
@@ -1240,19 +1089,13 @@ 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
{
TransactionId = "ragfair",
Action = "TradingConfirm",
SchemeItems =
[
new IdWithCount { Id = currency.GetCurrencyTpl(), Count = Math.Round(value) },
],
SchemeItems = [new IdWithCount { Id = currency.GetCurrencyTpl(), Count = Math.Round(value) }],
Type = "",
ItemId = MongoId.Empty(),
Count = 0,
@@ -19,32 +19,16 @@ public class RepairController(EventOutputHolder eventOutputHolder, RepairService
/// <param name="request">endpoint request data</param>
/// <param name="pmcData">player profile</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse TraderRepair(
MongoId sessionID,
TraderRepairActionDataRequest request,
PmcData pmcData
)
public ItemEventRouterResponse TraderRepair(MongoId sessionID, TraderRepairActionDataRequest request, PmcData pmcData)
{
var output = eventOutputHolder.GetOutput(sessionID);
// find the item to repair
foreach (var repairItem in request.RepairItems)
{
var repairDetails = repairService.RepairItemByTrader(
sessionID,
pmcData,
repairItem,
request.TraderId
);
var repairDetails = repairService.RepairItemByTrader(sessionID, pmcData, repairItem, request.TraderId);
repairService.PayForRepair(
sessionID,
pmcData,
repairItem.Id,
repairDetails.RepairCost.Value,
request.TraderId,
output
);
repairService.PayForRepair(sessionID, pmcData, repairItem.Id, repairDetails.RepairCost.Value, request.TraderId, output);
if (output.Warnings?.Count > 0)
{
@@ -69,22 +53,12 @@ public class RepairController(EventOutputHolder eventOutputHolder, RepairService
/// <param name="body">endpoint request data</param>
/// <param name="pmcData">player profile</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse RepairWithKit(
MongoId sessionId,
RepairActionDataRequest body,
PmcData pmcData
)
public ItemEventRouterResponse RepairWithKit(MongoId sessionId, RepairActionDataRequest body, PmcData pmcData)
{
var output = eventOutputHolder.GetOutput(sessionId);
// repair item
var repairDetails = repairService.RepairItemByKit(
sessionId,
pmcData,
body.RepairKitsInfo,
body.Target.Value,
output
);
var repairDetails = repairService.RepairItemByKit(sessionId, pmcData, body.RepairKitsInfo, body.Target.Value, output);
repairService.AddBuffToItem(repairDetails, pmcData);
@@ -46,12 +46,7 @@ public class RepeatableQuestController(
ICloner cloner
)
{
protected static readonly FrozenSet<string> _questTypes =
[
"PickUp",
"Exploration",
"Elimination",
];
protected static readonly FrozenSet<string> _questTypes = ["PickUp", "Exploration", "Elimination"];
protected readonly QuestConfig QuestConfig = configServer.GetConfig<QuestConfig>();
/// <summary>
@@ -63,18 +58,10 @@ 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,
MongoId sessionID
)
public ItemEventRouterResponse AcceptRepeatableQuest(PmcData pmcData, AcceptQuestRequestData acceptedQuest, MongoId sessionID)
{
// Create and store quest status object inside player profile
var newRepeatableQuest = questHelper.GetQuestReadyForProfile(
pmcData,
QuestStatusEnum.Started,
acceptedQuest
);
var newRepeatableQuest = questHelper.GetQuestReadyForProfile(pmcData, QuestStatusEnum.Started, acceptedQuest);
pmcData.Quests.Add(newRepeatableQuest);
// Look for the generated quest cache in profile.RepeatableQuests
@@ -82,22 +69,14 @@ public class RepeatableQuestController(
if (repeatableQuestProfile is null)
{
logger.Error(
serverLocalisationService.GetText(
"repeatable-accepted_repeatable_quest_not_found_in_active_quests",
acceptedQuest.QuestId
)
serverLocalisationService.GetText("repeatable-accepted_repeatable_quest_not_found_in_active_quests", acceptedQuest.QuestId)
);
throw new Exception(
serverLocalisationService.GetText("repeatable-unable_to_accept_quest_see_log")
);
throw new Exception(serverLocalisationService.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);
@@ -117,11 +96,7 @@ 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,
MongoId sessionID
)
public ItemEventRouterResponse ChangeRepeatableQuest(PmcData pmcData, RepeatableQuestChangeRequest changeRequest, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
@@ -134,9 +109,7 @@ public class RepeatableQuestController(
if (repeatables.RepeatableType is null || repeatables.Quest is null)
{
// Unable to find quest being replaced
var message = serverLocalisationService.GetText(
"quest-unable_to_find_repeatable_to_replace"
);
var message = serverLocalisationService.GetText("quest-unable_to_find_repeatable_to_replace");
logger.Error(message);
return httpResponseUtil.AppendErrorToOutput(output, message);
@@ -154,17 +127,13 @@ public class RepeatableQuestController(
.ToList();
// Save for later cost calculations
var previousChangeRequirement = cloner.Clone(
repeatablesOfTypeInProfile.ChangeRequirement[changeRequest.QuestId]
);
var previousChangeRequirement = cloner.Clone(repeatablesOfTypeInProfile.ChangeRequirement[changeRequest.QuestId]);
// Delete the replaced quest change requirement data as we're going to add new data below
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
if (repeatableConfig?.KeepDailyQuestTypeOnReplacement is not null)
@@ -173,16 +142,8 @@ 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 newRepeatableQuest = AttemptToGenerateRepeatableQuest(
sessionID,
pmcData,
allowedQuestTypes,
repeatableConfig
);
var allowedQuestTypes = GenerateQuestPool(repeatableConfig, pmcData.Info.Level.GetValueOrDefault(1));
var newRepeatableQuest = AttemptToGenerateRepeatableQuest(sessionID, pmcData, allowedQuestTypes, repeatableConfig);
if (newRepeatableQuest is null)
{
// Unable to find quest being replaced
@@ -218,11 +179,7 @@ public class RepeatableQuestController(
};
// Check if we should charge player for replacing quest
var isFreeToReplace = UseFreeRefreshIfAvailable(
fullProfile,
repeatablesOfTypeInProfile,
repeatableTypeLower
);
var isFreeToReplace = UseFreeRefreshIfAvailable(fullProfile, repeatablesOfTypeInProfile, repeatableTypeLower);
if (!isFreeToReplace)
{
// Reduce standing with trader for not doing their quest
@@ -233,17 +190,8 @@ public class RepeatableQuestController(
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;
@@ -302,11 +250,7 @@ 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)
@@ -318,9 +262,7 @@ public class RepeatableQuestController(
}
// Only certain game versions have access to free refreshes
var hasAccessToFreeRefreshSystem = profileHelper.HasAccessToRepeatableFreeRefreshSystem(
fullProfile.CharacterData.PmcData
);
var hasAccessToFreeRefreshSystem = profileHelper.HasAccessToRepeatableFreeRefreshSystem(fullProfile.CharacterData.PmcData);
// If the player has access and available refreshes:
if (hasAccessToFreeRefreshSystem)
@@ -345,10 +287,7 @@ 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
@@ -402,12 +341,7 @@ public class RepeatableQuestController(
if (attempts > maxAttempts)
{
logger.Error(
serverLocalisationService.GetText(
"quest-repeatable_generation_failed_please_report",
attempts
)
);
logger.Error(serverLocalisationService.GetText("quest-repeatable_generation_failed_please_report", attempts));
}
return newRepeatableQuest;
@@ -446,9 +380,7 @@ public class RepeatableQuestController(
var traderId = randomUtil.DrawRandomFromList(traders).FirstOrDefault();
if (traderId.IsEmpty())
{
logger.Error(
serverLocalisationService.GetText("repeatable-unable_to_find_trader_in_pool")
);
logger.Error(serverLocalisationService.GetText("repeatable-unable_to_find_trader_in_pool"));
return null;
}
@@ -460,34 +392,10 @@ public class RepeatableQuestController(
return questType switch
{
"Elimination" => eliminationQuestGenerator.Generate(
sessionId,
pmcLevel,
traderId,
questTypePool,
repeatableConfig
),
"Completion" => completionQuestGenerator.Generate(
sessionId,
pmcLevel,
traderId,
questTypePool,
repeatableConfig
),
"Exploration" => explorationQuestGenerator.Generate(
sessionId,
pmcLevel,
traderId,
questTypePool,
repeatableConfig
),
"Pickup" => pickupQuestGenerator.Generate(
sessionId,
pmcLevel,
traderId,
questTypePool,
repeatableConfig
),
"Elimination" => eliminationQuestGenerator.Generate(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
"Completion" => completionQuestGenerator.Generate(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
"Exploration" => explorationQuestGenerator.Generate(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
"Pickup" => pickupQuestGenerator.Generate(sessionId, pmcLevel, traderId, questTypePool, repeatableConfig),
_ => null,
};
}
@@ -500,18 +408,12 @@ public class RepeatableQuestController(
protected void RemoveQuestFromProfile(SptProfile fullProfile, MongoId 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)
{
questHelper.FindAndRemoveQuestFromArrayIfExists(
questToReplaceId,
fullProfile.CharacterData.ScavData.Quests
);
questHelper.FindAndRemoveQuestFromArrayIfExists(questToReplaceId, fullProfile.CharacterData.ScavData.Quests);
}
}
@@ -526,20 +428,14 @@ 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
{
continue;
}
return new GetRepeatableByIdResult
{
Quest = questToReplace,
RepeatableType = repeatablesInProfile,
};
return new GetRepeatableByIdResult { Quest = questToReplace, RepeatableType = repeatablesInProfile };
}
return null;
@@ -577,10 +473,7 @@ 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.ToLowerInvariant();
var canAccessRepeatables = CanProfileAccessRepeatableQuests(repeatableConfig, pmcData);
@@ -620,10 +513,7 @@ 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++)
@@ -642,9 +532,7 @@ public class RepeatableQuestController(
lifeline++;
if (lifeline > 10)
{
logger.Error(
"We were stuck in repeatable quest generation. This should never happen. Please report"
);
logger.Error("We were stuck in repeatable quest generation. This should never happen. Please report");
break;
}
@@ -675,9 +563,7 @@ 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
}
);
}
@@ -709,15 +595,10 @@ 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);
if (repeatableQuestDetails is null)
@@ -755,16 +636,10 @@ 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 == PlayerGroup.Pmc
&& !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)
)
if (repeatableConfig.Side == PlayerGroup.Pmc && !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig))
{
return false;
}
@@ -789,10 +664,7 @@ 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;
}
@@ -804,11 +676,7 @@ 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>
@@ -816,17 +684,12 @@ 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;
@@ -884,14 +747,8 @@ 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>(
cloner,
eliminationConfig.Targets
);
var eliminationConfig = repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(cloner, eliminationConfig.Targets);
// Populate Elimination quest targets and their locations
foreach (var target in targetsConfig)
@@ -899,10 +756,7 @@ public class RepeatableQuestController(
// Target is boss
if (target.Data?.IsBoss ?? false)
{
questPool.Pool.Elimination.Targets.Add(
target.Key,
new TargetLocation { Locations = ["any"] }
);
questPool.Pool.Elimination.Targets.Add(target.Key, new TargetLocation { Locations = ["any"] });
continue;
}
@@ -916,10 +770,7 @@ public class RepeatableQuestController(
questPool.Pool.Elimination.Targets.Add(
target.Key,
new TargetLocation
{
Locations = allowedLocations.Select(x => x.ToString()).ToList(),
}
new TargetLocation { Locations = allowedLocations.Select(x => x.ToString()).ToList() }
);
}
@@ -938,18 +789,9 @@ public class RepeatableQuestController(
Types = cloner.Clone(repeatableConfig.Types)!,
Pool = new QuestPool
{
Exploration = new ExplorationPool
{
Locations = new Dictionary<ELocationName, List<string>>(),
},
Elimination = new EliminationPool
{
Targets = new Dictionary<string, TargetLocation>(),
},
Pickup = new ExplorationPool
{
Locations = new Dictionary<ELocationName, List<string>>(),
},
Exploration = new ExplorationPool { Locations = new Dictionary<ELocationName, List<string>>() },
Elimination = new EliminationPool { Targets = new Dictionary<string, TargetLocation>() },
Pickup = new ExplorationPool { Locations = new Dictionary<ELocationName, List<string>>() },
},
};
}
@@ -971,10 +813,7 @@ public class RepeatableQuestController(
// Add elite bonus to daily quests
if (
string.Equals(repeatableConfig.Name, "daily", StringComparison.OrdinalIgnoreCase)
&& profileHelper.HasEliteSkillLevel(
SkillTypes.Charisma,
fullProfile.CharacterData.PmcData
)
&& profileHelper.HasEliteSkillLevel(SkillTypes.Charisma, fullProfile.CharacterData.PmcData)
)
// Elite charisma skill gives extra daily quest(s)
{
@@ -984,8 +823,7 @@ public class RepeatableQuestController(
}
// 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;
}
@@ -46,11 +46,7 @@ public class TradeController(
/// <param name="request"></param>
/// <param name="sessionID">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse ConfirmTrading(
PmcData pmcData,
ProcessBaseTradeRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse ConfirmTrading(PmcData pmcData, ProcessBaseTradeRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
@@ -76,11 +72,7 @@ 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>
@@ -90,11 +82,7 @@ public class TradeController(
/// <param name="request"></param>
/// <param name="sessionID">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse ConfirmRagfairTrading(
PmcData pmcData,
ProcessRagfairTradeRequestData request,
MongoId sessionID
)
public ItemEventRouterResponse ConfirmRagfairTrading(PmcData pmcData, ProcessRagfairTradeRequestData request, MongoId sessionID)
{
var output = eventOutputHolder.GetOutput(sessionID);
@@ -103,11 +91,7 @@ public class TradeController(
var fleaOffer = ragfairServer.GetOffer(offer.Id);
if (fleaOffer is null)
{
return httpResponseUtil.AppendErrorToOutput(
output,
$"Offer with ID {offer.Id} not found",
BackendErrorCodes.OfferNotFound
);
return httpResponseUtil.AppendErrorToOutput(output, $"Offer with ID {offer.Id} not found", BackendErrorCodes.OfferNotFound);
}
if (offer.Count == 0)
@@ -116,11 +100,7 @@ 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 (fleaOffer.IsTraderOffer())
@@ -168,11 +148,7 @@ public class TradeController(
logger.Debug(errorMessage);
}
httpResponseUtil.AppendErrorToOutput(
output,
errorMessage,
BackendErrorCodes.RagfairUnavailable
);
httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.RagfairUnavailable);
return;
}
@@ -188,13 +164,7 @@ public class TradeController(
SchemeId = 0,
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);
@@ -228,13 +198,7 @@ public class TradeController(
};
// 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;
@@ -274,11 +238,7 @@ public class TradeController(
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse SellScavItemsToFence(
PmcData pmcData,
SellScavItemsToFenceRequestData request,
MongoId sessionId
)
public ItemEventRouterResponse SellScavItemsToFence(PmcData pmcData, SellScavItemsToFenceRequestData request, MongoId sessionId)
{
var output = eventOutputHolder.GetOutput(sessionId);
@@ -316,11 +276,7 @@ public class TradeController(
sessionId,
trader,
MessageType.MessageWithItems,
randomUtil.GetArrayValue(
databaseService.GetTrader(trader).Dialogue.TryGetValue("soldItems", out var items)
? items
: []
),
randomUtil.GetArrayValue(databaseService.GetTrader(trader).Dialogue.TryGetValue("soldItems", out var items) ? items : []),
currencyReward.SelectMany(x => x).ToList(),
timeUtil.GetHoursAsSeconds(72)
);
@@ -347,25 +303,14 @@ 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
)
)
)
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;
@@ -83,10 +83,7 @@ public class TraderController(
foreach (var kvp in trader.Assort?.BarterScheme)
{
var barterSchemeItem = kvp.Value.FirstOrDefault()?.FirstOrDefault();
if (
barterSchemeItem?.Template != null
&& paymentHelper.IsMoneyTpl(barterSchemeItem.Template)
)
if (barterSchemeItem?.Template != null && paymentHelper.IsMoneyTpl(barterSchemeItem.Template))
{
barterSchemeItem.Count += Math.Round(barterSchemeItem?.Count * multiplier ?? 0D, 2);
}
@@ -50,11 +50,7 @@ public class WeatherController(
/// <returns>GetLocalWeatherResponseData</returns>
public GetLocalWeatherResponseData GenerateLocal(MongoId sessionId)
{
var result = new GetLocalWeatherResponseData
{
Season = seasonalEventService.GetActiveWeatherSeason(),
Weather = [],
};
var result = new GetLocalWeatherResponseData { Season = seasonalEventService.GetActiveWeatherSeason(), Weather = [] };
result.Weather.AddRange(raidWeatherService.GetUpcomingWeather());
@@ -18,11 +18,7 @@ public class WishlistController(EventOutputHolder eventOutputHolder)
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse AddToWishList(
PmcData pmcData,
AddToWishlistRequest request,
MongoId sessionId
)
public ItemEventRouterResponse AddToWishList(PmcData pmcData, AddToWishlistRequest request, MongoId sessionId)
{
pmcData.WishList ??= new DictionaryOrList<MongoId, int>(new Dictionary<MongoId, int>(), []);
foreach (var item in request.Items)
@@ -40,11 +36,7 @@ public class WishlistController(EventOutputHolder eventOutputHolder)
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse RemoveFromWishList(
PmcData pmcData,
RemoveFromWishlistRequest request,
MongoId sessionId
)
public ItemEventRouterResponse RemoveFromWishList(PmcData pmcData, RemoveFromWishlistRequest request, MongoId sessionId)
{
foreach (var itemId in request.Items)
{
@@ -61,11 +53,7 @@ public class WishlistController(EventOutputHolder eventOutputHolder)
/// <param name="request"></param>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public ItemEventRouterResponse ChangeWishListItemCategory(
PmcData pmcData,
ChangeWishlistItemCategoryRequest request,
MongoId sessionId
)
public ItemEventRouterResponse ChangeWishListItemCategory(PmcData pmcData, ChangeWishlistItemCategoryRequest request, MongoId sessionId)
{
pmcData.WishList.Dictionary[request.Item] = request.Category.Value;