From 9c58e2e0e54d2479ba1679f617e60f975524a5d3 Mon Sep 17 00:00:00 2001 From: Dev Date: Fri, 18 Oct 2024 11:39:51 +0100 Subject: [PATCH] Loosened bot name limit to 19 characters Moved value into config Added `botConfig.botRolesThatMustHaveUniqueName` Refactored `getPmcNicknameOfMaxLength()`, removed all recursion + handles when no name is below desired length Refactored `generateUniqueBotNickname()` to handle PMC names differently, use centralised pmc name function Updated ragfair to utilise maxlength value from bot config --- project/assets/configs/bot.json | 4 ++- project/src/controllers/GameController.ts | 2 +- project/src/generators/BotGenerator.ts | 15 +++++----- .../src/generators/RagfairOfferGenerator.ts | 7 +++-- project/src/helpers/BotHelper.ts | 25 ++++++++++++---- project/src/helpers/RagfairOfferHelper.ts | 5 +++- project/src/models/spt/config/IBotConfig.ts | 4 +++ project/src/services/BotNameService.ts | 30 +++++++++++-------- 8 files changed, 61 insertions(+), 31 deletions(-) diff --git a/project/assets/configs/bot.json b/project/assets/configs/bot.json index cb3f7643..eac82fe4 100644 --- a/project/assets/configs/bot.json +++ b/project/assets/configs/bot.json @@ -2835,5 +2835,7 @@ "max": 100 } } - } + }, + "botNameLengthLimit": 19, + "botRolesThatMustHaveUniqueName": ["assault", "pmcusec", "pmcbear"] } diff --git a/project/src/controllers/GameController.ts b/project/src/controllers/GameController.ts index e16910a7..5411730f 100644 --- a/project/src/controllers/GameController.ts +++ b/project/src/controllers/GameController.ts @@ -910,7 +910,7 @@ export class GameController { const bots = this.databaseService.getBots().types; // Official names can only be 15 chars in length - if (playerName.length > 15) { + if (playerName.length > this.botConfig.botNameLengthLimit) { return; } diff --git a/project/src/generators/BotGenerator.ts b/project/src/generators/BotGenerator.ts index 51e166c7..5380e81e 100644 --- a/project/src/generators/BotGenerator.ts +++ b/project/src/generators/BotGenerator.ts @@ -152,7 +152,7 @@ export class BotGenerator { botJsonTemplate: IBotType, botGenerationDetails: BotGenerationDetails, ): IBotBase { - const botRole = botGenerationDetails.role.toLowerCase(); + const botRoleLowercase = botGenerationDetails.role.toLowerCase(); const botLevel = this.botLevelGenerator.generateBotLevel( botJsonTemplate.experience.level, botGenerationDetails, @@ -169,12 +169,10 @@ export class BotGenerator { } bot.Info.Nickname = this.botNameService.generateUniqueBotNickname( - botJsonTemplate.firstName, - botJsonTemplate.lastName, + botJsonTemplate, botGenerationDetails, - botRole, - ["assault", "pmcusec", "pmcbear"], - sessionId, + botRoleLowercase, + this.botConfig.botRolesThatMustHaveUniqueName, ); if (!this.seasonalEventService.christmasEventEnabled()) { @@ -213,18 +211,19 @@ export class BotGenerator { } } + // Add drip this.setBotAppearance(bot, botJsonTemplate.appearance, botGenerationDetails); bot.Inventory = this.botInventoryGenerator.generateInventory( sessionId, botJsonTemplate, - botRole, + botRoleLowercase, botGenerationDetails.isPmc, botLevel.level, bot.Info.GameVersion, ); - if (this.botConfig.botRolesWithDogTags.includes(botRole)) { + if (this.botConfig.botRolesWithDogTags.includes(botRoleLowercase)) { this.addDogtagToBot(bot); } diff --git a/project/src/generators/RagfairOfferGenerator.ts b/project/src/generators/RagfairOfferGenerator.ts index 6329f02c..2e7d8529 100644 --- a/project/src/generators/RagfairOfferGenerator.ts +++ b/project/src/generators/RagfairOfferGenerator.ts @@ -14,6 +14,7 @@ import { BaseClasses } from "@spt/models/enums/BaseClasses"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { MemberCategory } from "@spt/models/enums/MemberCategory"; import { Money } from "@spt/models/enums/Money"; +import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; import { Condition, Dynamic, @@ -37,6 +38,7 @@ import { inject, injectable } from "tsyringe"; @injectable() export class RagfairOfferGenerator { protected ragfairConfig: IRagfairConfig; + protected botConfig: IBotConfig; protected allowedFleaPriceItemsForBarter: { tpl: string; price: number }[]; /** Internal counter to ensure each offer created has a unique value for its intId property */ @@ -65,6 +67,7 @@ export class RagfairOfferGenerator { @inject("PrimaryCloner") protected cloner: ICloner, ) { this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); + this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); } /** @@ -192,11 +195,11 @@ export class RagfairOfferGenerator { }; } - // Regular old fake pmc offer + // Fake pmc offer return { id: userID, memberType: MemberCategory.DEFAULT, - nickname: this.botHelper.getPmcNicknameOfMaxLength(userID, 50), + nickname: this.botHelper.getPmcNicknameOfMaxLength(this.botConfig.botNameLengthLimit), rating: this.randomUtil.getFloat( this.ragfairConfig.dynamic.rating.min, this.ragfairConfig.dynamic.rating.max, diff --git a/project/src/helpers/BotHelper.ts b/project/src/helpers/BotHelper.ts index be75c47d..7899092d 100644 --- a/project/src/helpers/BotHelper.ts +++ b/project/src/helpers/BotHelper.ts @@ -7,6 +7,7 @@ import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { DatabaseService } from "@spt/services/DatabaseService"; import { RandomUtil } from "@spt/utils/RandomUtil"; +import { max } from "date-fns"; import { inject, injectable } from "tsyringe"; @injectable() @@ -168,10 +169,24 @@ export class BotHelper { return this.randomUtil.getChance100(this.pmcConfig.isUsec) ? "Usec" : "Bear"; } - public getPmcNicknameOfMaxLength(userId: string, maxLength: number): string { - // recurivse if name is longer than max characters allowed (15 characters) - const randomType = this.randomUtil.getInt(0, 1) === 0 ? "usec" : "bear"; - const name = this.randomUtil.getStringArrayValue(this.databaseService.getBots().types[randomType].firstName); - return name.length > maxLength ? this.getPmcNicknameOfMaxLength(userId, maxLength) : name; + /** + * Get a name from a PMC that fits the desired length + * @param maxLength Max length of name, inclusive + * @param side OPTIONAL - what side PMC to get name from (usec/bear) + * @returns name of PMC + */ + public getPmcNicknameOfMaxLength(maxLength: number, side?: string): string { + const randomType = side ? side : this.randomUtil.getInt(0, 1) === 0 ? "usec" : "bear"; + const allNames = this.databaseService.getBots().types[randomType].firstName; + const filteredNames = allNames.filter((name) => name.length <= maxLength); + if (filteredNames.length === 0) { + this.logger.warning( + `Unable to filter: ${randomType} PMC names to only those under: ${maxLength}, none found that match that criteria, selecting from entire name pool instead`, + ); + + return this.randomUtil.getStringArrayValue(allNames); + } + + return this.randomUtil.getStringArrayValue(filteredNames); } } diff --git a/project/src/helpers/RagfairOfferHelper.ts b/project/src/helpers/RagfairOfferHelper.ts index 049cb9f3..85461973 100644 --- a/project/src/helpers/RagfairOfferHelper.ts +++ b/project/src/helpers/RagfairOfferHelper.ts @@ -21,6 +21,7 @@ import { MemberCategory } from "@spt/models/enums/MemberCategory"; import { MessageType } from "@spt/models/enums/MessageType"; import { RagfairSort } from "@spt/models/enums/RagfairSort"; import { Traders } from "@spt/models/enums/Traders"; +import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; import { IQuestConfig } from "@spt/models/spt/config/IQuestConfig"; import { IRagfairConfig, ITieredFlea } from "@spt/models/spt/config/IRagfairConfig"; import { ILogger } from "@spt/models/spt/utils/ILogger"; @@ -42,6 +43,7 @@ export class RagfairOfferHelper { protected static goodSoldTemplate = "5bdabfb886f7743e152e867e 0"; // Your {soldItem} {itemCount} items were bought by {buyerNickname}. protected ragfairConfig: IRagfairConfig; protected questConfig: IQuestConfig; + protected botConfig: IBotConfig; constructor( @inject("PrimaryLogger") protected logger: ILogger, @@ -69,6 +71,7 @@ export class RagfairOfferHelper { ) { this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); + this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); } /** @@ -565,7 +568,7 @@ export class RagfairOfferHelper { // Used to replace tokens in sold message sent to player const tplVars: ISystemData = { soldItem: globalLocales[`${itemTpl} Name`] || itemTpl, - buyerNickname: this.botHelper.getPmcNicknameOfMaxLength(this.hashUtil.generate(), 15), + buyerNickname: this.botHelper.getPmcNicknameOfMaxLength(this.botConfig.botNameLengthLimit), itemCount: boughtAmount, }; diff --git a/project/src/models/spt/config/IBotConfig.ts b/project/src/models/spt/config/IBotConfig.ts index 2ba9367a..d731c48c 100644 --- a/project/src/models/spt/config/IBotConfig.ts +++ b/project/src/models/spt/config/IBotConfig.ts @@ -45,6 +45,10 @@ export interface IBotConfig extends IBaseConfig { /** What bottypes should be excluded from having loot generated on them (backpack/pocket/vest) does not disable food/drink/special/ */ disableLootOnBotTypes: string[]; assaultToBossConversion: IAssaultToBossConversion; + /** Max length a bots name can be */ + botNameLengthLimit: number; + /** Bot roles that must have a unique name when generated vs other bots in raid */ + botRolesThatMustHaveUniqueName: string[]; } export interface IAssaultToBossConversion { diff --git a/project/src/services/BotNameService.ts b/project/src/services/BotNameService.ts index 12a6d42d..12b612e3 100644 --- a/project/src/services/BotNameService.ts +++ b/project/src/services/BotNameService.ts @@ -1,4 +1,6 @@ +import { BotHelper } from "@spt/helpers/BotHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import { IBotType } from "@spt/models/eft/common/tables/IBotType"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { BotGenerationDetails } from "@spt/models/spt/bots/BotGenerationDetails"; import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; @@ -21,6 +23,7 @@ export class BotNameService { @inject("PrimaryLogger") protected logger: ILogger, @inject("RandomUtil") protected randomUtil: RandomUtil, @inject("ProfileHelper") protected profileHelper: ProfileHelper, + @inject("BotHelper") protected botHelper: BotHelper, @inject("DatabaseService") protected databaseService: DatabaseService, @inject("LocalisationService") protected localisationService: LocalisationService, @inject("ConfigServer") protected configServer: ConfigServer, @@ -41,8 +44,7 @@ export class BotNameService { /** * Create a unique bot nickname - * @param firstNames FIRST names to choose from - * @param lastNames OPTIONAL: Names to choose from + * @param botJsonTemplate bot JSON data from db * @param botGenerationDetails * @param botRole role of bot e.g. assault * @param uniqueRoles Lowercase roles to always make unique @@ -50,22 +52,24 @@ export class BotNameService { * @returns Nickname for bot */ public generateUniqueBotNickname( - firstNames: string[], - lastNames: string[], + botJsonTemplate: IBotType, botGenerationDetails: BotGenerationDetails, botRole: string, uniqueRoles?: string[], - sessionId?: string, ): string { + const isPmc = botGenerationDetails.isPmc; + const isPlayerScav = botGenerationDetails.isPlayerScav; + const simulateScavName = isPlayerScav ? false : this.shouldSimulatePlayerScavName(botRole); + const showTypeInNickname = this.botConfig.showTypeInNickname && !isPlayerScav; + const roleShouldBeUnique = uniqueRoles.includes(botRole.toLowerCase()); + let isUnique = true; let attempts = 0; - while (attempts <= 5) { - const isPlayerScav = botGenerationDetails.isPlayerScav; - const simulateScavName = isPlayerScav ? false : this.shouldSimulatePlayerScavName(botRole); - - // Get basic name with no whitespace trimmed off sides - let name = `${this.randomUtil.getArrayValue(firstNames)} ${this.randomUtil.getArrayValue(lastNames) || ""}`; + // Get bot name with leading/trailing whitespace removed + let name = isPmc // Explicit handling of PMCs, all other bots will get "first_name last_name" + ? this.botHelper.getPmcNicknameOfMaxLength(this.botConfig.botNameLengthLimit, botGenerationDetails.side) + : `${this.randomUtil.getArrayValue(botJsonTemplate.firstName)} ${this.randomUtil.getArrayValue(botJsonTemplate.lastName) || ""}`; name = name.trim(); // Simulate bot looking like a player scav with the PMC name in brackets. @@ -75,7 +79,7 @@ export class BotNameService { } // Config is set to add role to end of bot name - if (this.botConfig.showTypeInNickname && !isPlayerScav) { + if (showTypeInNickname) { name += ` ${botRole}`; } @@ -86,7 +90,7 @@ export class BotNameService { } // Is this a role that must be unique - if (uniqueRoles.includes(botRole.toLowerCase())) { + if (roleShouldBeUnique) { // Check name in cache isUnique = !this.usedNameCache.has(name); if (!isUnique) {