Rework of post-raid scav/pmc profile handling:

Moved logic out of `updateProfileBaseStats()` and into separate functions for pmc/scav, left profile-agnostic code alone
new functions `updatePmcProfileDataPostRaid` and `updateScavProfileDataPostRaid`

scav - Only copy active quest progress from client profile to server scav profile
scav - dont attempt to update trader standings, none exist on scav profile
scav - dont transfer psot-raid limb damage to server profile

Update quest status values similarly to PMC quests post raid to ensure they're consistent with existing quest data in profile
Simplifies `migrateScavQuestProgressToPmcProfile`

made various warnings debug instead
This commit is contained in:
Dev 2023-11-20 16:33:04 +00:00
parent d2209114c9
commit ddb9917c6b
4 changed files with 121 additions and 72 deletions

View File

@ -107,19 +107,20 @@ export class InraidController
*/
protected savePmcProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void
{
const serverProfile = this.saveServer.getProfile(sessionID);
const locationName = serverProfile.inraid.location.toLowerCase();
const serveProfile = this.saveServer.getProfile(sessionID);
const locationName = serveProfile.inraid.location.toLowerCase();
const map: ILocationBase = this.databaseServer.getTables().locations[locationName].base;
const mapHasInsuranceEnabled = map.Insurance;
let serverPmcData = serverProfile.characters.pmc;
let serverPmcProfile = serveProfile.characters.pmc;
const isDead = this.isPlayerDead(postRaidRequest.exit);
const preRaidGear = this.inRaidHelper.getPlayerGear(serverPmcData.Inventory.items);
const preRaidGear = this.inRaidHelper.getPlayerGear(serverPmcProfile.Inventory.items);
serverProfile.inraid.character = "pmc";
serveProfile.inraid.character = "pmc";
serverPmcData = this.inRaidHelper.updateProfileBaseStats(serverPmcData, postRaidRequest, sessionID);
this.inRaidHelper.updateProfileBaseStats(serverPmcProfile, postRaidRequest, sessionID);
this.inRaidHelper.updatePmcProfileDataPostRaid(serverPmcProfile, postRaidRequest, sessionID);
// Check for exit status
this.markOrRemoveFoundInRaidItems(postRaidRequest);
@ -127,20 +128,20 @@ export class InraidController
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(
postRaidRequest.profile,
postRaidRequest.profile.Inventory.items,
serverPmcData.InsuredItems,
serverPmcProfile.InsuredItems,
postRaidRequest.profile.Inventory.fastPanel,
);
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
// Purge profile of equipment/container items
serverPmcData = this.inRaidHelper.setInventory(sessionID, serverPmcData, postRaidRequest.profile);
serverPmcProfile = this.inRaidHelper.setInventory(sessionID, serverPmcProfile, postRaidRequest.profile);
this.healthHelper.saveVitality(serverPmcData, postRaidRequest.health, sessionID);
this.healthHelper.saveVitality(serverPmcProfile, postRaidRequest.health, sessionID);
// Remove inventory if player died and send insurance items
if (mapHasInsuranceEnabled)
{
this.insuranceService.storeLostGear(serverPmcData, postRaidRequest, preRaidGear, sessionID, isDead);
this.insuranceService.storeLostGear(serverPmcProfile, postRaidRequest, preRaidGear, sessionID, isDead);
}
else
{
@ -151,7 +152,7 @@ export class InraidController
if (locationName === "lighthouse" && postRaidRequest.profile.Info.Side.toLowerCase() === "usec")
{
// Decrement counter if it exists, don't go below 0
const remainingCounter = serverPmcData?.Stats.Eft.OverallCounters.Items.find((x) =>
const remainingCounter = serverPmcProfile?.Stats.Eft.OverallCounters.Items.find((x) =>
x.Key.includes("UsecRaidRemainKills")
);
if (remainingCounter?.Value > 0)
@ -164,12 +165,12 @@ export class InraidController
{
this.pmcChatResponseService.sendKillerResponse(
sessionID,
serverPmcData,
serverPmcProfile,
postRaidRequest.profile.Stats.Eft.Aggressor,
);
this.matchBotDetailsCacheService.clearCache();
serverPmcData = this.performPostRaidActionsWhenDead(postRaidRequest, serverPmcData, sessionID);
serverPmcProfile = this.performPostRaidActionsWhenDead(postRaidRequest, serverPmcProfile, sessionID);
}
const victims = postRaidRequest.profile.Stats.Eft.Victims.filter((x) =>
@ -177,12 +178,12 @@ export class InraidController
);
if (victims?.length > 0)
{
this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcData);
this.pmcChatResponseService.sendVictimResponse(sessionID, victims, serverPmcProfile);
}
if (mapHasInsuranceEnabled)
{
this.insuranceService.sendInsuredItems(serverPmcData, sessionID, map.Id);
this.insuranceService.sendInsuredItems(serverPmcProfile, sessionID, map.Id);
}
}
@ -274,39 +275,43 @@ export class InraidController
*/
protected savePlayerScavProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void
{
const pmcData = this.profileHelper.getPmcProfile(sessionID);
let scavData = this.profileHelper.getScavProfile(sessionID);
const serverPmcProfile = this.profileHelper.getPmcProfile(sessionID);
const serverScavProfile = this.profileHelper.getScavProfile(sessionID);
const isDead = this.isPlayerDead(postRaidRequest.exit);
this.saveServer.getProfile(sessionID).inraid.character = "scav";
scavData = this.inRaidHelper.updateProfileBaseStats(scavData, postRaidRequest, sessionID);
this.inRaidHelper.updateProfileBaseStats(serverScavProfile, postRaidRequest, sessionID);
this.inRaidHelper.updateScavProfileDataPostRaid(serverScavProfile, postRaidRequest, sessionID);
// Completing scav quests create ConditionCounters, these values need to be transported to the PMC profile
if (this.profileHasConditionCounters(scavData))
if (this.profileHasConditionCounters(serverScavProfile))
{
// Scav quest progress needs to be moved to pmc so player can see it in menu / hand them in
this.migrateScavQuestProgressToPmcProfile(scavData, pmcData);
this.migrateScavQuestProgressToPmcProfile(serverScavProfile, serverPmcProfile);
}
// Check for exit status
// Change loot FiR status based on exit status
this.markOrRemoveFoundInRaidItems(postRaidRequest);
postRaidRequest.profile.Inventory.items = this.itemHelper.replaceIDs(
postRaidRequest.profile,
postRaidRequest.profile.Inventory.items,
pmcData.InsuredItems,
serverPmcProfile.InsuredItems,
postRaidRequest.profile.Inventory.fastPanel,
);
// Some items from client profile don't have upd objects when they're single stack items
this.inRaidHelper.addUpdToMoneyFromRaid(postRaidRequest.profile.Inventory.items);
this.handlePostRaidPlayerScavProcess(scavData, sessionID, postRaidRequest, pmcData, isDead);
// Reset hp/regenerate loot
this.handlePostRaidPlayerScavProcess(serverScavProfile, sessionID, postRaidRequest, serverPmcProfile, isDead);
}
/**
* Does provided profile contain any condition counters
* @param profile Profile to check for condition counters
* @returns
* @returns Profile has condition counters
*/
protected profileHasConditionCounters(profile: IPmcData): boolean
{
@ -318,6 +323,11 @@ export class InraidController
return profile.ConditionCounters.Counters.length > 0;
}
/**
* Scav quest progress isnt transferred automatically from scav to pmc, we do this manually
* @param scavProfile Scav profile with quest progress post-raid
* @param pmcProfile Server pmc profile to copy scav quest progress into
*/
protected migrateScavQuestProgressToPmcProfile(scavProfile: IPmcData, pmcProfile: IPmcData): void
{
for (const quest of scavProfile.Quests)
@ -329,17 +339,16 @@ export class InraidController
continue;
}
// Post-raid status is enum word e.g. `Started` but pmc quest status is number e.g. 2
// Status values mismatch or statusTimers counts mismatch
if (
quest.status !== <any>QuestStatus[pmcQuest.status]
quest.status !== pmcQuest.status
|| Object.keys(quest.statusTimers).length !== Object.keys(pmcQuest.statusTimers).length
)
{
this.logger.warning(
this.logger.debug(
`Quest: ${quest.qid} found in PMC profile has different status/statustimer. Scav: ${quest.status} vs PMC: ${pmcQuest.status}`,
);
pmcQuest.status = <any>QuestStatus[quest.status];
pmcQuest.status = quest.status;
// Copy status timers over + fix bad enum key for each
pmcQuest.statusTimers = quest.statusTimers;
@ -357,7 +366,7 @@ export class InraidController
// Loop over all scav counters and add into pmc profile
for (const scavCounter of scavProfile.ConditionCounters.Counters)
{
this.logger.warning(
this.logger.debug(
`Processing counter: ${scavCounter.id} value:${scavCounter.value} quest:${scavCounter.qid}`,
);
const counterInPmcProfile = pmcProfile.ConditionCounters.Counters.find((x) => x.id === scavCounter.id);
@ -369,14 +378,14 @@ export class InraidController
continue;
}
this.logger.warning(
this.logger.debug(
`Counter id: ${scavCounter.id} already exists in pmc profile! with value: ${counterInPmcProfile.value} for quest: ${counterInPmcProfile.qid}`,
);
// Only adjust counter value if its changed
if (counterInPmcProfile.value !== scavCounter.value)
{
this.logger.warning(`OVERWRITING with values: ${scavCounter.value} quest: ${scavCounter.qid}`);
this.logger.debug(`OVERWRITING with values: ${scavCounter.value} quest: ${scavCounter.qid}`);
counterInPmcProfile.value = scavCounter.value;
}
}

View File

@ -21,6 +21,7 @@ import { SaveServer } from "@spt-aki/servers/SaveServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { ProfileHelper } from "./ProfileHelper";
@injectable()
export class InRaidHelper
@ -35,6 +36,7 @@ export class InRaidHelper
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("QuestHelper") protected questHelper: QuestHelper,
@inject("PaymentHelper") protected paymentHelper: PaymentHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@ -62,9 +64,7 @@ export class InRaidHelper
*/
public addUpdToMoneyFromRaid(items: Item[]): void
{
for (const item of items)
{
if (this.paymentHelper.isMoneyTpl(item._tpl))
for (const item of items.filter(x => this.paymentHelper.isMoneyTpl(x._tpl)))
{
if (!item.upd)
{
@ -77,7 +77,6 @@ export class InRaidHelper
}
}
}
}
/**
* Add karma changes up and return the new value
@ -140,18 +139,44 @@ export class InRaidHelper
profileData: IPmcData,
saveProgressRequest: ISaveProgressRequestData,
sessionID: string,
): IPmcData
): void
{
// remove old skill fatigue
// Remove skill fatigue values
this.resetSkillPointsEarnedDuringRaid(saveProgressRequest.profile);
// set profile data
// Set profile data
profileData.Info.Level = saveProgressRequest.profile.Info.Level;
profileData.Skills = saveProgressRequest.profile.Skills;
profileData.Stats.Eft = saveProgressRequest.profile.Stats.Eft;
profileData.Encyclopedia = saveProgressRequest.profile.Encyclopedia;
profileData.ConditionCounters = saveProgressRequest.profile.ConditionCounters;
this.validateBackendCounters(saveProgressRequest, profileData);
profileData.SurvivorClass = saveProgressRequest.profile.SurvivorClass;
// Add experience points
profileData.Info.Experience += profileData.Stats.Eft.TotalSessionExperience;
profileData.Stats.Eft.TotalSessionExperience = 0;
this.setPlayerInRaidLocationStatusToNone(sessionID);
}
/**
* Reset the skill points earned in a raid to 0, ready for next raid
* @param profile Profile to update
*/
protected resetSkillPointsEarnedDuringRaid(profile: IPmcData): void
{
for (const skill of profile.Skills.Common)
{
skill.PointsEarnedDuringSession = 0.0;
}
}
/** Check counters are correct in profile */
protected validateBackendCounters(saveProgressRequest: ISaveProgressRequestData, profileData: IPmcData): void
{
for (const backendCounterKey in saveProgressRequest.profile.BackendCounters)
{
// Skip counters with no id
@ -178,33 +203,47 @@ export class InRaidHelper
if (matchingPreRaidCounter.value !== postRaidValue)
{
this.logger.error(
`Backendcounter: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`,
`Backendcounter: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`
);
}
}
this.processFailedQuests(sessionID, profileData, profileData.Quests, saveProgressRequest.profile.Quests);
profileData.Quests = saveProgressRequest.profile.Quests;
// Transfer effects from request to profile
this.transferPostRaidLimbEffectsToProfile(saveProgressRequest, profileData);
this.applyTraderStandingAdjustments(profileData.TradersInfo, saveProgressRequest.profile.TradersInfo);
profileData.SurvivorClass = saveProgressRequest.profile.SurvivorClass;
// Add experience points
profileData.Info.Experience += profileData.Stats.Eft.TotalSessionExperience;
profileData.Stats.Eft.TotalSessionExperience = 0;
this.setPlayerInRaidLocationStatusToNone(sessionID);
if (!saveProgressRequest.isPlayerScav)
{
this.profileFixerService.checkForAndFixPmcProfileIssues(profileData);
}
return profileData;
/**
* Update various serverPMC profile values; quests/limb hp/trader standing with values post-raic
* @param pmcData Server PMC profile
* @param saveProgressRequest Post-raid request data
* @param sessionId Session id
*/
public updatePmcProfileDataPostRaid(pmcData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionId: string): void
{
// Process failed quests then copy everything
this.processFailedQuests(sessionId, pmcData, pmcData.Quests, saveProgressRequest.profile.Quests);
pmcData.Quests = saveProgressRequest.profile.Quests;
// No need to do this for scav, old scav is deleted and new one generated
this.transferPostRaidLimbEffectsToProfile(saveProgressRequest, pmcData);
// Trader standing only occur on pmc profile, scav kills are handled in handlePostRaidPlayerScavKarmaChanges()
// Scav client data has standing values of 0 for all traders, DO NOT RUN ON SCAV RAIDS
this.applyTraderStandingAdjustments(pmcData.TradersInfo, saveProgressRequest.profile.TradersInfo);
this.profileFixerService.checkForAndFixPmcProfileIssues(pmcData);
}
/**
* Update scav quest values on server profile with updated values post-raid
* @param scavData Server scav profile
* @param saveProgressRequest Post-raid request data
* @param sessionId Session id
*/
public updateScavProfileDataPostRaid(scavData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionId: string): void
{
// Only copy active quests into scav profile // Progress will later to copied over to PMC profile
const existingActiveQuestIds = scavData.Quests.filter(x => x.status !== QuestStatus.AvailableForStart).map(x => x.qid);
scavData.Quests = saveProgressRequest.profile.Quests.filter(x => existingActiveQuestIds.includes(x.qid));
this.profileFixerService.checkForAndFixScavProfileIssues(scavData);
}
/**
@ -250,14 +289,6 @@ export class InRaidHelper
}
}
protected resetSkillPointsEarnedDuringRaid(profile: IPmcData): void
{
for (const skill of profile.Skills.Common)
{
skill.PointsEarnedDuringSession = 0.0;
}
}
/**
* Take body part effects from client profile and apply to server profile
* @param saveProgressRequest post-raid request

View File

@ -185,7 +185,7 @@ export class SaveServer
{
const filePath = `${this.profileFilepath}${sessionID}.json`;
// run callbacks
// Run pre-save callbacks before we save into json
for (const callback in this.onBeforeSaveCallbacks)
{
const previous = this.profiles[sessionID];

View File

@ -162,6 +162,15 @@ export class ProfileFixerService
}
}
/**
* Find issues in the scav profile data that may cause issues
* @param scavProfile profile to check and fix
*/
public checkForAndFixScavProfileIssues(scavProfile: IPmcData): void
{
this.updateProfileQuestDataValues(scavProfile);
}
protected addMissingGunStandContainerImprovements(pmcProfile: IPmcData): void
{
const weaponStandArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WEAPON_STAND);