Skip to content

Commit

Permalink
feat: configurable exfil prompt message
Browse files Browse the repository at this point in the history
  • Loading branch information
Trap committed Dec 28, 2024
1 parent 61ed558 commit b478dea
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 75 deletions.
16 changes: 13 additions & 3 deletions PTT-Plugin/Data/ExfilsTargets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,25 @@ public class ExfilTarget
public string transitSpawnPointId; // transit only
public string offraidPosition; // empty on transit

// TODO: i18n support (use the offraid position displayName)
public string GetCustomActionName()
{
if (isTransit)
{
return $"Transit to {transitMapId}";
string transitTemplate = "PTT_TRANSITS_PROMPT_TEMPLATE".Localized();
return string.Format(transitTemplate, transitMapId.Localized());
}

return $"Extract to {offraidPosition}";
string extractTemplate = "PTT_EXTRACTS_PROMPT_TEMPLATE".Localized();
string offraidPositionDisplayNameKey = $"PTT_OFFRAIDPOS_DISPLAY_NAME_{offraidPosition}";
string offraidPositionDisplayName = offraidPositionDisplayNameKey.Localized();

// when the offraid position display name cannot be resolved
if (offraidPositionDisplayName == offraidPositionDisplayNameKey)
{
return string.Format(extractTemplate, offraidPosition);
}

return string.Format(extractTemplate, offraidPositionDisplayName);
}

