Merge branch 'master' of https://dev.sp-tarkov.com/SPT-AKI/Server into 3.8.0
# Conflicts: # project/src/controllers/HealthController.ts
This commit is contained in:
commit
0d205ed50c
@ -58,7 +58,8 @@
|
||||
"5a0c27731526d80618476ac4",
|
||||
"57864c8c245977548867e7f1",
|
||||
"6087e570b998180e9f76dc24",
|
||||
"6391fcf5744e45201147080f"
|
||||
"6391fcf5744e45201147080f",
|
||||
"614451b71e5874611e2c7ae5"
|
||||
]
|
||||
},
|
||||
"pocketLoot": {
|
||||
@ -158,7 +159,8 @@
|
||||
"5a0c27731526d80618476ac4",
|
||||
"6087e570b998180e9f76dc24",
|
||||
"63495c500c297e20065a08b1",
|
||||
"6391fcf5744e45201147080f"
|
||||
"6391fcf5744e45201147080f",
|
||||
"614451b71e5874611e2c7ae5"
|
||||
]
|
||||
},
|
||||
"dynamicLoot": {
|
||||
|
@ -144,12 +144,12 @@
|
||||
"minPlayerLevel": 5,
|
||||
"rewardScaling": {
|
||||
"levels": [1, 10, 20, 30, 40, 50, 60],
|
||||
"experience": [1000, 2000, 8000, 14000, 20000, 24000, 28000],
|
||||
"roubles": [11000, 20000, 45000, 60000, 77000, 95000, 115000],
|
||||
"experience": [1000, 2000, 8000, 13000, 19000, 24000, 30000],
|
||||
"roubles": [11000, 20000, 32000, 45000, 58000, 70000, 82000],
|
||||
"items": [2, 4, 5, 5, 5, 5, 5],
|
||||
"reputation": [0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.03],
|
||||
"rewardSpread": 0.5,
|
||||
"skillRewardChance": [0, 0.01, 0.05, 0.1, 0.15, 0.2, 0.25],
|
||||
"skillRewardChance": [0, 0.01, 0.05, 0.1, 0.15, 0.2, 0.2],
|
||||
"skillPointReward": [10, 15, 20, 25, 30, 35, 40]
|
||||
},
|
||||
"locations": {
|
||||
@ -262,7 +262,8 @@
|
||||
],
|
||||
"questConfig": {
|
||||
"Exploration": {
|
||||
"maxExtracts": 3,
|
||||
"maxExtracts": 5,
|
||||
"maxExtractsWithSpecificExit": 3,
|
||||
"possibleSkillRewards": ["Endurance", "Strength", "Vitality"],
|
||||
"specificExits": {
|
||||
"probability": 0.25,
|
||||
@ -279,7 +280,7 @@
|
||||
"Completion": {
|
||||
"possibleSkillRewards": ["Endurance", "Strength", "Vitality"],
|
||||
"minRequestedAmount": 1,
|
||||
"maxRequestedAmount": 5,
|
||||
"maxRequestedAmount": 4,
|
||||
"minRequestedBulletAmount": 20,
|
||||
"maxRequestedBulletAmount": 60,
|
||||
"useWhitelist": true,
|
||||
@ -782,12 +783,12 @@
|
||||
"minPlayerLevel": 15,
|
||||
"rewardScaling": {
|
||||
"levels": [1, 10, 20, 30, 40, 50, 60],
|
||||
"experience": [5000, 25000, 40000, 110000, 220000, 370000, 720000],
|
||||
"roubles": [50000, 150000, 300000, 425000, 550000, 675000, 850000],
|
||||
"experience": [5000, 15000, 27000, 80000, 142000, 220000, 290000],
|
||||
"roubles": [20000, 50000, 175000, 350000, 540000, 710000, 880000],
|
||||
"items": [4, 5, 5, 6, 6, 7, 7],
|
||||
"reputation": [0.02, 0.03, 0.04, 0.04, 0.05, 0.05, 0.05],
|
||||
"rewardSpread": 0.5,
|
||||
"skillRewardChance": [0, 0.05, 0.1, 0.2, 0.3, 0.35, 0.4],
|
||||
"skillRewardChance": [0, 0.05, 0.1, 0.15, 0.25, 0.3, 0.3],
|
||||
"skillPointReward": [25, 35, 45, 50, 55, 60, 65]
|
||||
},
|
||||
"locations": {
|
||||
@ -901,7 +902,8 @@
|
||||
"questConfig": {
|
||||
"Exploration": {
|
||||
"possibleSkillRewards": ["Endurance", "Strength", "Vitality"],
|
||||
"maxExtracts": 10,
|
||||
"maxExtracts": 25,
|
||||
"maxExtractsWithSpecificExit": 12,
|
||||
"specificExits": {
|
||||
"probability": 0.4,
|
||||
"passageRequirementWhitelist": [
|
||||
@ -1486,11 +1488,11 @@
|
||||
"numQuests": 1,
|
||||
"minPlayerLevel": 1,
|
||||
"rewardScaling": {
|
||||
"levels": [1, 20, 45, 100],
|
||||
"experience": [1000, 4000, 20000, 80000],
|
||||
"roubles": [6000, 10000, 100000, 250000],
|
||||
"items": [3, 3, 4, 4],
|
||||
"reputation": [0.02, 0.02, 0.05, 0.05],
|
||||
"levels": [1, 10, 20, 30, 40, 50, 60],
|
||||
"experience": [0, 0, 0, 0, 0, 0, 0],
|
||||
"roubles": [11000, 20000, 32000, 45000, 58000, 70000, 82000],
|
||||
"items": [2, 3, 3, 3, 3, 4, 4],
|
||||
"reputation": [0.02, 0.02, 0.03, 0.03, 0.04, 0.04, 0.05],
|
||||
"rewardSpread": 0.5,
|
||||
"skillRewardChance": [0, 0, 0, 0, 0, 0, 0],
|
||||
"skillPointReward": [10, 15, 20, 25, 30, 35, 40]
|
||||
@ -1522,7 +1524,8 @@
|
||||
"questConfig": {
|
||||
"Exploration": {
|
||||
"possibleSkillRewards": ["Endurance", "Strength", "Vitality"],
|
||||
"maxExtracts": 3,
|
||||
"maxExtracts": 4,
|
||||
"maxExtractsWithSpecificExit": 2,
|
||||
"specificExits": {
|
||||
"probability": 0.25,
|
||||
"passageRequirementWhitelist": [
|
||||
|
@ -91,8 +91,8 @@ export class HealthController
|
||||
else
|
||||
{
|
||||
// Get max healing from db
|
||||
const maxHp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
|
||||
healingItemToUse.upd.MedKit = { HpResource: maxHp - request.count }; // Subtract amount used from max
|
||||
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
|
||||
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max
|
||||
}
|
||||
|
||||
// Resource in medkit is spent, delete it
|
||||
@ -169,10 +169,13 @@ export class HealthController
|
||||
const payMoneyRequest: IProcessBuyTradeRequestData = {
|
||||
Action: healthTreatmentRequest.Action,
|
||||
tid: Traders.THERAPIST,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
scheme_items: healthTreatmentRequest.items,
|
||||
type: "",
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
item_id: "",
|
||||
count: 0,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
scheme_id: 0,
|
||||
};
|
||||
|
||||
@ -188,8 +191,12 @@ export class HealthController
|
||||
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
|
||||
const profilePart = pmcData.Health.BodyParts[bodyPartKey];
|
||||
|
||||
// Set profile body part to max
|
||||
profilePart.Health.Current = profilePart.Health.Maximum;
|
||||
// Bodypart healing is chosen when part request hp is above 0
|
||||
if (partRequest.Health > 0)
|
||||
{
|
||||
// Heal bodypart
|
||||
profilePart.Health.Current = profilePart.Health.Maximum;
|
||||
}
|
||||
|
||||
// Check for effects to remove
|
||||
if (partRequest.Effects?.length > 0)
|
||||
@ -220,6 +227,7 @@ export class HealthController
|
||||
* @param info Request data
|
||||
* @param sessionID
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void
|
||||
{
|
||||
// https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674
|
||||
|
@ -654,7 +654,7 @@ export class BotEquipmentModGenerator
|
||||
modTpl,
|
||||
modSlot,
|
||||
);
|
||||
if (!modCompatibilityResult.incompatible)
|
||||
if (!modCompatibilityResult.incompatible && !this.weaponModComboIsIncompatible(weapon, modTpl))
|
||||
{
|
||||
found = true;
|
||||
|
||||
@ -666,6 +666,7 @@ export class BotEquipmentModGenerator
|
||||
if (modCompatibilityResult.incompatible && parentSlot._required)
|
||||
{
|
||||
this.logger.debug(modCompatibilityResult.reason);
|
||||
this.logger.debug(`Weapon: ${weapon.map(x => `${x._tpl} ${x.slotId ?? ""}`).join(",")}`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -699,6 +700,23 @@ export class BotEquipmentModGenerator
|
||||
return this.itemHelper.getItem(modTpl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temp fix to prevent certain combinations of weapons with mods that are known to be incompatible
|
||||
* @param weapon Weapon
|
||||
* @param modTpl Mod to check compatibility with weapon
|
||||
* @returns True if incompatible
|
||||
*/
|
||||
protected weaponModComboIsIncompatible(weapon: Item[], modTpl: string): boolean
|
||||
{
|
||||
// STM-9 + AR-15 Lone Star Ion Lite handguard
|
||||
if (weapon[0]._tpl === "60339954d62c9b14ed777c06" && modTpl === "5d4405f0a4b9361e6a4e6bd9")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mod item with parameters as properties
|
||||
* @param modId _id
|
||||
|
@ -620,25 +620,26 @@ export class RepeatableQuestGenerator
|
||||
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
|
||||
* This is a helper method for GenerateCompletionQuest to create a completion condition (of which a completion quest theoretically can have many)
|
||||
*
|
||||
* @param {string} targetItemId id of the item to request
|
||||
* @param {string} itemTpl id of the item to request
|
||||
* @param {integer} value amount of items of this specific type to request
|
||||
* @returns {object} object of "Completion"-condition
|
||||
*/
|
||||
protected generateCompletionAvailableForFinish(targetItemId: string, value: number): ICompletionAvailableFor
|
||||
protected generateCompletionAvailableForFinish(itemTpl: string, value: number): ICompletionAvailableFor
|
||||
{
|
||||
let minDurability = 0;
|
||||
let onlyFoundInRaid = true;
|
||||
if (
|
||||
this.itemHelper.isOfBaseclass(targetItemId, BaseClasses.WEAPON)
|
||||
|| this.itemHelper.isOfBaseclass(targetItemId, BaseClasses.ARMOR)
|
||||
this.itemHelper.isOfBaseclass(itemTpl, BaseClasses.WEAPON)
|
||||
|| this.itemHelper.isOfBaseclass(itemTpl, BaseClasses.ARMOR)
|
||||
)
|
||||
{
|
||||
minDurability = 80;
|
||||
minDurability = this.randomUtil.getArrayValue([60, 80]);
|
||||
}
|
||||
|
||||
// By default all collected items must be FiR, except dog tags
|
||||
if (
|
||||
this.itemHelper.isOfBaseclass(targetItemId, BaseClasses.DOG_TAG_USEC)
|
||||
|| this.itemHelper.isOfBaseclass(targetItemId, BaseClasses.DOG_TAG_BEAR)
|
||||
this.itemHelper.isOfBaseclass(itemTpl, BaseClasses.DOG_TAG_USEC)
|
||||
|| this.itemHelper.isOfBaseclass(itemTpl, BaseClasses.DOG_TAG_BEAR)
|
||||
)
|
||||
{
|
||||
onlyFoundInRaid = false;
|
||||
@ -651,7 +652,7 @@ export class RepeatableQuestGenerator
|
||||
dynamicLocale: true,
|
||||
index: 0,
|
||||
visibilityConditions: [],
|
||||
target: [targetItemId],
|
||||
target: [itemTpl],
|
||||
value: value,
|
||||
minDurability: minDurability,
|
||||
maxDurability: 100,
|
||||
@ -680,6 +681,7 @@ export class RepeatableQuestGenerator
|
||||
): IExploration
|
||||
{
|
||||
const explorationConfig = repeatableConfig.questConfig.Exploration;
|
||||
const requiresSpecificExtract = Math.random() < repeatableConfig.questConfig.Exploration.specificExits.probability;
|
||||
|
||||
if (Object.keys(questTypePool.pool.Exploration.locations).length === 0)
|
||||
{
|
||||
@ -696,7 +698,8 @@ export class RepeatableQuestGenerator
|
||||
// remove the location from the available pool
|
||||
delete questTypePool.pool.Exploration.locations[locationKey];
|
||||
|
||||
const numExtracts = this.randomUtil.randInt(1, explorationConfig.maxExtracts + 1);
|
||||
// Different max extract count when specific extract needed
|
||||
const numExtracts = this.randomUtil.randInt(1, requiresSpecificExtract ? explorationConfig.maxExtractsWithSpecificExit : explorationConfig.maxExtracts + 1);
|
||||
|
||||
const quest = this.generateRepeatableTemplate("Exploration", traderId, repeatableConfig.side) as IExploration;
|
||||
|
||||
@ -715,7 +718,8 @@ export class RepeatableQuestGenerator
|
||||
quest.conditions.AvailableForFinish[0]._props.id = this.objectId.generate();
|
||||
quest.location = this.getQuestLocationByMapId(locationKey);
|
||||
|
||||
if (Math.random() < repeatableConfig.questConfig.Exploration.specificExits.probability)
|
||||
|
||||
if (requiresSpecificExtract)
|
||||
{
|
||||
// Filter by whitelist, it's also possible that the field "PassageRequirement" does not exist (e.g. Shoreline)
|
||||
// Scav exits are not listed at all in locations.base currently. If that changes at some point, additional filtering will be required
|
||||
@ -875,27 +879,34 @@ export class RepeatableQuestGenerator
|
||||
let roublesBudget = rewardRoubles;
|
||||
let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
|
||||
|
||||
// Add xp reward
|
||||
const rewards: IRewards = {
|
||||
Started: [],
|
||||
Success: [{ value: rewardXP, type: "Experience", index: 0 }],
|
||||
Success: [],
|
||||
Fail: [],
|
||||
};
|
||||
|
||||
let rewardIndex = 0;
|
||||
// Add xp reward
|
||||
if (rewardXP > 0)
|
||||
{
|
||||
rewards.Success.push({ value: rewardXP, type: "Experience", index: rewardIndex });
|
||||
rewardIndex++;
|
||||
}
|
||||
|
||||
// Add money reward
|
||||
if (traderId === Traders.PEACEKEEPER)
|
||||
if (traderId === Traders.PEACEKEEPER || traderId === Traders.FENCE)
|
||||
{
|
||||
// convert to equivalent dollars
|
||||
rewards.Success.push(
|
||||
this.generateRewardItem(Money.EUROS, this.handbookHelper.fromRUB(rewardRoubles, Money.EUROS), 1),
|
||||
this.generateRewardItem(Money.EUROS, this.handbookHelper.fromRUB(rewardRoubles, Money.EUROS), rewardIndex),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
rewards.Success.push(this.generateRewardItem(Money.ROUBLES, rewardRoubles, 1));
|
||||
rewards.Success.push(this.generateRewardItem(Money.ROUBLES, rewardRoubles, rewardIndex));
|
||||
}
|
||||
rewardIndex++;
|
||||
|
||||
let index = 2;
|
||||
if (rewardItemPool.length > 0)
|
||||
{
|
||||
let weaponRewardCount = 0;
|
||||
@ -946,14 +957,15 @@ export class RepeatableQuestGenerator
|
||||
itemCount = 2;
|
||||
}
|
||||
|
||||
rewards.Success.push(this.generateRewardItem(itemSelected._id, itemCount, index, children));
|
||||
rewards.Success.push(this.generateRewardItem(itemSelected._id, itemCount, rewardIndex, children));
|
||||
rewardIndex++;
|
||||
|
||||
const itemCost = (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.WEAPON))
|
||||
? this.itemHelper.getItemMaxPrice(children[0]._tpl) // use if preset is not default : this.itemHelper.getWeaponPresetPrice(children[0], children, this.itemHelper.getStaticItemPrice(itemSelected._id))
|
||||
: this.itemHelper.getStaticItemPrice(itemSelected._id);
|
||||
roublesBudget -= itemCount * itemCost;
|
||||
index += 1;
|
||||
|
||||
// if we still have budget narrow down the items
|
||||
// If we still have budget narrow down possible items
|
||||
if (roublesBudget > 0)
|
||||
{
|
||||
// Filter possible reward items to only items with a price below the remaining budget
|
||||
@ -975,18 +987,19 @@ export class RepeatableQuestGenerator
|
||||
// Add rep reward to rewards array
|
||||
if (rewardReputation > 0)
|
||||
{
|
||||
const reward: IReward = { target: traderId, value: rewardReputation, type: "TraderStanding", index: index };
|
||||
const reward: IReward = { target: traderId, value: rewardReputation, type: "TraderStanding", index: rewardIndex };
|
||||
rewards.Success.push(reward);
|
||||
rewardIndex++;
|
||||
}
|
||||
|
||||
// Chance of adding skill reward
|
||||
if (this.randomUtil.getChance100(skillRewardChance * 100))
|
||||
{
|
||||
index++;
|
||||
const reward: IReward = {
|
||||
target: this.randomUtil.getArrayValue(questConfig.possibleSkillRewards),
|
||||
value: skillPointReward,
|
||||
type: "Skill",
|
||||
index: index,
|
||||
index: rewardIndex,
|
||||
};
|
||||
rewards.Success.push(reward);
|
||||
}
|
||||
|
@ -89,8 +89,9 @@ export interface IRepeatableQuestTypesConfig
|
||||
|
||||
export interface IExploration extends IBaseQuestConfig
|
||||
{
|
||||
maxExtracts: number;
|
||||
specificExits: ISpecificExits;
|
||||
maxExtracts: number
|
||||
maxExtractsWithSpecificExit: number
|
||||
specificExits: ISpecificExits
|
||||
}
|
||||
|
||||
export interface ISpecificExits
|
||||
|
@ -184,7 +184,8 @@ export class EventOutputHolder
|
||||
}
|
||||
}
|
||||
|
||||
return productions;
|
||||
// Return null of there's no crafts to send to client to match live behaviour
|
||||
return (Object.keys(productions).length > 0) ? productions : null
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user