mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-12 22:10:44 -05:00
Feature: Add system that allows PMCs to send text messages to player post-raic. Can be positive or negative (!40)
Co-authored-by: Dev <dev@noreply.dev.sp-tarkov.com> Reviewed-on: SPT-AKI/Server#40
This commit is contained in:
parent
e4bc6c8d68
commit
1137912d51
24
project/assets/configs/pmcchatresponse.json
Normal file
24
project/assets/configs/pmcchatresponse.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"victim": {
|
||||
"responseChancePercent": 25,
|
||||
"responseTypeWeights": {
|
||||
"positive": 8,
|
||||
"negative": 2,
|
||||
"plead": 2,
|
||||
},
|
||||
"stripCapitalisationChancePercent": 25,
|
||||
"allCapsChancePercent": 25,
|
||||
"appendBroToMessageEndChancePercent": 35
|
||||
},
|
||||
"killer": {
|
||||
"responseChancePercent": 20,
|
||||
"responseTypeWeights": {
|
||||
"positive": 5,
|
||||
"negative": 2,
|
||||
"plead": 2,
|
||||
},
|
||||
"stripCapitalisationChancePercent": 25,
|
||||
"allCapsChancePercent": 25,
|
||||
"appendBroToMessageEndChancePercent": 35
|
||||
}
|
||||
}
|
@ -147,8 +147,8 @@
|
||||
"ragfair-unable_to_find_item_in_inventory": "Unable to find item with id: {{id}} in inventory",
|
||||
"ragfair-unable_to_find_requested_items_in_inventory": "Unable to find any requested items in the inventory",
|
||||
"ragfair-unable_to_pay_commission_fee": "Unable to pay commission fee",
|
||||
"ragfair-offer_no_longer_exists": "Offer no longer exists",
|
||||
"ragfair-unable_to_purchase_0_count_item": "Unable to purchase item: %s with a count of 0",
|
||||
"ragfair-offer_no_longer_exists": "Offer no longer exists",
|
||||
"ragfair-unable_to_purchase_0_count_item": "Unable to purchase item: %s with a count of 0",
|
||||
"ragfair-unable_to_place_offer_with_no_requirements": "Unable to place offer with no requirements",
|
||||
"repeatable-accepted_repeatable_quest_not_found_in_active_quests": "Accepted a repeatable quest: %s which could not be found in the activeQuests array. Please report this bug",
|
||||
"repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive": "Generate Completion Quest: No items remain. Either Whitelist is too small or Blacklist too restrictive",
|
||||
@ -215,5 +215,30 @@
|
||||
"websocket-player_connected": "[WS] Player: %s has connected",
|
||||
"websocket-received_message": "[WS] Received message from user %s ",
|
||||
"websocket-socket_lost_deleting_handle": "[WS] Socket lost, deleting handle",
|
||||
"websocket-started": "Started websocket at %s"
|
||||
}
|
||||
"websocket-started": "Started websocket at %s",
|
||||
"pmcresponse-victim_positive_1": "Nice shot",
|
||||
"pmcresponse-victim_positive_2": "Great shot",
|
||||
"pmcresponse-victim_positive_3": "Good kill man",
|
||||
"pmcresponse-victim_positive_4": "Deserved kill, good one",
|
||||
"pmcresponse-victim_positive_5": "Lucky kill",
|
||||
"pmcresponse-victim_positive_6": "Good fight",
|
||||
"pmcresponse-victim_positive_7": "That was fair, nice kill",
|
||||
"pmcresponse-victim_positive_8": "You're a good shot, that's for sure",
|
||||
"pmcresponse-victim_negative_1": "Nice aimbot",
|
||||
"pmcresponse-victim_negative_2": "Cheap shot",
|
||||
"pmcresponse-victim_negative_3": "Wow esp much",
|
||||
"pmcresponse-victim_negative_4": "Cheap kill",
|
||||
"pmcresponse-victim_negative_5": "Nice cheese strats",
|
||||
"pmcresponse-victim_negative_6": "How much did your hacks cost",
|
||||
"pmcresponse-victim_negative_7": ":(",
|
||||
"pmcresponse-victim_negative_8": "I am malding so hard right now",
|
||||
"pmcresponse-victim_negative_9": "Good job sweatlord",
|
||||
"pmcresponse-victim_negative_10": "I was AFK!!",
|
||||
"pmcresponse-victim_negative_11": "Reported you for cheating :)",
|
||||
"pmcresponse-victim_plead_1": "I was questing",
|
||||
"pmcresponse-victim_plead_2": "I just wanted to do a quest, why'd you kill me :(",
|
||||
"pmcresponse-victim_plead_3": "Hope ur happy i cant even afford a new kit",
|
||||
"pmcresponse-victim_plead_4": "Bro i'm new to the game why you kill me",
|
||||
"pmcresponse-victim_plead_5": "I am never gonna get this stupid quest done",
|
||||
"pmcresponse-victim_plead_6": "Did you at least stash my gear?!"
|
||||
}
|
@ -29,6 +29,7 @@ import { DatabaseServer } from "../servers/DatabaseServer";
|
||||
import { SaveServer } from "../servers/SaveServer";
|
||||
import { InsuranceService } from "../services/InsuranceService";
|
||||
import { LocaleService } from "../services/LocaleService";
|
||||
import { PmcChatResponseService } from "../services/PmcChatResponseService";
|
||||
import { JsonUtil } from "../utils/JsonUtil";
|
||||
import { TimeUtil } from "../utils/TimeUtil";
|
||||
|
||||
@ -48,6 +49,7 @@ export class InraidController
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("LocaleService") protected localeService: LocaleService,
|
||||
@inject("PmcChatResponseService") protected pmcChatResponseService: PmcChatResponseService,
|
||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
@ -137,28 +139,22 @@ export class InraidController
|
||||
{
|
||||
if (locationName.toLowerCase() === "laboratory")
|
||||
{
|
||||
const localeDb = this.localeService.getLocaleDb();
|
||||
|
||||
const failedText = localeDb["5a8fd75188a45036844e0b0c"];
|
||||
const senderDetails: IUserDialogInfo = {
|
||||
_id: Traders.PRAPOR,
|
||||
info: {
|
||||
Nickname: "Prapor",
|
||||
Level: 1,
|
||||
Side: "Bear",
|
||||
MemberCategory: MemberCategory.TRADER
|
||||
}
|
||||
};
|
||||
|
||||
this.notificationSendHelper.sendMessageToPlayer(sessionID, senderDetails, failedText, MessageType.NPC_TRADER);
|
||||
this.sendLostInsuranceMessage(sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDead)
|
||||
{
|
||||
//TODO - find way to get killer name //this.pmcChatResponseService.sendKillerResponse(sessionID, pmcData);
|
||||
pmcData = this.performPostRaidActionsWhenDead(offraidData, pmcData, insuranceEnabled, preRaidGear, sessionID);
|
||||
}
|
||||
|
||||
const victims = offraidData.profile.Stats.Victims.filter(x => x.Role === "sptBear" || x.Role === "sptUsec");
|
||||
if (victims?.length > 0)
|
||||
{
|
||||
this.pmcChatResponseService.sendVictimResponse(sessionID, victims);
|
||||
}
|
||||
|
||||
if (insuranceEnabled)
|
||||
{
|
||||
this.insuranceService.sendInsuredItems(pmcData, sessionID, map.Id);
|
||||
@ -260,6 +256,24 @@ export class InraidController
|
||||
this.handlePostRaidPlayerScavProcess(scavData, sessionID, offraidData, pmcData, isDead);
|
||||
}
|
||||
|
||||
protected sendLostInsuranceMessage(sessionID: string): void
|
||||
{
|
||||
const localeDb = this.localeService.getLocaleDb();
|
||||
|
||||
const failedText = localeDb["5a8fd75188a45036844e0b0c"];
|
||||
const senderDetails: IUserDialogInfo = {
|
||||
_id: Traders.PRAPOR,
|
||||
info: {
|
||||
Nickname: "Prapor",
|
||||
Level: 1,
|
||||
Side: "Bear",
|
||||
MemberCategory: MemberCategory.TRADER
|
||||
}
|
||||
};
|
||||
|
||||
this.notificationSendHelper.sendMessageToPlayer(sessionID, senderDetails, failedText, MessageType.NPC_TRADER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the player dead after a raid - dead is anything other than "survived" / "runner"
|
||||
* @param statusOnExit exit value from offraidData object
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DependencyContainer, Lifecycle } from "tsyringe";
|
||||
|
||||
import { BotCallbacks } from "../callbacks/BotCallbacks";
|
||||
import { BundleCallbacks } from "../callbacks/BundleCallbacks";
|
||||
import { CustomizationCallbacks } from "../callbacks/CustomizationCallbacks";
|
||||
@ -212,6 +213,7 @@ import { NotificationService } from "../services/NotificationService";
|
||||
import { OpenZoneService } from "../services/OpenZoneService";
|
||||
import { PaymentService } from "../services/PaymentService";
|
||||
import { PlayerService } from "../services/PlayerService";
|
||||
import { PmcChatResponseService } from "../services/PmcChatResponseService";
|
||||
import { ProfileFixerService } from "../services/ProfileFixerService";
|
||||
import { ProfileSnapshotService } from "../services/ProfileSnapshotService";
|
||||
import { RagfairCategoriesService } from "../services/RagfairCategoriesService";
|
||||
@ -603,6 +605,7 @@ export class Container
|
||||
depContainer.register<BotWeaponModLimitService>("BotWeaponModLimitService", BotWeaponModLimitService, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<SeasonalEventService>("SeasonalEventService", SeasonalEventService, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<TraderPurchasePersisterService>("TraderPurchasePersisterService", TraderPurchasePersisterService);
|
||||
depContainer.register<PmcChatResponseService>("PmcChatResponseService", PmcChatResponseService);
|
||||
}
|
||||
|
||||
private static registerServers(depContainer: DependencyContainer): void
|
||||
|
@ -14,6 +14,7 @@ export enum ConfigTypes
|
||||
LOCATION = "aki-location",
|
||||
MATCH = "aki-match",
|
||||
PLAYERSCAV = "aki-playerscav",
|
||||
PMC_CHAT_RESPONSE = "aki-pmcchatresponse",
|
||||
QUEST = "aki-quest",
|
||||
RAGFAIR = "aki-ragfair",
|
||||
REPAIR = "aki-repair",
|
||||
|
17
project/src/models/spt/config/IPmChatResponse.ts
Normal file
17
project/src/models/spt/config/IPmChatResponse.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { IBaseConfig } from "./IBaseConfig";
|
||||
|
||||
export interface IPmcChatResponse extends IBaseConfig
|
||||
{
|
||||
kind: "aki-pmcchatresponse"
|
||||
victim: IResponseSettings
|
||||
killer: IResponseSettings
|
||||
}
|
||||
|
||||
export interface IResponseSettings
|
||||
{
|
||||
responseChancePercent: number
|
||||
responseTypeWeights: Record<string, number>
|
||||
stripCapitalisationChancePercent: number
|
||||
allCapsChancePercent: number;
|
||||
appendBroToMessageEndChancePercent: number;
|
||||
}
|
@ -42,4 +42,13 @@ export class LocalisationService
|
||||
{
|
||||
return this.i18n.__(key.toLowerCase(), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all locale keys
|
||||
* @returns string array of keys
|
||||
*/
|
||||
public getKeys(): string[]
|
||||
{
|
||||
return Object.keys(this.i18n.getCatalog("en"));
|
||||
}
|
||||
}
|
179
project/src/services/PmcChatResponseService.ts
Normal file
179
project/src/services/PmcChatResponseService.ts
Normal file
@ -0,0 +1,179 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
import { NotificationSendHelper } from "../helpers/NotificationSendHelper";
|
||||
import { WeightedRandomHelper } from "../helpers/WeightedRandomHelper";
|
||||
import { IPmcData } from "../models/eft/common/IPmcData";
|
||||
import { Victim } from "../models/eft/common/tables/IBotBase";
|
||||
import { IUserDialogInfo } from "../models/eft/profile/IAkiProfile";
|
||||
import { ConfigTypes } from "../models/enums/ConfigTypes";
|
||||
import { MemberCategory } from "../models/enums/MemberCategory";
|
||||
import { MessageType } from "../models/enums/MessageType";
|
||||
import { IPmcChatResponse } from "../models/spt/config/IPmChatResponse";
|
||||
import { ConfigServer } from "../servers/ConfigServer";
|
||||
import { RandomUtil } from "../utils/RandomUtil";
|
||||
import { LocalisationService } from "./LocalisationService";
|
||||
|
||||
@injectable()
|
||||
export class PmcChatResponseService
|
||||
{
|
||||
protected pmcResponsesConfig: IPmcChatResponse;
|
||||
|
||||
constructor(
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("NotificationSendHelper") protected notificationSendHelper: NotificationSendHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
)
|
||||
{
|
||||
this.pmcResponsesConfig = this.configServer.getConfig(ConfigTypes.PMC_CHAT_RESPONSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a random victim from those provided and sends a message to the player, can be positive or negative
|
||||
* @param sessionId Session id
|
||||
* @param pmcVictims Array of bots killed by player
|
||||
*/
|
||||
public sendVictimResponse(sessionId: string, pmcVictims: Victim[]): void
|
||||
{
|
||||
const victim = this.chooseRandomVictim(pmcVictims);
|
||||
|
||||
const message = this.chooseMessage(true);
|
||||
|
||||
this.notificationSendHelper.sendMessageToPlayer(sessionId, victim, message, MessageType.USER_MESSAGE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Not fully implemented yet, needs method of acquiring killers details after raid
|
||||
* @param sessionId Session id
|
||||
* @param pmcData Players profile
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public sendKillerResponse(sessionId: string, pmcData: IPmcData): void
|
||||
{
|
||||
const killer: IUserDialogInfo = {
|
||||
_id: "",
|
||||
info: undefined
|
||||
};
|
||||
|
||||
const message = this.chooseMessage(false);
|
||||
|
||||
this.notificationSendHelper.sendMessageToPlayer(sessionId, killer, message, MessageType.USER_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose a localised message to send the player (different if sender was killed or killed player)
|
||||
* @param isVictim
|
||||
* @returns
|
||||
*/
|
||||
protected chooseMessage(isVictim: boolean): string
|
||||
{
|
||||
// Positive/negative etc
|
||||
const responseType = this.chooseResponseType(isVictim);
|
||||
|
||||
// Get all locale keys
|
||||
const possibleResponseLocaleKeys = this.getResponseLocaleKeys(responseType, isVictim);
|
||||
|
||||
// Choose random response from above list and request it from localisation service
|
||||
let responseText = this.localisationService.getText(this.randomUtil.getArrayValue(possibleResponseLocaleKeys));
|
||||
if (this.appendBroToMessageEnd(isVictim))
|
||||
{
|
||||
responseText += " bro";
|
||||
}
|
||||
|
||||
if (this.stripCapitalistion(isVictim))
|
||||
{
|
||||
responseText = responseText.toLowerCase();
|
||||
}
|
||||
|
||||
if (this.allCaps(isVictim))
|
||||
{
|
||||
responseText = responseText.toUpperCase();
|
||||
}
|
||||
|
||||
return responseText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should capitalisation be stripped from the message response before sending
|
||||
* @param isVictim Was responder a victim of player
|
||||
* @returns true = should be stripped
|
||||
*/
|
||||
protected stripCapitalistion(isVictim: boolean): boolean
|
||||
{
|
||||
const chance = isVictim
|
||||
? this.pmcResponsesConfig.victim.stripCapitalisationChancePercent
|
||||
: this.pmcResponsesConfig.killer.stripCapitalisationChancePercent;
|
||||
|
||||
return this.randomUtil.getChance100(chance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should capitalisation be stripped from the message response before sending
|
||||
* @param isVictim Was responder a victim of player
|
||||
* @returns true = should be stripped
|
||||
*/
|
||||
protected allCaps(isVictim: boolean): boolean
|
||||
{
|
||||
const chance = isVictim
|
||||
? this.pmcResponsesConfig.victim.allCapsChancePercent
|
||||
: this.pmcResponsesConfig.killer.allCapsChancePercent;
|
||||
|
||||
return this.randomUtil.getChance100(chance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the word 'bro' be appended to the message being sent to player
|
||||
* @param isVictim Was responder a victim of player
|
||||
* @returns true = should be stripped
|
||||
*/
|
||||
appendBroToMessageEnd(isVictim: boolean): boolean
|
||||
{
|
||||
const chance = isVictim
|
||||
? this.pmcResponsesConfig.victim.appendBroToMessageEndChancePercent
|
||||
: this.pmcResponsesConfig.killer.appendBroToMessageEndChancePercent;
|
||||
|
||||
return this.randomUtil.getChance100(chance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose a type of response based on the weightings in pmc response config
|
||||
* @param isVictim Was responder killed by player
|
||||
* @returns Response type (positive/negative)
|
||||
*/
|
||||
protected chooseResponseType(isVictim = true): string
|
||||
{
|
||||
const responseWeights = isVictim
|
||||
? this.pmcResponsesConfig.victim.responseTypeWeights
|
||||
: this.pmcResponsesConfig.killer.responseTypeWeights;
|
||||
|
||||
return this.weightedRandomHelper.getWeightedInventoryItem(responseWeights);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale keys related to the type of response to send (victim/killer)
|
||||
* @param keyType Positive/negative
|
||||
* @param isVictim Was responder killed by player
|
||||
* @returns
|
||||
*/
|
||||
protected getResponseLocaleKeys(keyType: string, isVictim = true): string[]
|
||||
{
|
||||
const keyBase = isVictim ? "pmcresponse-victim_" : "pmcresponse-killer_";
|
||||
const keys = this.localisationService.getKeys();
|
||||
|
||||
return keys.filter(x => x.startsWith(`${keyBase}${keyType}`));
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly draw a victim of the the array and return thier details
|
||||
* @param pmcVictims Possible victims to choose from
|
||||
* @returns IUserDialogInfo
|
||||
*/
|
||||
protected chooseRandomVictim(pmcVictims: Victim[]): IUserDialogInfo
|
||||
{
|
||||
const randomVictim = this.randomUtil.getArrayValue(pmcVictims);
|
||||
|
||||
return {_id: randomVictim.Name, info:{Nickname: randomVictim.Name, Level: randomVictim.Level, Side: randomVictim.Side, MemberCategory: MemberCategory.UNIQUE_ID}};
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user