public string GetCustomExitName(ExfiltrationPoint exfil)
Expand Down
12 changes: 10 additions & 2 deletions configs/Default/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,14 @@
}
},
"exfiltrations_tooltips_template": "$exfilDisplayName -> $offraidPositionDisplayName",
"extracts_prompt_template": {
"en": "Extract: {0}",
"fr": "Extraction: {0}"
},
"transits_prompt_template": {
"en": "Transit: {0}",
"fr": "Transit: {0}"
},
"offraid_positions": {
"PraporHideout": {
"displayName": {
Expand Down Expand Up @@ -1186,8 +1194,8 @@
},
"FactoryZB-1011": {
"displayName": {
"en": "Customs/Factory",
"fr": "Douanes/Usine"
"en": "Bunker 11 Customs/Factory",
"fr": "Bunker 11 Douanes/Usine"
}
},
"SniperRB": {
Expand Down
5 changes: 5 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type AvailableLocales<T> = {
};

export type ByLocale<T> = Partial<AvailableLocales<T>>;
export type ByLocaleFull<T> = AvailableLocales<T>;

export const INDEXED_AVAILABLE_LOCALES: AvailableLocales<true> = {
ch: true,
Expand Down Expand Up @@ -75,6 +76,8 @@ export const isLocalAvailable = (givenLocale: string): boolean => {

export const AVAILABLE_LOCALES: string[] = Object.keys(INDEXED_AVAILABLE_LOCALES);

export const DEFAULT_FALLBACK_LANGUAGE = 'en';

type ByProfileId<T> = Record<string, T | undefined>;

export type MapName = keyof ByMap<unknown>;
Expand Down Expand Up @@ -226,6 +229,8 @@ type RawConfig = {
infiltrations_config?: InfiltrationsConfig;
exfiltrations_config?: Record<ExtractName, ExfiltrationConfig>; // TODO: validate in config-analysis
exfiltrations_tooltips_template?: string; // TODO(config-analysis): error when unknown template variable usage is found
transits_prompt_template?: ByLocale<string>; // TODO: validate in config-analysis
extracts_prompt_template?: ByLocale<string>; // TODO: validate in config-analysis
offraid_positions?: Record<OffraidPositionName, OffraidPositionDefinition>; // TODO: validate in config-analysis
};

Expand Down
32 changes: 32 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { StaticRouterModService } from '@spt/services/mod/staticRouter/Stat
import type { AccessVia, Config, PositionXYZ, Profile, SpawnPoint, StashConfig } from './config';
import { EMPTY_STASH, SLOT_ID_HIDEOUT, SLOT_ID_LOCKED_STASH, VANILLA_STASH_IDS } from './config';
import type { IItem } from '@spt/models/eft/common/tables/IItem';
import type { AllLocalesInDb } from './services/LocaleResolver';

export function checkAccessVia(access_via: AccessVia, value: string): boolean {
return access_via === '*' || access_via[0] === '*' || access_via.includes(value);
Expand Down Expand Up @@ -309,3 +310,34 @@ export const retrieveMainStashIdFromItems = (

return null;
};

export type LocalesMutationReport = {
nbLocalesImpacted: number;
nbTotalValuesUpdated: number;
};

export function mutateLocales(
allLocales: AllLocalesInDb,
partialLocales: Partial<AllLocalesInDb>,
): LocalesMutationReport {
const report: LocalesMutationReport = {
nbLocalesImpacted: 0,
nbTotalValuesUpdated: 0,
};

void Object.keys(allLocales).forEach(localeName => {
if (partialLocales[localeName]) {
const values = allLocales[localeName];
const newValues = partialLocales[localeName] ?? {};
const nbNewValues = Object.keys(newValues).length;

if (nbNewValues > 0) {
void Object.assign(values, newValues); // mutation here
report.nbLocalesImpacted += 1;
report.nbTotalValuesUpdated += nbNewValues;
}
}
});

return report;
}
100 changes: 97 additions & 3 deletions src/path-to-tarkov-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ import type { ConfigServer } from '@spt/servers/ConfigServer';
import type { DatabaseServer } from '@spt/servers/DatabaseServer';
import type { SaveServer } from '@spt/servers/SaveServer';

import type { Config, ConfigGetter, MapName, Profile, SpawnConfig } from './config';
import { MAPLIST, VANILLA_STASH_IDS } from './config';
import type {
ByLocale,
Config,
ConfigGetter,
LocaleName,
MapName,
Profile,
SpawnConfig,
} from './config';
import { DEFAULT_FALLBACK_LANGUAGE, MAPLIST, VANILLA_STASH_IDS } from './config';

import {
changeRestrictionsInRaid,
Expand All @@ -16,6 +24,7 @@ import {
disableRunThrough,
isIgnoredArea,
isPlayerSpawnPoint,
mutateLocales,
PTT_INFILTRATION,
} from './helpers';

Expand All @@ -40,6 +49,7 @@ import { fixRepeatableQuestsForPmc } from './fix-repeatable-quests';
import { KeepFoundInRaidTweak } from './keep-fir-tweak';
import { ExfilsTooltipsTemplater } from './services/ExfilsTooltipsTemplater';
import type { RaidCache } from './event-watcher';
import type { AllLocalesInDb } from './services/LocaleResolver';

type IndexedLocations = Record<string, ILocationBase>;

Expand Down Expand Up @@ -122,6 +132,8 @@ export class PathToTarkovController {

this.tradersAvailabilityService.init(quests);
this.injectTooltipsInLocales(config);
this.injectPromptTemplatesInLocales(config);
this.injectOffraidPositionDisplayNamesInLocales(config);
this.tradersController.initTraders(config);

const nbAddedTemplates = this.stashController.initSecondaryStashTemplates(
Expand Down Expand Up @@ -248,14 +260,96 @@ export class PathToTarkovController {
}

const partialLocales = this.tooltipsTemplater.computeLocales(config);
const report = ExfilsTooltipsTemplater.mutateLocales(allLocales, partialLocales);
const report = mutateLocales(allLocales, partialLocales);

const nbValuesUpdated = report.nbTotalValuesUpdated / report.nbLocalesImpacted;
this.debug(
`${nbValuesUpdated} extract tooltip values updated for ${report.nbLocalesImpacted} locales (total of ${report.nbTotalValuesUpdated})`,
);
}

// TODO: refactor in a dedicated service
// TODO: make it dynamic (aka intercept instead of mutating the db)
private injectPromptTemplatesInLocales(config: Config): void {
const allLocales = this.db.getTables()?.locales?.global;

if (!allLocales) {
throw new Error('Path To Tarkov: no locales found in db');
}

// 1. prepare transits_prompt_template
const DEFAULT_TRANSITS_PROMPT_TEMPLATE_KEY = 'PTT_TRANSITS_PROMPT_TEMPLATE';
const DEFAULT_TRANSITS_PROMPT_TEMPLATE_VALUE = 'Transit: {0}';
const DEFAULT_TRANSITS_PROMPT_TEMPLATE: ByLocale<string> = {
[DEFAULT_FALLBACK_LANGUAGE]: DEFAULT_TRANSITS_PROMPT_TEMPLATE_VALUE,
};
const transitsPromptTemplate =
config.transits_prompt_template ?? DEFAULT_TRANSITS_PROMPT_TEMPLATE;

// 2. prepare extracts_prompt_template
const DEFAULT_EXTRACTS_PROMPT_TEMPLATE_KEY = 'PTT_EXTRACTS_PROMPT_TEMPLATE';
const DEFAULT_EXTRACTS_PROMPT_TEMPLATE_VALUE = 'Extract: {0}';
const DEFAULT_EXTRACTS_PROMPT_TEMPLATE: ByLocale<string> = {
[DEFAULT_FALLBACK_LANGUAGE]: DEFAULT_EXTRACTS_PROMPT_TEMPLATE_VALUE,
};
const extractsPromptTemplate =
config.extracts_prompt_template ?? DEFAULT_EXTRACTS_PROMPT_TEMPLATE;

// 3. prepare new locales
const newLocales: Partial<AllLocalesInDb> = {};
Object.keys(allLocales).forEach(locale => {
const localeValues: Record<string, string> = {
[DEFAULT_TRANSITS_PROMPT_TEMPLATE_KEY]:
transitsPromptTemplate[locale as LocaleName] ?? DEFAULT_TRANSITS_PROMPT_TEMPLATE_VALUE,
[DEFAULT_EXTRACTS_PROMPT_TEMPLATE_KEY]:
extractsPromptTemplate[locale as LocaleName] ?? DEFAULT_EXTRACTS_PROMPT_TEMPLATE_VALUE,
};
newLocales[locale] = localeValues;
});

// 4. mutate locales
const report = mutateLocales(allLocales, newLocales);

const nbValuesUpdated = report.nbTotalValuesUpdated / report.nbLocalesImpacted;
this.debug(
`${nbValuesUpdated} prompt templates values updated for ${report.nbLocalesImpacted} locales (total of ${report.nbTotalValuesUpdated})`,
);
}

private injectOffraidPositionDisplayNamesInLocales(config: Config): void {
const allLocales = this.db.getTables()?.locales?.global;

if (!allLocales) {
throw new Error('Path To Tarkov: no locales found in db');
}

// 1. create new locales
const newLocales: Partial<AllLocalesInDb> = {};
Object.keys(allLocales).forEach(locale => {
const localeValues: Record<string, string> = {};

const offraidPositions = config.offraid_positions ?? {};
Object.keys(offraidPositions).forEach(offraidPosition => {
const displayNameValue = ExfilsTooltipsTemplater.resolveOffraidPositionDisplayName(config, {
offraidPosition,
locale: locale as LocaleName,
});

localeValues[`PTT_OFFRAIDPOS_DISPLAY_NAME_${offraidPosition}`] = displayNameValue;
});

newLocales[locale] = localeValues;
});

// 2. mutate locales
const report = mutateLocales(allLocales, newLocales);

const nbValuesUpdated = report.nbTotalValuesUpdated / report.nbLocalesImpacted;
this.debug(
`${nbValuesUpdated} prompt templates values updated for ${report.nbLocalesImpacted} locales (total of ${report.nbTotalValuesUpdated})`,
);
}

private getRespawnOffraidPosition = (sessionId: string): string => {
const profile: Profile = this.saveServer.getProfile(sessionId);
const profileTemplateId = profile.info.edition;
Expand Down
76 changes: 9 additions & 67 deletions src/services/ExfilsTooltipsTemplater.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,25 @@
import { parseExilTargetFromPTTConfig } from '../exfils-targets';
import {
AVAILABLE_LOCALES,
DEFAULT_FALLBACK_LANGUAGE,
type ByLocale,
type Config,
type LocaleName,
type MapName,
} from '../config';
import { deepClone } from '../utils';

const DEFAULT_FALLBACK_LANGUAGE = 'en';
import type { AllLocalesInDb } from './LocaleResolver';
import { LocaleResolver } from './LocaleResolver';
import { mutateLocales } from '../helpers';

const EXFIL_DISPLAY_NAME_VARIABLE = '$exfilDisplayName';
const OFFRAID_POSITION_DISPLAY_NAME_VARIABLE = '$offraidPositionDisplayName';

type AllLocalesInDb = Record<string, Record<string, string>>;

type LocaleKey = string;

type LocaleKeysLowerCaseMapping = {
[localeName: string]: {
[lowerCasedLocaleKey: string]: LocaleKey;
};
};

class ExfilsLocaleResolver {
private readonly localeKeysMapping: LocaleKeysLowerCaseMapping = {};

constructor(allLocales: AllLocalesInDb) {
void Object.keys(allLocales).forEach(localeName => {
const localeValues: Record<string, string> = {};
this.localeKeysMapping[localeName] = localeValues;

void Object.keys(allLocales[localeName]).forEach(localeKey => {
localeValues[localeKey.toLowerCase()] = localeKey;
});
});
}

public retrieveKey(exfilName: string, locale: LocaleName): string {
return this.localeKeysMapping?.[locale]?.[exfilName.toLowerCase()] ?? exfilName;
}
}

export type MinimumConfigForTooltipsTemplater = Pick<
Config,
'exfiltrations' | 'exfiltrations_config' | 'exfiltrations_tooltips_template' | 'offraid_positions'
>;

export type LocalesMutationReport = {
nbLocalesImpacted: number;
nbTotalValuesUpdated: number;
};

export type ComputeLocaleValueParameter = {
locale: LocaleName;
localeKey: string;
Expand All @@ -64,11 +32,11 @@ export class ExfilsTooltipsTemplater {

// this is used to be sure to keep the vanilla locales even after mutations are applied to the database
private readonly snapshotLocales: AllLocalesInDb;
private readonly localeResolver: ExfilsLocaleResolver;
private readonly localeResolver: LocaleResolver;

constructor(allLocales: AllLocalesInDb) {
this.snapshotLocales = deepClone(allLocales);
this.localeResolver = new ExfilsLocaleResolver(allLocales);
this.localeResolver = new LocaleResolver(allLocales);
}

public computeLocales(config: MinimumConfigForTooltipsTemplater): Partial<AllLocalesInDb> {
Expand Down Expand Up @@ -123,36 +91,10 @@ export class ExfilsTooltipsTemplater {
},
{},
);
void ExfilsTooltipsTemplater.mutateLocales(mergedLocales, partialLocales);
void mutateLocales(mergedLocales, partialLocales);
return mergedLocales[locale];
}

public static mutateLocales(
allLocales: AllLocalesInDb,
partialLocales: Partial<AllLocalesInDb>,
): LocalesMutationReport {
const report: LocalesMutationReport = {
nbLocalesImpacted: 0,
nbTotalValuesUpdated: 0,
};

void Object.keys(allLocales).forEach(localeName => {
if (partialLocales[localeName]) {
const values = allLocales[localeName];
const newValues = partialLocales[localeName] ?? {};
const nbNewValues = Object.keys(newValues).length;

if (nbNewValues > 0) {
void Object.assign(values, newValues); // mutation here
report.nbLocalesImpacted += 1;
report.nbTotalValuesUpdated += nbNewValues;
}
}
});

return report;
}

private computeLocaleValue(
config: MinimumConfigForTooltipsTemplater,
params: ComputeLocaleValueParameter,
Expand Down Expand Up @@ -194,9 +136,9 @@ export class ExfilsTooltipsTemplater {
return resolvedDisplayName;
}

private static resolveOffraidPositionDisplayName(
public static resolveOffraidPositionDisplayName(
config: MinimumConfigForTooltipsTemplater,
{ offraidPosition, locale }: ComputeLocaleValueParameter,
{ offraidPosition, locale }: { offraidPosition: string; locale: LocaleName },
): string {
const offraidPositionDefinition = config.offraid_positions?.[offraidPosition];
const resolvedDisplayName = ExfilsTooltipsTemplater.resolveDisplayName(
Expand Down
Loading

0 comments on commit b478dea

Please sign in to comment.