d2e2f04c93
* Fix exception sometimes thrown on save
- Switch back from File.Rename to File.Move, as Rename is throwing exceptions on some users systems
* Change BTR skin to tarcola during Christmas event
* Added comment
* Remove unused using
* Add wipe Response model
* formatting and add Wipe Endpoint to V2
* Format Style Fixes
* Merge pull request #669 from sp-tarkov/Assembly-ref-validation
Validate core assembly reference when loading mods
* removed zombies from customs and interchange + increased infection across other maps that have zombie kill quests
* Don't apply hostility changes to maps without zombies during halloween
`ReplaceBotHostiltiy` has optional map whitelist param
* Updated hostility values for maps with infection:
bosses = hostile to player not to pmc bots
followers = hostile to player not to pmc bots
pmcs = hostile to player + always hostile to scavs
scavs = hostile to player and pmc bots
raiders = hostile to player and pmc bots
Adjusted infection rates to just maps with zombie kill quests
* Format Style Fixes
* Added missing values for event bosses
* Format Style Fixes
* Added missing values for `ravangezryachiyevent`
Fixed preset typo `bossTagillaAgro`
* Format Style Fixes
* Flagged `Night of The Cult` as halloween quest
* Fixed incorrect logic
* Enabled `Night of The Cult` bosses to spawn
* Format Style Fixes
* Addd a new ReleaseCheckService to notify users of updates (#670)
* Addd a new ReleaseCheckService to notify users of updates
- Pulls the latest release from GitHub API to compare the tag against the users current SPT version
- Runs at the very end of the startup process to avoid being pushed off screen by mod logging
- Only notifies of patch version increments, not major or minor increments
- Links the release notes so users can Ctrl+Click to open directly to the upgrade page
- Is run on its own thread, and discards all errors, so as to not impact users without an internet connection or ability to access GitHub
* Formatting
* Use record for the ReleaseInformation class
---------
Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com>
* ProfileDataService changes:
Added `ClearProfileData()`
Replaced filepath access with `Path.Combine`
Reduced various sources of duplication
* Adjusted `Goons` spawn chance to 20% across `Customs/Lighthouse/Woods/Shoreline`
* Account for compound items in DialogHelper.GetMessageItemContents
* Generate weapon/armor price based on the child item price total
* Added halloween event bosses to april event
* Flagged infected spawns as `ForceSpawn` and ``
* Add migration for invalid pockets
* Default assign IEnumerable
* Post raid effect fixes:
When exiting raid with severe muscle pain, prevent client instructing server to add mild muscle pain
When exiting a raid with effect that has a timer, decrease timer value by amount of time spent in raid
* Updated nuget packages
* Fixed player scav not having correct HP values on limbs #642
* Remove unused record
* Revert "Updated nuget packages"
This reverts commit f6d9d461a6.
* Added `IMP mine detector` to reward and flea blacklist
* Fixed weapon builds not overwriting existing #654
Cleaned up `SaveWeaponBuild` and `SaveEquipmentBuild`
---------
Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com>
Co-authored-by: Chomp <27521899+chompDev@users.noreply.github.com>
Co-authored-by: Chomp <dev@dev.sp-tarkov.com>
Co-authored-by: CWX <CWXDEV@outlook.com>
Co-authored-by: sp-tarkov-bot <singleplayertarkov@gmail.com>
Co-authored-by: Cj <161484149+CJ-SPT@users.noreply.github.com>
Co-authored-by: Tyfon <29051038+tyfon7@users.noreply.github.com>
Co-authored-by: Archangel <jesse@archangel.wtf>
240 lines
8.7 KiB
C#
240 lines
8.7 KiB
C#
using SPTarkov.DI.Annotations;
|
|
using SPTarkov.Server.Core.Helpers;
|
|
using SPTarkov.Server.Core.Models.Common;
|
|
using SPTarkov.Server.Core.Models.Eft.Builds;
|
|
using SPTarkov.Server.Core.Models.Eft.PresetBuild;
|
|
using SPTarkov.Server.Core.Models.Eft.Profile;
|
|
using SPTarkov.Server.Core.Models.Enums;
|
|
using SPTarkov.Server.Core.Models.Utils;
|
|
using SPTarkov.Server.Core.Servers;
|
|
using SPTarkov.Server.Core.Services;
|
|
using SPTarkov.Server.Core.Utils.Cloners;
|
|
|
|
namespace SPTarkov.Server.Core.Controllers;
|
|
|
|
[Injectable]
|
|
public class BuildController(
|
|
ISptLogger<BuildController> logger,
|
|
DatabaseService databaseService,
|
|
ProfileHelper profileHelper,
|
|
ServerLocalisationService serverLocalisationService,
|
|
ItemHelper itemHelper,
|
|
SaveServer saveServer,
|
|
ICloner cloner
|
|
)
|
|
{
|
|
/// <summary>
|
|
/// Handle client/handbook/builds/my/list
|
|
/// </summary>
|
|
/// <param name="sessionID">Session/player id</param>
|
|
/// <returns></returns>
|
|
public UserBuilds? GetUserBuilds(MongoId sessionID)
|
|
{
|
|
const string secureContainerSlotId = "SecuredContainer";
|
|
|
|
var profile = profileHelper.GetFullProfile(sessionID);
|
|
profile.UserBuildData ??= new UserBuilds
|
|
{
|
|
EquipmentBuilds = [],
|
|
WeaponBuilds = [],
|
|
MagazineBuilds = [],
|
|
};
|
|
|
|
// Ensure the secure container in the default presets match what the player has equipped
|
|
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 firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone
|
|
.FirstOrDefault()
|
|
?.Items?.FirstOrDefault(x => x.SlotId == secureContainerSlotId);
|
|
|
|
if (playerSecureContainer is not null && playerSecureContainer.Template != firstDefaultItemsSecureContainer?.Template)
|
|
// Default equipment presets' secure container tpl doesn't match players secure container tpl
|
|
{
|
|
foreach (var defaultPreset in defaultEquipmentPresetsClone)
|
|
{
|
|
// Find presets secure container
|
|
var secureContainer = defaultPreset.Items?.FirstOrDefault(item => item.SlotId == secureContainerSlotId);
|
|
if (secureContainer is not null)
|
|
{
|
|
secureContainer.Template = playerSecureContainer.Template;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clone player build data from profile and append the above defaults onto end
|
|
var userBuildsClone = cloner.Clone(profile?.UserBuildData);
|
|
|
|
userBuildsClone.EquipmentBuilds ??= [];
|
|
userBuildsClone.EquipmentBuilds?.AddRange(defaultEquipmentPresetsClone);
|
|
|
|
return userBuildsClone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client/builds/weapon/save
|
|
/// </summary>
|
|
/// <param name="sessionId">Session/Player id</param>
|
|
/// <param name="request"></param>
|
|
public void SaveWeaponBuild(MongoId sessionId, PresetBuildActionRequestData request)
|
|
{
|
|
var profile = profileHelper.GetFullProfile(sessionId);
|
|
|
|
// Replace duplicate Id's. The first item is the base item.
|
|
// The root ID and the base item ID need to match.
|
|
request.Items = itemHelper.ReplaceIDs(request.Items, profile.CharacterData.PmcData);
|
|
request.Root = request.Items.FirstOrDefault().Id;
|
|
|
|
// Create new object ready to save into profile userbuilds.weaponBuilds
|
|
var newBuild = new WeaponBuild
|
|
{
|
|
Id = request.Id,
|
|
Name = request.Name,
|
|
Root = request.Root,
|
|
Items = request.Items.ToList(),
|
|
};
|
|
|
|
var savedWeaponBuilds = profile.UserBuildData.WeaponBuilds;
|
|
var existingBuild = savedWeaponBuilds.FirstOrDefault(build => build.Name == request.Name || build.Id == request.Id);
|
|
if (existingBuild is not null)
|
|
{
|
|
// exists, replace
|
|
profile.UserBuildData.WeaponBuilds.Remove(existingBuild);
|
|
profile.UserBuildData.WeaponBuilds.Add(newBuild);
|
|
}
|
|
else
|
|
{
|
|
// Add fresh
|
|
profile.UserBuildData.WeaponBuilds.Add(newBuild);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client/builds/equipment/save event
|
|
/// </summary>
|
|
/// <param name="sessionID">Session/player id</param>
|
|
/// <param name="request"></param>
|
|
public void SaveEquipmentBuild(MongoId sessionID, PresetBuildActionRequestData request)
|
|
{
|
|
var profile = profileHelper.GetFullProfile(sessionID);
|
|
|
|
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.
|
|
request.Items = itemHelper.ReplaceIDs(request.Items, profile.CharacterData.PmcData);
|
|
|
|
var newBuild = new EquipmentBuild
|
|
{
|
|
Id = request.Id,
|
|
Name = request.Name,
|
|
BuildType = EquipmentBuildType.Custom,
|
|
Root = request.Items.First().Id,
|
|
Items = request.Items.ToList(),
|
|
};
|
|
|
|
var existingBuild = existingSavedEquipmentBuilds?.FirstOrDefault(build => build.Name == request.Name || build.Id == request.Id);
|
|
if (existingBuild is not null)
|
|
{
|
|
// Already exists, replace
|
|
profile.UserBuildData.EquipmentBuilds.Remove(existingBuild);
|
|
profile.UserBuildData.EquipmentBuilds.Add(newBuild);
|
|
}
|
|
else
|
|
{
|
|
// Fresh, add new
|
|
profile.UserBuildData.EquipmentBuilds.Add(newBuild);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client/builds/delete
|
|
/// </summary>
|
|
/// <param name="sessionId">Session/Player id</param>
|
|
/// <param name="request"></param>
|
|
public void RemoveBuild(MongoId sessionId, RemoveBuildRequestData request)
|
|
{
|
|
RemovePlayerBuild(request.Id, sessionId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client/builds/magazine/save
|
|
/// </summary>
|
|
/// <param name="sessionId">Session/Player id</param>
|
|
/// <param name="request"></param>
|
|
public void CreateMagazineTemplate(MongoId sessionId, SetMagazineRequest request)
|
|
{
|
|
var result = new MagazineBuild
|
|
{
|
|
Id = request.Id,
|
|
Name = request.Name,
|
|
Caliber = request.Caliber,
|
|
TopCount = request.TopCount,
|
|
BottomCount = request.BottomCount,
|
|
Items = request.Items,
|
|
};
|
|
|
|
var profile = profileHelper.GetFullProfile(sessionId);
|
|
|
|
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);
|
|
if (magazineBuildToRemove is not null)
|
|
{
|
|
profile.UserBuildData.MagazineBuilds.Remove(magazineBuildToRemove);
|
|
}
|
|
|
|
// Add new template to profile
|
|
profile.UserBuildData.MagazineBuilds.Add(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client/builds/delete
|
|
/// Remove build from players profile
|
|
/// </summary>
|
|
/// <param name="idToRemove"></param>
|
|
/// <param name="sessionID">Session/Player id</param>
|
|
protected void RemovePlayerBuild(MongoId idToRemove, MongoId sessionID)
|
|
{
|
|
var profile = saveServer.GetProfile(sessionID);
|
|
var weaponBuilds = profile.UserBuildData.WeaponBuilds;
|
|
var equipmentBuilds = profile.UserBuildData.EquipmentBuilds;
|
|
var magazineBuilds = profile.UserBuildData.MagazineBuilds;
|
|
|
|
// Check for id in weapon array first
|
|
var matchingWeaponBuild = weaponBuilds.FirstOrDefault(weaponBuild => weaponBuild.Id == idToRemove);
|
|
if (matchingWeaponBuild is not null)
|
|
{
|
|
weaponBuilds.Remove(matchingWeaponBuild);
|
|
|
|
return;
|
|
}
|
|
|
|
// Id not found in weapons, try equipment
|
|
var matchingEquipmentBuild = equipmentBuilds.FirstOrDefault(equipmentBuild => equipmentBuild.Id == idToRemove);
|
|
if (matchingEquipmentBuild is not null)
|
|
{
|
|
equipmentBuilds.Remove(matchingEquipmentBuild);
|
|
|
|
return;
|
|
}
|
|
|
|
// Id not found in weapons/equipment, try mags
|
|
var matchingMagazineBuild = magazineBuilds.FirstOrDefault(magBuild => magBuild.Id == idToRemove);
|
|
if (matchingMagazineBuild is not null)
|
|
{
|
|
magazineBuilds.Remove(matchingMagazineBuild);
|
|
|
|
return;
|
|
}
|
|
|
|
// Not found in weapons,equipment or magazines, not good
|
|
logger.Error(serverLocalisationService.GetText("build-unable_to_delete_preset", idToRemove));
|
|
}
|
|
}
|