0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 09:50:43 -05:00

Improve flea offer item durability randomisation

Expanded `condition` config system to separate out current and max durability
Added missing armor config settings
Rewrote weapon durability method `randomiseWeaponDurabilityValues()` to do work similar to `randomiseArmorDurabilityValues()`
Fixed various items being classified as ARMORED_EQUIPMENT and using the wrong category config values
Moved ARMORED_EQUIPMENT to bottom of config so its chosen last, fixes vests being flagged as wrong item
This commit is contained in:
Dev 2024-02-08 14:52:09 +00:00
parent a9c48bc5c8
commit 810cd8abd6
3 changed files with 185 additions and 105 deletions

View File

@ -89,82 +89,172 @@
"5422acb9af1c889c16000029": { "5422acb9af1c889c16000029": {
"_name": "WEAPON", "_name": "WEAPON",
"conditionChance": 0.2, "conditionChance": 0.2,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.6, "min": 0.6,
"max": 1 "max": 1
}
}, },
"543be5664bdc2dd4348b4569": { "543be5664bdc2dd4348b4569": {
"_name": "MEDS", "_name": "MEDS",
"conditionChance": 0.2, "conditionChance": 0.2,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.6, "min": 0.6,
"max": 1 "max": 1
}
}, },
"5447e0e74bdc2d3c308b4567": { "5447e0e74bdc2d3c308b4567": {
"_name": "SPEC_ITEM", "_name": "SPEC_ITEM",
"conditionChance": 0.3, "conditionChance": 0.3,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.02, "min": 0.02,
"max": 1 "max": 1
}
}, },
"543be5e94bdc2df1348b4568": { "543be5e94bdc2df1348b4568": {
"_name": "KEY", "_name": "KEY",
"conditionChance": 0.04, "conditionChance": 0.04,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.96, "min": 0.96,
"max": 1 "max": 1
}
}, },
"5448e5284bdc2dcb718b4567": { "5448e5284bdc2dcb718b4567": {
"_name": "VEST", "_name": "VEST",
"conditionChance": 0.2, "conditionChance": 0.2,
"min": 0.05, "current": {
"min": 0,
"max": 1 "max": 1
}, },
"65649eb40bf0ed77b8044453": { "max": {
"_name": "BUILT_IN_INSERTS", "min": 0.45,
"conditionChance": 0.3, "max": 1
"min": 0.1, }
},
"5448e54d4bdc2dcc718b4568": {
"_name": "ARMOR",
"conditionChance": 0.2,
"current": {
"min": 0,
"max": 1 "max": 1
}, },
"57bef4c42459772e8d35a53b": { "max": {
"_name": "ARMORED_EQUIPMENT", "min": 0.45,
"conditionChance": 0.25,
"min": 0.05,
"max": 1 "max": 1
}
}, },
"644120aa86ffbe10ee032b6f": { "644120aa86ffbe10ee032b6f": {
"_name": "ARMOR_PLATE", "_name": "ARMOR_PLATE",
"conditionChance": 0.45, "conditionChance": 0.45,
"min": 0.2, "current": {
"min": 0,
"max": 1 "max": 1
}, },
"max": {
"min": 0.7,
"max": 1
}
},
"65649eb40bf0ed77b8044453": {
"_name": "BUILT_IN_INSERTS",
"conditionChance": 0.3,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.3,
"max": 1
}
},
"543be6674bdc2df1348b4569": { "543be6674bdc2df1348b4569": {
"_name": "FOOD_DRINK", "_name": "FOOD_DRINK",
"conditionChance": 0.05, "conditionChance": 0.05,
"current": {
"min": 1,
"max": 1
},
"max": {
"min": 0.05, "min": 0.05,
"max": 1 "max": 1
}
}, },
"5d650c3e815116009f6201d2": { "5d650c3e815116009f6201d2": {
"_name": "FUEL", "_name": "FUEL",
"conditionChance": 0.12, "conditionChance": 0.12,
"current": {
"min": 1,
"max": 1
},
"max": {
"min": 0.7, "min": 0.7,
"max": 0.99 "max": 100
}
}, },
"5a341c4686f77469e155819e": { "5a341c4686f77469e155819e": {
"_name": "FACECOVER", "_name": "FACECOVER",
"conditionChance": 0.32, "conditionChance": 0.32,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.6, "min": 0.6,
"max": 1 "max": 1
}
}, },
"5448e5724bdc2ddf718b4568": { "5448e5724bdc2ddf718b4568": {
"_name": "VISORS", "_name": "VISORS",
"conditionChance": 0.32, "conditionChance": 0.32,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.7, "min": 0.7,
"max": 1 "max": 1
}
}, },
"5a341c4086f77401f2541505": { "5a341c4086f77401f2541505": {
"_name": "HELMETS", "_name": "HELMETS",
"conditionChance": 0.20, "conditionChance": 0.32,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.45, "min": 0.45,
"max": 1 "max": 1
} }
}, },
"57bef4c42459772e8d35a53b": {
"_name": "ARMORED_EQUIPMENT",
"conditionChance": 0.25,
"current": {
"min": 0,
"max": 1
},
"max": {
"min": 0.05,
"max": 1
}
}
},
"stackablePercent": { "stackablePercent": {
"min": 10, "min": 10,
"max": 600 "max": 600

View File

@ -14,7 +14,7 @@ import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"; import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { Money } from "@spt-aki/models/enums/Money"; import { Money } from "@spt-aki/models/enums/Money";
import { Dynamic, IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig"; import { Condition, Dynamic, IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
@ -637,8 +637,12 @@ export class RagfairOfferGenerator
{ {
const rootItem = itemWithMods[0]; const rootItem = itemWithMods[0];
const itemConditionValues = this.ragfairConfig.dynamic.condition[conditionSettingsId]; const itemConditionValues: Condition = this.ragfairConfig.dynamic.condition[conditionSettingsId];
const multiplier = this.randomUtil.getFloat(itemConditionValues.min, itemConditionValues.max); const maxMultiplier = this.randomUtil.getFloat(itemConditionValues.max.min, itemConditionValues.max.max);
const currentMultiplier = this.randomUtil.getFloat(
itemConditionValues.current.min,
itemConditionValues.current.max,
);
// Randomise armor + plates + armor related things // Randomise armor + plates + armor related things
if ( if (
@ -646,17 +650,7 @@ export class RagfairOfferGenerator
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT]) || this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
) )
{ {
// Chance to not adjust armor this.randomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier);
if (
!this.randomUtil.getChance100(
this.ragfairConfig.dynamic.condition[BaseClasses.ARMORED_EQUIPMENT].conditionChance * 100,
)
)
{
return;
}
this.randomiseArmorDurabilityValues(itemWithMods);
// Add hits to visor // Add hits to visor
const visorMod = itemWithMods.find((item) => const visorMod = itemWithMods.find((item) =>
@ -678,7 +672,7 @@ export class RagfairOfferGenerator
// Randomise Weapons // Randomise Weapons
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.WEAPON)) if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.WEAPON))
{ {
this.randomiseWeaponDurabilityValues(itemWithMods[0], multiplier); this.randomiseWeaponDurability(itemWithMods[0], itemDetails, maxMultiplier, currentMultiplier);
return; return;
} }
@ -686,7 +680,7 @@ export class RagfairOfferGenerator
if (rootItem.upd.MedKit) if (rootItem.upd.MedKit)
{ {
// randomize health // randomize health
rootItem.upd.MedKit.HpResource = Math.round(rootItem.upd.MedKit.HpResource * multiplier) || 1; rootItem.upd.MedKit.HpResource = Math.round(rootItem.upd.MedKit.HpResource * maxMultiplier) || 1;
return; return;
} }
@ -694,7 +688,7 @@ export class RagfairOfferGenerator
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1) if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1)
{ {
// randomize key uses // randomize key uses
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - multiplier)) rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - maxMultiplier))
|| 0; || 0;
return; return;
@ -703,7 +697,7 @@ export class RagfairOfferGenerator
if (rootItem.upd.FoodDrink) if (rootItem.upd.FoodDrink)
{ {
// randomize food/drink value // randomize food/drink value
rootItem.upd.FoodDrink.HpPercent = Math.round(itemDetails._props.MaxResource * multiplier) || 1; rootItem.upd.FoodDrink.HpPercent = Math.round(itemDetails._props.MaxResource * maxMultiplier) || 1;
return; return;
} }
@ -711,7 +705,7 @@ export class RagfairOfferGenerator
if (rootItem.upd.RepairKit) if (rootItem.upd.RepairKit)
{ {
// randomize repair kit (armor/weapon) uses // randomize repair kit (armor/weapon) uses
rootItem.upd.RepairKit.Resource = Math.round(itemDetails._props.MaxRepairResource * multiplier) || 1; rootItem.upd.RepairKit.Resource = Math.round(itemDetails._props.MaxRepairResource * maxMultiplier) || 1;
return; return;
} }
@ -719,81 +713,75 @@ export class RagfairOfferGenerator
if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.FUEL)) if (this.itemHelper.isOfBaseclass(itemDetails._id, BaseClasses.FUEL))
{ {
const totalCapacity = itemDetails._props.MaxResource; const totalCapacity = itemDetails._props.MaxResource;
const remainingFuel = Math.round(totalCapacity * multiplier); const remainingFuel = Math.round(totalCapacity * maxMultiplier);
rootItem.upd.Resource = { UnitsConsumed: totalCapacity - remainingFuel, Value: remainingFuel }; rootItem.upd.Resource = { UnitsConsumed: totalCapacity - remainingFuel, Value: remainingFuel };
} }
} }
/** /**
* Adjust an items durability/maxDurability value * Adjust an items durability/maxDurability value
* @param item item (weapon/armor) to adjust * @param item item (weapon/armor) to Adjust
* @param multiplier Value to multiple durability by * @param itemDbDetails Weapon details from db
* @param maxMultiplier Value to multiply max durability by
* @param currentMultiplier Value to multiply current durability by
*/ */
protected randomiseWeaponDurabilityValues(item: Item, multiplier: number): void protected randomiseWeaponDurability(
item: Item,
itemDbDetails: ITemplateItem,
maxMultiplier: number,
currentMultiplier: number,
): void
{ {
item.upd.Repairable.Durability = Math.round(item.upd.Repairable.Durability * multiplier) || 1; const lowestMaxDurability = this.randomUtil.getFloat(maxMultiplier, 1) * itemDbDetails._props.MaxDurability;
const chosenMaxDurability = Math.round(
this.randomUtil.getFloat(lowestMaxDurability, itemDbDetails._props.MaxDurability),
);
// randomize max durability, store to a temporary value so we can still compare the max durability const lowestCurrentDurability = this.randomUtil.getFloat(currentMultiplier, 1) * chosenMaxDurability;
let tempMaxDurability = const chosenCurrentDurability = Math.round(
Math.round( this.randomUtil.getFloat(lowestCurrentDurability, chosenMaxDurability),
this.randomUtil.getFloat(item.upd.Repairable.Durability - 5, item.upd.Repairable.MaxDurability + 5), );
) || item.upd.Repairable.Durability;
// clamp values to max/current item.upd.Repairable.Durability = chosenCurrentDurability || 1; // Never let value become 0
if (tempMaxDurability >= item.upd.Repairable.MaxDurability) item.upd.Repairable.MaxDurability = chosenMaxDurability;
{
tempMaxDurability = item.upd.Repairable.MaxDurability;
}
if (tempMaxDurability < item.upd.Repairable.Durability)
{
tempMaxDurability = item.upd.Repairable.Durability;
}
// after clamping, assign to the item's properties
item.upd.Repairable.MaxDurability = tempMaxDurability;
} }
/** /**
* Randomise the durabiltiy values for an armors plates and soft inserts * Randomise the durabiltiy values for an armors plates and soft inserts
* @param armorWithMods Armor item with its child mods * @param armorWithMods Armor item with its child mods
* @param currentMultiplier Chosen multipler to use for current durability value
* @param maxMultiplier Chosen multipler to use for max durability value
*/ */
protected randomiseArmorDurabilityValues(armorWithMods: Item[]): void protected randomiseArmorDurabilityValues(
armorWithMods: Item[],
currentMultiplier: number,
maxMultiplier: number,
): void
{ {
const itemDurabilityConfigDict = this.ragfairConfig.dynamic.condition; for (const armorItem of armorWithMods)
const childMultiplerValues = {};
for (const item of armorWithMods)
{ {
const itemDbDetails = this.itemHelper.getItem(item._tpl)[1]; const itemDbDetails = this.itemHelper.getItem(armorItem._tpl)[1];
if ((parseInt(<string>itemDbDetails._props.armorClass)) > 1) if ((parseInt(<string>itemDbDetails._props.armorClass)) > 1)
{ {
if (!item.upd) if (!armorItem.upd)
{ {
item.upd = {}; armorItem.upd = {};
} }
// Store mod types durabiltiy multiplier for later use in current/max durability value calculation const lowestMaxDurability = this.randomUtil.getFloat(maxMultiplier, 1)
if (!childMultiplerValues[itemDbDetails._parent]) * itemDbDetails._props.MaxDurability;
{ const chosenMaxDurability = Math.round(
childMultiplerValues[itemDbDetails._parent] = this.randomUtil.getFloat(lowestMaxDurability, itemDbDetails._props.MaxDurability),
this.randomUtil.getFloat( );
itemDurabilityConfigDict[itemDbDetails._parent].min,
itemDurabilityConfigDict[itemDbDetails._parent].max,
) / itemDurabilityConfigDict[itemDbDetails._parent].max;
}
const modMultipler = childMultiplerValues[itemDbDetails._parent]; const lowestCurrentDurability = this.randomUtil.getFloat(currentMultiplier, 1) * chosenMaxDurability;
const maxDurability = Math.round( const chosenCurrentDurability = Math.round(
this.randomUtil.getFloat( this.randomUtil.getFloat(lowestCurrentDurability, chosenMaxDurability),
itemDbDetails._props.MaxDurability * this.randomUtil.getFloat(modMultipler, 1),
itemDbDetails._props.MaxDurability,
),
); );
const durability = Math.round(
this.randomUtil.getFloat(maxDurability * this.randomUtil.getFloat(modMultipler, 1), maxDurability), armorItem.upd.Repairable = {
); Durability: chosenCurrentDurability || 1, // Never let value become 0
item.upd.Repairable = { MaxDurability: chosenMaxDurability,
Durability: durability || 1, // Never let value become 0
MaxDurability: maxDurability,
}; };
} }
} }

View File

@ -141,10 +141,12 @@ export interface OfferAdjustment
priceThreshholdRub: number; priceThreshholdRub: number;
} }
export interface Condition extends MinMax export interface Condition
{ {
/** Percentage change durability is altered */ /** Percentage change durability is altered */
conditionChance: number; conditionChance: number;
current: MinMax;
max: MinMax;
} }
export interface Blacklist export interface Blacklist