From ab26e4205c25d78d85fa1e8c4fc4e4ee57dfe4ab Mon Sep 17 00:00:00 2001 From: CWX Date: Sat, 25 Jan 2025 11:49:47 +0000 Subject: [PATCH] Partial implementation of healthHelper, Type fuckery needs to be fixed --- Libraries/Core/Helpers/HealthHelper.cs | 259 ++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 9 deletions(-) diff --git a/Libraries/Core/Helpers/HealthHelper.cs b/Libraries/Core/Helpers/HealthHelper.cs index cce83f4c..9be8bcd6 100644 --- a/Libraries/Core/Helpers/HealthHelper.cs +++ b/Libraries/Core/Helpers/HealthHelper.cs @@ -3,14 +3,32 @@ using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.Health; using Core.Models.Eft.Profile; +using Core.Models.Spt.Config; +using Core.Models.Utils; +using Core.Servers; +using Core.Services; +using Core.Utils; +using Core.Utils.Cloners; +using SptCommon.Extensions; using BodyPartHealth = Core.Models.Eft.Common.Tables.BodyPartHealth; using Effects = Core.Models.Eft.Profile.Effects; +using Health = Core.Models.Eft.Profile.Health; +using Vitality = Core.Models.Eft.Profile.Vitality; namespace Core.Helpers; [Injectable] -public class HealthHelper +public class HealthHelper( + ISptLogger _logger, + TimeUtil _timeUtil, + SaveServer _saveServer, + DatabaseService _databaseService, + ConfigServer _configServer, + ICloner _cloner +) { + protected HealthConfig _healthConfig = _configServer.GetConfig(); + /// /// Resets the profiles vitality/health and vitality/effects properties to their defaults /// @@ -18,7 +36,34 @@ public class HealthHelper /// Updated profile public SptProfile ResetVitality(string sessionID) { - throw new NotImplementedException(); + var profile = _saveServer.GetProfile(sessionID); + + profile.VitalityData ??= new Vitality { Health = null, Effects = null }; + + profile.VitalityData.Health = new Health { + Hydration = 0, + Energy = 0, + Temperature = 0, + Head = 0, + Chest = 0, + Stomach = 0, + LeftArm = 0, + RightArm = 0, + LeftLeg = 0, + RightLeg = 0, + }; + + profile.VitalityData.Effects = new Effects { + Head = new Head(), + Chest = new Chest(), + Stomach = new Stomach(), + LeftArm = new LeftArm(), + RightArm = new RightArm(), + LeftLeg = new LeftLeg(), + RightLeg = new RightLeg(), + }; + + return profile; } /// @@ -36,7 +81,53 @@ public class HealthHelper string sessionID, bool isDead) { - throw new NotImplementedException(); + var fullProfile = _saveServer.GetProfile(sessionID); + var profileEdition = fullProfile.ProfileInfo.Edition; + var profileSide = fullProfile.CharacterData.PmcData.Info.Side; + + var defaultTemperature = + _databaseService.GetProfiles() + .GetByJsonProp(profileEdition) + .GetByJsonProp(profileSide.ToLower()) + ?.Character?.Health?.Temperature ?? new CurrentMinMax { Current = 36.6 }; + + StoreHydrationEnergyTempInProfile( + fullProfile, + postRaidHealth.Hydration.Current ?? 0, + postRaidHealth.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) { + // Effects + if (postRaidHealth.BodyParts[bodyPart.Key].Effects is not null) { + // fullProfile.VitalityData.Effects[bodyPart.Key] = postRaidHealth.BodyParts[bodyPart.Key].Effects; + // TODO: this will need to change, typing is all fucked up + } + + // Limb hp + if (!isDead) + { + // Player alive, not is limb alive + var byJsonProp = fullProfile.VitalityData.Health.GetByJsonProp(bodyPart.Key); + byJsonProp = postRaidHealth.BodyParts[bodyPart.Key].Health.Current ?? 0; + } else { + var byJsonProp = fullProfile.VitalityData.Health.GetByJsonProp(bodyPart.Key); + byJsonProp = (pmcData.Health.BodyParts[bodyPart.Key].Health.Maximum * _healthConfig.HealthMultipliers.Death) ?? 0; + } + } + + TransferPostRaidLimbEffectsToProfile(postRaidHealth.BodyParts, pmcData); + + // Adjust hydration/energy/temp and limb hp using temp storage hydated above + SaveHealth(pmcData, sessionID); + + // Reset temp storage + ResetVitality(sessionID); + + // Update last edited timestamp + pmcData.Health.UpdateTime = _timeUtil.GetTimeStamp(); } protected void StoreHydrationEnergyTempInProfile( @@ -45,7 +136,9 @@ public class HealthHelper double energy, double temprature) { - throw new NotImplementedException(); + fullProfile.VitalityData.Health.Hydration = hydration; + fullProfile.VitalityData.Health.Energy = energy; + fullProfile.VitalityData.Health.Temperature = temprature; } /// @@ -55,7 +148,37 @@ public class HealthHelper /// Player profile on server protected void TransferPostRaidLimbEffectsToProfile(Dictionary postRaidBodyParts, PmcData profileData) { - throw new NotImplementedException(); + // Iterate over each body part + List effectsToIgnore = ["Dehydration", "Exhaustion"]; + foreach (var bodyPartId in postRaidBodyParts) { + // Get effects on body part from profile + var bodyPartEffects = postRaidBodyParts[bodyPartId.Key].Effects; + foreach (var effect in bodyPartEffects) { + var effectDetails = bodyPartEffects[effect.Key]; + + // Null guard + profileData.Health.BodyParts[bodyPartId.Key].Effects ??= new Dictionary(); + + // Effect already exists on limb in server profile, skip + var profileBodyPartEffects = profileData.Health.BodyParts[bodyPartId.Key].Effects; + if (profileBodyPartEffects[effect.Key] is not null) { + if (effectsToIgnore.Contains(effect.Key)) { + // Get rid of certain effects we dont want to persist out of raid + profileBodyPartEffects[effect.Key] = null; + } + + continue; + } + + if (effectsToIgnore.Contains(effect.Key)) { + // Do not pass some effects to out of raid profile + continue; + } + + // Add effect to server profile + profileBodyPartEffects[effect.Key] = new BodyPartEffectProperties { Time = effectDetails.Time ?? -1 }; + } + } } /// @@ -73,7 +196,45 @@ public class HealthHelper bool addEffects = true, bool deleteExistingEffects = true) { - throw new NotImplementedException(); + var postRaidBodyParts = request.Health; // post raid health settings + var fullProfile = _saveServer.GetProfile(sessionID); + var profileEffects = fullProfile.VitalityData.Effects; + + StoreHydrationEnergyTempInProfile(fullProfile, request.Hydration ?? 0, request.Energy ?? 0, request.Temperature ?? 0); + + // Process request data into profile + foreach (var bodyPart in postRaidBodyParts) { + // Transfer effects from request to profile + if (bodyPart.Effects is not null) { + // profileEffects[bodyPart] = postRaidBodyParts[bodyPart].Effects; + } + + if (request.IsAlive ?? false) { + // Player alive, not is limb alive + // fullProfile.VitalityData.Health[bodyPart] = postRaidBodyParts[bodyPart].Current; + } else { + // fullProfile.VitalityData.Health[bodyPart] = + // pmcData.Health.BodyParts[bodyPart].Health.Maximum * _healthConfig.HealthMultipliers.Death; + }// TODO: this will need to change, typing is all fucked up + } + + // Add effects to body parts if enabled + if (addEffects) { + SaveEffects( + pmcData, + sessionID, + _cloner.Clone(_saveServer.GetProfile(sessionID).VitalityData.Effects), + deleteExistingEffects + ); + } + + // Adjust hydration/energy/temp and limb hp + SaveHealth(pmcData, sessionID); + + ResetVitality(sessionID); + + // Update last edited timestamp + pmcData.Health.UpdateTime = _timeUtil.GetTimeStamp(); } /// @@ -83,7 +244,40 @@ public class HealthHelper /// Session id protected void SaveHealth(PmcData pmcData, string sessionID) { - throw new NotImplementedException(); + // TODO: this will need to change, typing is all fucked up + // if (!_healthConfig.Save.Health) { + // return; + // } + // + // var profileHealth = _saveServer.GetProfile(sessionID).VitalityData.Health; + // foreach (var healthModifier in profileHealth) { + // let target = profileHealth[healthModifier]; + // + // if (["Hydration", "Energy", "Temperature"].includes(healthModifier)) { + // // Set resources + // if (target > pmcData.Health[healthModifier].Maximum) { + // target = pmcData.Health[healthModifier].Maximum; + // } + // + // pmcData.Health[healthModifier].Current = Math.round(target); + // } else { + // // Over max, limit + // if (target > pmcData.Health.BodyParts[healthModifier].Health.Maximum) { + // target = pmcData.Health.BodyParts[healthModifier].Health.Maximum; + // } + // + // // Part was zeroed out in raid + // if (target === 0) { + // // Blacked body part + // target = Math.round( + // pmcData.Health.BodyParts[healthModifier].Health.Maximum * + // this.healthConfig.healthMultipliers.blacked, + // ); + // } + // + // pmcData.Health.BodyParts[healthModifier].Health.Current = Math.round(target); + // } + // } } /// @@ -101,7 +295,42 @@ public class HealthHelper Effects bodyPartsWithEffects, bool deleteExistingEffects = true) { - throw new NotImplementedException(); + // TODO: this will need to change, typing is all fucked up + // if (!this.healthConfig.save.effects) { + // return; + // } + // + // for (const bodyPart in bodyPartsWithEffects) { + // // clear effects from profile bodyPart + // if (deleteExistingEffects) { + // // biome-ignore lint/performance/noDelete: Delete is fine here as we entirely want to get rid of the effect. + // delete pmcData.Health.BodyParts[bodyPart].Effects; + // } + // + // for (const effectType in bodyPartsWithEffects[bodyPart]) { + // if (typeof effectType !== "string") { + // this.logger.warning(`Effect ${effectType} on body part ${bodyPart} not a string, report this`); + // } + // + // // // data can be index or the effect string (e.g. "Fracture") itself + // // const effect = /^-?\d+$/.test(effectValue) // is an int + // // ? nodeEffects[bodyPart][effectValue] + // // : effectValue; + // let time = bodyPartsWithEffects[bodyPart][effectType]; + // if (time) { + // // Sometimes the value can be Infinity instead of -1, blame HealthListener.cs in modules + // if (time === "Infinity") { + // this.logger.warning( + // `Effect ${effectType} found with value of Infinity, changed to -1, this is an issue with HealthListener.cs`, + // ); + // time = -1; + // } + // this.addEffect(pmcData, bodyPart, effectType, time); + // } else { + // this.addEffect(pmcData, bodyPart, effectType); + // } + // } + // } } /// @@ -113,6 +342,18 @@ public class HealthHelper /// How long the effect has left in seconds (-1 by default, no duration). protected void AddEffect(PmcData pmcData, string effectBodyPart, string effectType, int duration = -1) { - throw new NotImplementedException(); + // TODO: this will need to change, typing is all fucked up + // const profileBodyPart = pmcData.Health.BodyParts[effectBodyPart]; + // if (!profileBodyPart.Effects) { + // profileBodyPart.Effects = {}; + // } + // + // profileBodyPart.Effects[effectType] = { Time: duration }; + // + // // Delete empty property to prevent client bugs + // if (this.isEmpty(profileBodyPart.Effects)) { + // // biome-ignore lint/performance/noDelete: Delete is fine here, we're removing an empty property to prevent game bugs. + // delete profileBodyPart.Effects; + // } } }