Cleaned up a lot of health-related code formatting to be more readable in prep for fixing effect transference issues
This commit is contained in:
@@ -37,40 +37,40 @@ public class HealthHelper(
|
||||
/// <summary>
|
||||
/// Update player profile vitality values with changes from client request object
|
||||
/// </summary>
|
||||
/// <param name="pmcData">Player profile</param>
|
||||
/// <param name="postRaidHealth">Post raid data</param>
|
||||
/// <param name="sessionID">Session id</param>
|
||||
/// <param name="isDead">Is player dead</param>
|
||||
public void UpdateProfileHealthPostRaid(
|
||||
PmcData pmcData,
|
||||
BotBaseHealth postRaidHealth,
|
||||
/// <param name="pmcProfileToUpdate">Player profile to apply changes to</param>
|
||||
/// <param name="healthChanges">Changes to apply </param>
|
||||
/// <param name="isDead">OPTIONAL - Is player dead</param>
|
||||
public void ApplyHealthChangesToProfile(
|
||||
string sessionID,
|
||||
bool isDead
|
||||
PmcData pmcProfileToUpdate,
|
||||
BotBaseHealth healthChanges,
|
||||
bool isDead = false
|
||||
)
|
||||
{
|
||||
var fullProfile = _saveServer.GetProfile(sessionID);
|
||||
var profileEdition = fullProfile.ProfileInfo.Edition;
|
||||
var profileSide = fullProfile.CharacterData.PmcData.Info.Side;
|
||||
|
||||
// Get matching 'side e.g. USEC
|
||||
// Get matching 'side' e.g. USEC
|
||||
var matchingSide = _profileHelper.GetProfileTemplateForSide(profileEdition, profileSide);
|
||||
|
||||
var defaultTemperature =
|
||||
matchingSide?.Character?.Health?.Temperature ?? new CurrentMinMax { Current = 36.6 };
|
||||
|
||||
fullProfile.StoreHydrationEnergyTempInProfile(
|
||||
postRaidHealth.Hydration.Current ?? 0,
|
||||
postRaidHealth.Energy.Current ?? 0,
|
||||
healthChanges.Hydration.Current ?? 0,
|
||||
healthChanges.Energy.Current ?? 0,
|
||||
defaultTemperature.Current ?? 0 // Reset profile temp to the default to prevent very cold/hot temps persisting into next raid
|
||||
);
|
||||
|
||||
// Store limb effects from post-raid in profile
|
||||
foreach (var bodyPart in postRaidHealth.BodyParts)
|
||||
foreach (var bodyPart in healthChanges.BodyParts)
|
||||
{
|
||||
// Effects
|
||||
if (postRaidHealth.BodyParts[bodyPart.Key].Effects is not null)
|
||||
if (healthChanges.BodyParts[bodyPart.Key].Effects is not null)
|
||||
{
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Effects = postRaidHealth
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Effects = healthChanges
|
||||
.BodyParts[bodyPart.Key]
|
||||
.Effects;
|
||||
}
|
||||
@@ -80,75 +80,77 @@ public class HealthHelper(
|
||||
// Player alive, not is limb alive
|
||||
{
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Health.Current =
|
||||
postRaidHealth.BodyParts[bodyPart.Key].Health.Current ?? 0;
|
||||
healthChanges.BodyParts[bodyPart.Key].Health.Current ?? 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Health.Current =
|
||||
pmcData.Health.BodyParts[bodyPart.Key].Health.Maximum
|
||||
pmcProfileToUpdate.Health.BodyParts[bodyPart.Key].Health.Maximum
|
||||
* _healthConfig.HealthMultipliers.Death
|
||||
?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
TransferPostRaidLimbEffectsToProfile(postRaidHealth.BodyParts, pmcData);
|
||||
// Alter saved profiles Health with values from post-raid client data
|
||||
ModifyProfileHeathProperties(
|
||||
healthChanges.BodyParts,
|
||||
pmcProfileToUpdate,
|
||||
["Dehydration", "Exhaustion"]
|
||||
);
|
||||
|
||||
// Adjust hydration/energy/temp and limb hp using temp storage hydrated above
|
||||
SaveHealth(pmcData, sessionID);
|
||||
SaveHealth(pmcProfileToUpdate, sessionID);
|
||||
|
||||
// Reset temp storage
|
||||
ResetVitality(sessionID);
|
||||
|
||||
// Update last edited timestamp
|
||||
pmcData.Health.UpdateTime = _timeUtil.GetTimeStamp();
|
||||
pmcProfileToUpdate.Health.UpdateTime = _timeUtil.GetTimeStamp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take body part effects from client profile and apply to server profile
|
||||
/// Apply Health values to profile
|
||||
/// </summary>
|
||||
/// <param name="postRaidBodyParts">Post-raid body part data</param>
|
||||
/// <param name="profileData">Player profile on server</param>
|
||||
protected void TransferPostRaidLimbEffectsToProfile(
|
||||
Dictionary<string, BodyPartHealth> postRaidBodyParts,
|
||||
PmcData profileData
|
||||
/// <param name="bodyPartChanges">Changes to apply</param>
|
||||
/// <param name="profileToAdjust">Player profile on server</param>
|
||||
/// <param name="effectsToSkip"></param>
|
||||
protected void ModifyProfileHeathProperties(
|
||||
Dictionary<string, BodyPartHealth> bodyPartChanges,
|
||||
PmcData profileToAdjust,
|
||||
HashSet<string>? effectsToSkip = null
|
||||
)
|
||||
{
|
||||
// Iterate over each body part
|
||||
HashSet<string> effectsToIgnore = ["Dehydration", "Exhaustion"];
|
||||
foreach (var bodyPartId in postRaidBodyParts)
|
||||
foreach (var (partName, partProperties) in bodyPartChanges)
|
||||
{
|
||||
// Get effects on body part from profile
|
||||
var bodyPartEffects = postRaidBodyParts[bodyPartId.Key].Effects;
|
||||
foreach (var (key, effectDetails) in bodyPartEffects)
|
||||
// Process each effect for each part
|
||||
foreach (var (key, effectDetails) in partProperties.Effects)
|
||||
{
|
||||
// Null guard
|
||||
profileData.Health.BodyParts[bodyPartId.Key].Effects ??=
|
||||
new Dictionary<string, BodyPartEffectProperties>();
|
||||
var matchingProfilePart = profileToAdjust.Health.BodyParts[partName];
|
||||
matchingProfilePart.Effects ??= new Dictionary<string, BodyPartEffectProperties>();
|
||||
|
||||
// Effect already exists on limb in server profile, skip
|
||||
var profileBodyPartEffects = profileData.Health.BodyParts[bodyPartId.Key].Effects;
|
||||
if (profileBodyPartEffects.ContainsKey(key))
|
||||
if (matchingProfilePart.Effects.ContainsKey(key))
|
||||
{
|
||||
if (effectsToIgnore.Contains(key))
|
||||
// Get rid of certain effects we don't want to persist out of raid
|
||||
// Edge case - effect already exists at destination, but we don't want to overwrite details
|
||||
if (effectsToSkip is not null && effectsToSkip.Contains(key))
|
||||
{
|
||||
profileBodyPartEffects[key] = null;
|
||||
matchingProfilePart.Effects[key] = null;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (effectsToIgnore.Contains(key))
|
||||
// Do not pass some effects to out of raid profile
|
||||
if (effectsToSkip is not null && effectsToSkip.Contains(key))
|
||||
// Do not pass skipped effect into profile
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var effectToAdd = new BodyPartEffectProperties { Time = effectDetails.Time ?? -1 };
|
||||
// Add effect to server profile
|
||||
if (profileBodyPartEffects.TryAdd(key, effectToAdd))
|
||||
if (matchingProfilePart.Effects.TryAdd(key, effectToAdd))
|
||||
{
|
||||
profileBodyPartEffects[key] = effectToAdd;
|
||||
matchingProfilePart.Effects[key] = effectToAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,22 +189,21 @@ public class HealthHelper(
|
||||
pmcData.Health.Energy.Current = Math.Round(profileHealth.Energy ?? 0);
|
||||
pmcData.Health.Temperature.Current = Math.Round(profileHealth.Temperature ?? 0);
|
||||
|
||||
foreach (var bodyPart in pmcData.Health.BodyParts)
|
||||
foreach (var (partName, partProperties) in pmcData.Health.BodyParts)
|
||||
{
|
||||
if (profileHealth.Health[bodyPart.Key].Health.Maximum > bodyPart.Value.Health.Maximum)
|
||||
var matchingProfilePart = profileHealth.Health[partName];
|
||||
if (matchingProfilePart.Health.Maximum > partProperties.Health.Maximum)
|
||||
{
|
||||
profileHealth.Health[bodyPart.Key].Health.Maximum = bodyPart.Value.Health.Maximum;
|
||||
matchingProfilePart.Health.Maximum = partProperties.Health.Maximum;
|
||||
}
|
||||
|
||||
if (profileHealth.Health[bodyPart.Key].Health.Current == 0)
|
||||
if (matchingProfilePart.Health.Current == 0)
|
||||
{
|
||||
profileHealth.Health[bodyPart.Key].Health.Current =
|
||||
bodyPart.Value.Health.Maximum * _healthConfig.HealthMultipliers.Blacked;
|
||||
matchingProfilePart.Health.Current =
|
||||
partProperties.Health.Maximum * _healthConfig.HealthMultipliers.Blacked;
|
||||
}
|
||||
|
||||
bodyPart.Value.Health.Current = Math.Round(
|
||||
profileHealth.Health[bodyPart.Key].Health.Current ?? 0
|
||||
);
|
||||
partProperties.Health.Current = Math.Round(matchingProfilePart.Health.Current ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -459,7 +459,7 @@ public class LocationLifecycleService
|
||||
{
|
||||
// Manually store the map player just left
|
||||
request.LocationTransit.SptLastVisitedLocation = locationName;
|
||||
// TODO - Persist each players last visited location history over multiple transits, e.g using InMemoryCacheService, need to take care to not let data get stored forever
|
||||
// TODO - Persist each players last visited location history over multiple transits, e.g. using InMemoryCacheService, need to take care to not let data get stored forever
|
||||
// Store transfer data for later use in `startLocalRaid()` when next raid starts
|
||||
request.LocationTransit.SptExitName = request.Results.ExitName;
|
||||
_profileActivityService.GetProfileActivityRaidData(sessionId).LocationTransit =
|
||||
@@ -881,7 +881,7 @@ public class LocationLifecycleService
|
||||
/// Handles PMC Profile after the raid
|
||||
/// </summary>
|
||||
/// <param name="sessionId"> Player id </param>
|
||||
/// <param name="fullProfile"> Pmc profile </param>
|
||||
/// <param name="fullServerProfile"> Pmc profile from server</param>
|
||||
/// <param name="scavProfile"> Scav profile </param>
|
||||
/// <param name="isDead"> Player died/got left behind in raid </param>
|
||||
/// <param name="isSurvived"> Not same as opposite of `isDead`, specific status </param>
|
||||
@@ -890,7 +890,7 @@ public class LocationLifecycleService
|
||||
/// <param name="locationName"> Current finished Raid location </param>
|
||||
protected void HandlePostRaidPmc(
|
||||
string sessionId,
|
||||
SptProfile fullProfile,
|
||||
SptProfile fullServerProfile,
|
||||
PmcData scavProfile,
|
||||
bool isDead,
|
||||
bool isSurvived,
|
||||
@@ -899,78 +899,84 @@ public class LocationLifecycleService
|
||||
string locationName
|
||||
)
|
||||
{
|
||||
var pmcProfile = fullProfile.CharacterData.PmcData;
|
||||
var serverPmcProfile = fullServerProfile.CharacterData.PmcData;
|
||||
var postRaidProfile = request.Results.Profile;
|
||||
var preRaidProfileQuestDataClone = _cloner.Clone(pmcProfile.Quests);
|
||||
var preRaidProfileQuestDataClone = _cloner.Clone(serverPmcProfile.Quests);
|
||||
|
||||
// MUST occur BEFORE inventory actions (setInventory()) occur
|
||||
// Player died, get quest items they lost for use later
|
||||
var lostQuestItems = postRaidProfile.GetQuestItemsInProfile();
|
||||
|
||||
// Update inventory
|
||||
_inRaidHelper.SetInventory(sessionId, pmcProfile, postRaidProfile, isSurvived, isTransfer);
|
||||
_inRaidHelper.SetInventory(
|
||||
sessionId,
|
||||
serverPmcProfile,
|
||||
postRaidProfile,
|
||||
isSurvived,
|
||||
isTransfer
|
||||
);
|
||||
|
||||
pmcProfile.Info.Level = postRaidProfile.Info.Level;
|
||||
pmcProfile.Skills = postRaidProfile.Skills;
|
||||
pmcProfile.Stats.Eft = postRaidProfile.Stats.Eft;
|
||||
pmcProfile.Encyclopedia = postRaidProfile.Encyclopedia;
|
||||
pmcProfile.TaskConditionCounters = postRaidProfile.TaskConditionCounters;
|
||||
pmcProfile.SurvivorClass = postRaidProfile.SurvivorClass;
|
||||
serverPmcProfile.Info.Level = postRaidProfile.Info.Level;
|
||||
serverPmcProfile.Skills = postRaidProfile.Skills;
|
||||
serverPmcProfile.Stats.Eft = postRaidProfile.Stats.Eft;
|
||||
serverPmcProfile.Encyclopedia = postRaidProfile.Encyclopedia;
|
||||
serverPmcProfile.TaskConditionCounters = postRaidProfile.TaskConditionCounters;
|
||||
serverPmcProfile.SurvivorClass = postRaidProfile.SurvivorClass;
|
||||
|
||||
// MUST occur prior to profile achievements being overwritten by post-raid achievements
|
||||
ProcessAchievementRewards(fullProfile, postRaidProfile.Achievements);
|
||||
ProcessAchievementRewards(fullServerProfile, postRaidProfile.Achievements);
|
||||
|
||||
pmcProfile.Achievements = postRaidProfile.Achievements;
|
||||
pmcProfile.Quests = ProcessPostRaidQuests(postRaidProfile.Quests);
|
||||
serverPmcProfile.Achievements = postRaidProfile.Achievements;
|
||||
serverPmcProfile.Quests = ProcessPostRaidQuests(postRaidProfile.Quests);
|
||||
|
||||
// Handle edge case - must occur AFTER processPostRaidQuests()
|
||||
LightkeeperQuestWorkaround(
|
||||
sessionId,
|
||||
postRaidProfile.Quests,
|
||||
preRaidProfileQuestDataClone,
|
||||
pmcProfile
|
||||
serverPmcProfile
|
||||
);
|
||||
|
||||
pmcProfile.WishList = postRaidProfile.WishList;
|
||||
serverPmcProfile.WishList = postRaidProfile.WishList;
|
||||
|
||||
pmcProfile.Info.Experience = postRaidProfile.Info.Experience;
|
||||
serverPmcProfile.Info.Experience = postRaidProfile.Info.Experience;
|
||||
|
||||
ApplyTraderStandingAdjustments(pmcProfile.TradersInfo, postRaidProfile.TradersInfo);
|
||||
ApplyTraderStandingAdjustments(serverPmcProfile.TradersInfo, postRaidProfile.TradersInfo);
|
||||
|
||||
// Must occur AFTER experience is set and stats copied over
|
||||
pmcProfile.Stats.Eft.TotalSessionExperience = 0;
|
||||
serverPmcProfile.Stats.Eft.TotalSessionExperience = 0;
|
||||
|
||||
const string fenceId = Traders.FENCE;
|
||||
|
||||
// Clamp fence standing
|
||||
var currentFenceStanding = postRaidProfile.TradersInfo[fenceId].Standing;
|
||||
pmcProfile.TradersInfo[fenceId].Standing = Math.Min(
|
||||
serverPmcProfile.TradersInfo[fenceId].Standing = Math.Min(
|
||||
Math.Max((double)currentFenceStanding, -7),
|
||||
15
|
||||
); // Ensure it stays between -7 and 15
|
||||
|
||||
// Copy fence values to Scav
|
||||
scavProfile.TradersInfo[fenceId] = pmcProfile.TradersInfo[fenceId];
|
||||
scavProfile.TradersInfo[fenceId] = serverPmcProfile.TradersInfo[fenceId];
|
||||
|
||||
// MUST occur AFTER encyclopedia updated
|
||||
MergePmcAndScavEncyclopedias(pmcProfile, scavProfile);
|
||||
MergePmcAndScavEncyclopedias(serverPmcProfile, scavProfile);
|
||||
|
||||
// Handle temp, hydration, limb hp/effects
|
||||
_healthHelper.UpdateProfileHealthPostRaid(
|
||||
pmcProfile,
|
||||
postRaidProfile.Health,
|
||||
_healthHelper.ApplyHealthChangesToProfile(
|
||||
sessionId,
|
||||
serverPmcProfile,
|
||||
postRaidProfile.Health,
|
||||
isDead
|
||||
);
|
||||
|
||||
if (isTransfer)
|
||||
{
|
||||
// Adjust limb hp and effects while transiting
|
||||
UpdateLimbValuesAfterTransit(pmcProfile.Health);
|
||||
UpdateLimbValuesAfterTransit(serverPmcProfile.Health);
|
||||
}
|
||||
|
||||
// This must occur _BEFORE_ `deleteInventory`, as that method clears insured items
|
||||
HandleInsuredItemLostEvent(sessionId, pmcProfile, request, locationName);
|
||||
HandleInsuredItemLostEvent(sessionId, serverPmcProfile, request, locationName);
|
||||
|
||||
if (isDead)
|
||||
{
|
||||
@@ -978,7 +984,11 @@ public class LocationLifecycleService
|
||||
// MUST occur AFTER quests have post raid quest data has been merged "processPostRaidQuests()"
|
||||
// Player is dead + had quest items, check and fix any broken find item quests
|
||||
{
|
||||
CheckForAndFixPickupQuestsAfterDeath(sessionId, lostQuestItems, pmcProfile.Quests);
|
||||
CheckForAndFixPickupQuestsAfterDeath(
|
||||
sessionId,
|
||||
lostQuestItems,
|
||||
serverPmcProfile.Quests
|
||||
);
|
||||
}
|
||||
|
||||
if (postRaidProfile.Stats.Eft.Aggressor is not null)
|
||||
@@ -987,16 +997,16 @@ public class LocationLifecycleService
|
||||
postRaidProfile.Stats.Eft.Aggressor.ProfileId = request.Results.KillerId;
|
||||
_pmcChatResponseService.SendKillerResponse(
|
||||
sessionId,
|
||||
pmcProfile,
|
||||
serverPmcProfile,
|
||||
postRaidProfile.Stats.Eft.Aggressor
|
||||
);
|
||||
}
|
||||
|
||||
_inRaidHelper.DeleteInventory(pmcProfile, sessionId);
|
||||
_inRaidHelper.DeleteInventory(serverPmcProfile, sessionId);
|
||||
|
||||
_inRaidHelper.RemoveFiRStatusFromItemsInContainer(
|
||||
sessionId,
|
||||
pmcProfile,
|
||||
serverPmcProfile,
|
||||
"SecuredContainer"
|
||||
);
|
||||
}
|
||||
@@ -1012,7 +1022,7 @@ public class LocationLifecycleService
|
||||
if (victims?.Count > 0)
|
||||
// Player killed PMCs, send some mail responses to them
|
||||
{
|
||||
_pmcChatResponseService.SendVictimResponse(sessionId, victims, pmcProfile);
|
||||
_pmcChatResponseService.SendVictimResponse(sessionId, victims, serverPmcProfile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user