Fixed issues relating to gift codes with profile modifications

Updated enums to match client formatting and ids
Added `SkillClass` enum

#368
This commit is contained in:
Chomp
2025-06-07 23:08:21 +01:00
parent 2d67d8002b
commit 3a2f68a232
13 changed files with 131 additions and 121 deletions
@@ -49,10 +49,10 @@ public class HideoutController(
protected List<HideoutAreas> _hideoutAreas =
[
HideoutAreas.AIR_FILTERING,
HideoutAreas.WATER_COLLECTOR,
HideoutAreas.GENERATOR,
HideoutAreas.BITCOIN_FARM
HideoutAreas.AirFilteringUnit,
HideoutAreas.WaterCollector,
HideoutAreas.Generator,
HideoutAreas.BitcoinFarm
];
protected HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
@@ -209,15 +209,15 @@ public class HideoutController(
// Upgrading water collector / med station
if (
profileHideoutArea.Type == HideoutAreas.WATER_COLLECTOR ||
profileHideoutArea.Type == HideoutAreas.MEDSTATION
profileHideoutArea.Type == HideoutAreas.WaterCollector ||
profileHideoutArea.Type == HideoutAreas.MedStation
)
{
SetWallVisibleIfPrereqsMet(pmcData);
}
// Cleanup temporary buffs/debuffs from wall if complete
if (profileHideoutArea.Type == HideoutAreas.EMERGENCY_WALL && profileHideoutArea.Level == 6)
if (profileHideoutArea.Type == HideoutAreas.EmergencyWall && profileHideoutArea.Level == 6)
{
_hideoutHelper.RemoveHideoutWallBuffsAndDebuffs(hideoutData, pmcData);
}
@@ -236,11 +236,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.WATER_COLLECTOR);
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.EMERGENCY_WALL);
var wall = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.EmergencyWall);
if (wall?.Level == 0)
{
wall.Level = 3;
@@ -275,7 +275,7 @@ public class HideoutController(
AddUpdateInventoryItemToProfile(sessionId, pmcData, dbHideoutArea, hideoutStage);
// Edge case, add/update `stand1/stand2/stand3` children
if (dbHideoutArea.Type == HideoutAreas.EQUIPMENT_PRESETS_STAND)
if (dbHideoutArea.Type == HideoutAreas.EquipmentPresetsStand)
// Can have multiple 'standx' children depending on upgrade level
{
AddMissingPresetStandItemsToProfile(sessionId, hideoutStage, pmcData, dbHideoutArea, output);
@@ -283,8 +283,8 @@ public class HideoutController(
// Don't inform client when upgraded area is hall of fame or equipment stand, BSG doesn't inform client this specific upgrade has occurred
// will break client if sent
HashSet<HideoutAreas> check = [HideoutAreas.PLACE_OF_FAME];
if (!check.Contains(dbHideoutArea.Type ?? HideoutAreas.NOTSET))
HashSet<HideoutAreas> check = [HideoutAreas.PlaceOfFame];
if (!check.Contains(dbHideoutArea.Type ?? HideoutAreas.NotSet))
{
AddContainerUpgradeToClientOutput(sessionId, keyForHideoutAreaStash, dbHideoutArea, hideoutStage, output);
}
@@ -477,7 +477,7 @@ public class HideoutController(
// Handle areas that have resources that can be placed in/taken out of slots from the area
if (
_hideoutAreas.Contains(hideoutArea.Type ?? HideoutAreas.NOTSET)
_hideoutAreas.Contains(hideoutArea.Type ?? HideoutAreas.NotSet)
)
{
var response = RemoveResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
@@ -197,7 +197,7 @@ public class InventoryController(
break;
case "SkillPoints":
{
var profileSkill = pmcData.Skills.Common.FirstOrDefault(x => x.Id == 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}");
@@ -231,7 +231,7 @@ public class InventoryController(
{
var areaName = mailEvent.Entity;
var newValue = mailEvent.Value;
var hideoutAreaType = Enum.Parse<HideoutAreas>(areaName ?? "NOTSET");
var hideoutAreaType = Enum.Parse<HideoutAreas>(areaName ?? "NotSet");
var desiredArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == hideoutAreaType);
if (desiredArea is not null)
@@ -648,7 +648,7 @@ public class RepeatableQuestController(
/// <returns>True if unlocked</returns>
protected bool PlayerHasDailyScavQuestsUnlocked(PmcData pmcData)
{
return pmcData?.Hideout?.Areas?.FirstOrDefault(hideoutArea => hideoutArea.Type == HideoutAreas.INTEL_CENTER)
return pmcData?.Hideout?.Areas?.FirstOrDefault(hideoutArea => hideoutArea.Type == HideoutAreas.IntelligenceCenter)
?.Level >=
1;
}
@@ -662,7 +662,7 @@ public class BotGenerator(
// All skills have id and progress props
var skillToAdd = new BaseSkill
{
Id = kvp.Key,
Id = Enum.Parse<SkillTypes>(kvp.Key),
Progress = _randomUtil.GetDouble(skill.Min, skill.Max)
};
@@ -241,15 +241,15 @@ public class HideoutHelper(
/// <returns>Properties</returns>
protected HideoutProperties GetHideoutProperties(PmcData pmcData)
{
var bitcoinFarm = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.BITCOIN_FARM);
var bitcoinFarm = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.BitcoinFarm);
var bitcoinCount = (bitcoinFarm?.Slots).Count(slot => slot.Items is not null); // Get slots with an item property
var hideoutProperties = new HideoutProperties
{
BtcFarmGcs = bitcoinCount,
IsGeneratorOn = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.GENERATOR)?.Active ?? false,
IsGeneratorOn = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.Generator)?.Active ?? false,
WaterCollectorHasFilter = DoesWaterCollectorHaveFilter(
pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.WATER_COLLECTOR)
pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.WaterCollector)
)
};
@@ -312,22 +312,22 @@ public class HideoutHelper(
}
// Special handling required
if (IsCraftOfType(craft, HideoutAreas.SCAV_CASE))
if (IsCraftOfType(craft, HideoutAreas.ScavCase))
{
UpdateScavCaseProductionTimer(pmcData, prodId.Key);
continue;
}
if (IsCraftOfType(craft, HideoutAreas.WATER_COLLECTOR))
if (IsCraftOfType(craft, HideoutAreas.WaterCollector))
{
UpdateWaterCollectorProductionTimer(pmcData, prodId.Key, hideoutProperties);
continue;
}
// Continious craft
if (IsCraftOfType(craft, HideoutAreas.BITCOIN_FARM))
// Continuous craft
if (IsCraftOfType(craft, HideoutAreas.BitcoinFarm))
{
UpdateBitcoinFarm(
pmcData,
@@ -340,7 +340,7 @@ public class HideoutHelper(
}
// No recipe, needs special handling
if (IsCraftOfType(craft, HideoutAreas.CIRCLE_OF_CULTISTS))
if (IsCraftOfType(craft, HideoutAreas.CircleOfCultists))
{
UpdateCultistCircleCraftProgress(pmcData, prodId.Key);
@@ -348,7 +348,7 @@ public class HideoutHelper(
}
// Ensure recipe exists before using it in updateProductionProgress()
var recipe = recipes.Recipes.FirstOrDefault(r => r.Id == prodId.Key);
var recipe = recipes?.Recipes?.FirstOrDefault(r => r.Id == prodId.Key);
if (recipe is null)
{
_logger.Error(_localisationService.GetText("hideout-missing_recipe_for_area", prodId));
@@ -370,13 +370,13 @@ public class HideoutHelper(
{
switch (hideoutType)
{
case HideoutAreas.WATER_COLLECTOR:
case HideoutAreas.WaterCollector:
return craft.RecipeId == WaterCollector;
case HideoutAreas.BITCOIN_FARM:
case HideoutAreas.BitcoinFarm:
return craft.RecipeId == BitcoinFarm;
case HideoutAreas.SCAV_CASE:
case HideoutAreas.ScavCase:
return craft.SptIsScavCase ?? false;
case HideoutAreas.CIRCLE_OF_CULTISTS:
case HideoutAreas.CircleOfCultists:
return craft.SptIsCultistCircle ?? false;
default:
_logger.Error($"Unhandled hideout area: {hideoutType}, assuming craft: {craft.RecipeId} is not of this type");
@@ -541,18 +541,18 @@ public class HideoutHelper(
{
switch (area.Type)
{
case HideoutAreas.GENERATOR:
case HideoutAreas.Generator:
if (hideoutProperties.IsGeneratorOn)
{
UpdateFuel(area, pmcData, hideoutProperties.IsGeneratorOn);
}
break;
case HideoutAreas.WATER_COLLECTOR:
case HideoutAreas.WaterCollector:
UpdateWaterCollector(sessionID, pmcData, area, hideoutProperties);
break;
case HideoutAreas.AIR_FILTERING:
case HideoutAreas.AirFilteringUnit:
if (hideoutProperties.IsGeneratorOn)
{
UpdateAirFilters(area, pmcData, hideoutProperties.IsGeneratorOn);
@@ -1388,9 +1388,9 @@ public class HideoutHelper(
public void UnlockHideoutWallInProfile(PmcData profileData)
{
var profileHideoutAreas = profileData.Hideout.Areas;
var waterCollector = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.WATER_COLLECTOR);
var medStation = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.MEDSTATION);
var wall = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.EMERGENCY_WALL);
var waterCollector = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.WaterCollector);
var medStation = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.MedStation);
var wall = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.EmergencyWall);
// No collector or med station, skip
if (waterCollector is null && medStation is null)
@@ -1442,12 +1442,12 @@ public class HideoutHelper(
/// <param name="profileData">Player profile</param>
public void ApplyPlaceOfFameDogtagBonus(PmcData pmcData)
{
var fameAreaProfile = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.PLACE_OF_FAME);
var fameAreaProfile = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.PlaceOfFame);
// Get hideout area 16 bonus array
var fameAreaDb = _databaseService
.GetHideout()
.Areas.FirstOrDefault(area => area.Type == HideoutAreas.PLACE_OF_FAME);
.Areas.FirstOrDefault(area => area.Type == HideoutAreas.PlaceOfFame);
// Get SkillGroupLevelingBoost object
var combatBoostBonusDb = fameAreaDb.Stages[fameAreaProfile.Level.ToString()]
@@ -470,7 +470,7 @@ public class ProfileHelper(
return false;
}
var profileSkill = profileSkills.FirstOrDefault(s => s.Id == skill.ToString());
var profileSkill = profileSkills.FirstOrDefault(s => s.Id == skill);
if (profileSkill == null)
{
_logger.Error(_localisationService.GetText("quest-no_skill_found", skill));
@@ -504,7 +504,7 @@ public class ProfileHelper(
return;
}
var profileSkill = profileSkills.FirstOrDefault(s => s.Id == skill.ToString());
var profileSkill = profileSkills.FirstOrDefault(s => s.Id == skill);
if (profileSkill == null)
{
_logger.Error(_localisationService.GetText("quest-no_skill_found", skill));
@@ -539,7 +539,7 @@ public class ProfileHelper(
/// <returns>Common skill object from desired profile</returns>
public BaseSkill? GetSkillFromProfile(PmcData pmcData, SkillTypes skill)
{
var skillToReturn = pmcData?.Skills?.Common.FirstOrDefault(s => s.Id == skill.ToString());
var skillToReturn = pmcData?.Skills?.Common.FirstOrDefault(s => s.Id == skill);
if (skillToReturn == null)
{
_logger.Warning($"Profile {pmcData.SessionId} does not have a skill named: {skill.ToString()}");
@@ -997,7 +997,7 @@ public record BaseSkill
set;
}
public string? Id
public SkillTypes? Id
{
get;
set;
@@ -254,8 +254,7 @@ public record StageImprovementBonus
}
[JsonPropertyName("skillType")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public SkillTypes? SkillType
public SkillClass? SkillType
{
get;
set;
@@ -2,33 +2,33 @@
public enum HideoutAreas
{
NOTSET = -1,
VENTS = 0,
SECURITY = 1,
LAVATORY = 2,
STASH = 3,
GENERATOR = 4,
HEATING = 5,
WATER_COLLECTOR = 6,
MEDSTATION = 7,
NUTRITION_UNIT = 8,
REST_SPACE = 9,
WORKBENCH = 10,
INTEL_CENTER = 11,
SHOOTING_RANGE = 12,
LIBRARY = 13,
SCAV_CASE = 14,
ILLUMINATION = 15,
PLACE_OF_FAME = 16,
AIR_FILTERING = 17,
SOLAR_POWER = 18,
BOOZE_GENERATOR = 19,
BITCOIN_FARM = 20,
CHRISTMAS_TREE = 21,
EMERGENCY_WALL = 22,
GYM = 23,
WEAPON_STAND = 24,
WEAPON_STAND_SECONDARY = 25,
EQUIPMENT_PRESETS_STAND = 26,
CIRCLE_OF_CULTISTS = 27
NotSet = -1,
Vents,
Security,
WaterCloset,
Stash,
Generator,
Heating,
WaterCollector,
MedStation,
Kitchen,
RestSpace,
Workbench,
IntelligenceCenter,
ShootingRange,
Library,
ScavCase,
Illumination,
PlaceOfFame,
AirFilteringUnit,
SolarPower,
BoozeGenerator,
BitcoinFarm,
ChristmasIllumination,
EmergencyWall,
Gym,
WeaponStand,
WeaponStandSecondary,
EquipmentPresetsStand,
CircleOfCultists
}
@@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace SPTarkov.Server.Core.Models.Enums
{
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SkillClass
{
Physical,
Combat,
Special,
Practical,
Mental
}
}
@@ -5,34 +5,43 @@ namespace SPTarkov.Server.Core.Models.Enums;
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SkillTypes
{
BotReload,
BotSound,
HideoutManagement,
Crafting,
Metabolism,
Immunity,
Endurance,
Strength,
Vitality,
Health,
StressResistance,
Throwing,
RecoilControl,
CovertMovement,
FieldMedicine,
Search,
Sniping,
Metabolism,
Immunity,
Perception,
Intellect,
Attention,
Charisma,
Charisma = 10,
Memory,
MagDrills,
Pistol,
Revolver,
SMG,
Assault,
Shotgun,
Sniper,
LMG,
HMG,
Launcher,
AttachedLauncher,
Throwing,
Misc,
Melee,
Surgery,
AimDrills,
DMR,
DrawMaster,
AimMaster,
RecoilControl,
TroubleShooting,
Sniping,
CovertMovement,
ProneMovement,
FirstAid,
FieldMedicine,
Surgery,
LightVests,
HeavyVests,
WeaponModding,
@@ -40,10 +49,9 @@ public enum SkillTypes
NightOps,
SilentOps,
Lockpicking,
Search,
// Also called Weapon Maintenance
WeaponTreatment,
MagDrills,
Freetrading,
Auctions,
Cleanoperations,
@@ -56,24 +64,13 @@ public enum SkillTypes
BearHeavycaliber,
BearRawpower,
UsecArsystems,
UsecDeepweaponmodding_Settings,
UsecLongrangeoptics_Settings,
UsecDeepweaponmodding,
UsecLongrangeoptics,
UsecNegotiations,
UsecTactics,
Pistol,
Revolver,
SMG,
Assault,
Shotgun,
Sniper,
LMG,
HMG,
Launcher,
AttachedLauncher,
Misc,
DMR,
DrawMaster,
AimMaster,
Physical,
Combat
BotReload,
BotSound,
AimDrills,
HideoutManagement,
Crafting = 66
}
@@ -60,7 +60,7 @@ public class CircleOfCultistService(
{
var output = _eventOutputHolder.GetOutput(sessionId);
var cultistCircleStashId = pmcData.Inventory.HideoutAreaStashes.GetValueOrDefault(((int)HideoutAreas.CIRCLE_OF_CULTISTS).ToString());
var cultistCircleStashId = pmcData.Inventory.HideoutAreaStashes.GetValueOrDefault(((int)HideoutAreas.CircleOfCultists).ToString());
if (cultistCircleStashId is null)
{
_logger.Error("Could not find cultist circle stash ID inside inventory! No rewards generated");
@@ -800,7 +800,7 @@ public class CircleOfCultistService(
{
return areas.Where(area =>
{
if (area.Type == HideoutAreas.CHRISTMAS_TREE && !_seasonalEventService.ChristmasEventEnabled())
if (area.Type == HideoutAreas.ChristmasIllumination && !_seasonalEventService.ChristmasEventEnabled())
// Christmas tree area and not Christmas, skip
{
return false;
@@ -418,7 +418,7 @@ public class ProfileFixerService(
{
var globals = _databaseService.GetGlobals();
var generator = pmcProfile.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.GENERATOR);
var generator = pmcProfile.Hideout.Areas.FirstOrDefault(area => area.Type == HideoutAreas.Generator);
if (generator is not null)
{
var genSlots = generator.Slots.Count;
@@ -431,11 +431,11 @@ public class ProfileFixerService(
_logger.Debug("Updating generator area slots to a size of 6 + hideout management skill");
}
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.GENERATOR, (int) (6 + extraGenSlots ?? 0), pmcProfile);
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.Generator, (int) (6 + extraGenSlots ?? 0), pmcProfile);
}
}
var waterCollSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.WATER_COLLECTOR)
var waterCollSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.WaterCollector)
.Slots
.Count;
var extraWaterCollSlots = globals.Configuration.SkillsSettings.HideoutManagement.EliteSlots.WaterCollector.Slots;
@@ -447,10 +447,10 @@ public class ProfileFixerService(
_logger.Debug("Updating water collector area slots to a size of 1 + hideout management skill");
}
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.WATER_COLLECTOR, (int) (1 + extraWaterCollSlots ?? 0), pmcProfile);
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.WaterCollector, (int) (1 + extraWaterCollSlots ?? 0), pmcProfile);
}
var filterSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.AIR_FILTERING).Slots.Count;
var filterSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.AirFilteringUnit).Slots.Count;
var extraFilterSlots = globals.Configuration.SkillsSettings.HideoutManagement.EliteSlots.AirFilteringUnit.Slots;
if (filterSlots < 3 + extraFilterSlots)
@@ -460,10 +460,10 @@ public class ProfileFixerService(
_logger.Debug("Updating air filter area slots to a size of 3 + hideout management skill");
}
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.AIR_FILTERING, (int) (3 + extraFilterSlots ?? 0), pmcProfile);
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.AirFilteringUnit, (int) (3 + extraFilterSlots ?? 0), pmcProfile);
}
var btcFarmSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.BITCOIN_FARM).Slots.Count;
var btcFarmSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.BitcoinFarm).Slots.Count;
var extraBtcSlots = globals.Configuration.SkillsSettings.HideoutManagement.EliteSlots.BitcoinFarm.Slots;
// BTC Farm doesnt have extra slots for hideout management, but we still check for modded stuff!!
@@ -474,10 +474,10 @@ public class ProfileFixerService(
_logger.Debug("Updating bitcoin farm area slots to a size of 50 + hideout management skill");
}
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.BITCOIN_FARM, (int) (50 + extraBtcSlots ?? 0), pmcProfile);
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.BitcoinFarm, (int) (50 + extraBtcSlots ?? 0), pmcProfile);
}
var cultistAreaSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.CIRCLE_OF_CULTISTS)
var cultistAreaSlots = pmcProfile.Hideout.Areas.FirstOrDefault(x => x.Type == HideoutAreas.CircleOfCultists)
.Slots
.Count;
if (cultistAreaSlots < 1)
@@ -487,7 +487,7 @@ public class ProfileFixerService(
_logger.Debug("Updating cultist area slots to a size of 1");
}
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.CIRCLE_OF_CULTISTS, 1, pmcProfile);
AddEmptyObjectsToHideoutAreaSlots(HideoutAreas.CircleOfCultists, 1, pmcProfile);
}
}