mooooaaaaaaaaaaar

This commit is contained in:
Alex
2025-01-24 17:06:43 +00:00
parent 19619ec061
commit aaa272b151
12 changed files with 1132 additions and 292 deletions
@@ -1,59 +1,159 @@
using SptCommon.Annotations;
using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Services;
using Core.Utils;
namespace Core.Generators;
[Injectable]
public class RagfairAssortGenerator()
public class RagfairAssortGenerator(
HashUtil hashUtil,
ItemHelper itemHelper,
PresetHelper presetHelper,
SeasonalEventService seasonalEventService,
ConfigServer configServer
)
{
protected List<List<Item>> generatedAssortItems = [];
protected RagfairConfig ragfairConfig = configServer.GetConfig<RagfairConfig>();
/// <summary>
/// Get an array of arrays that can be sold on the flea
/// Each sub array contains item + children (if any)
/// </summary>
/// <returns>List of Lists</returns>
protected List<string> ragfairItemInvalidBaseTypes =
[
BaseClasses.LOOT_CONTAINER, // Safe, barrel cache etc
BaseClasses.STASH, // Player inventory stash
BaseClasses.SORTING_TABLE,
BaseClasses.INVENTORY,
BaseClasses.STATIONARY_CONTAINER,
BaseClasses.POCKETS,
BaseClasses.BUILT_IN_INSERTS,
];
/**
* Get an array of arrays that can be sold on the flea
* Each sub array contains item + children (if any)
* @returns array of arrays
*/
public List<List<Item>> GetAssortItems()
{
throw new NotImplementedException();
if (!AssortsAreGenerated())
{
generatedAssortItems = GenerateRagfairAssortItems();
}
return generatedAssortItems;
}
/// <summary>
/// Check internal generatedAssortItems array has objects
/// </summary>
/// <returns>true if array has objects</returns>
/**
* Check internal generatedAssortItems array has objects
* @returns true if array has objects
*/
protected bool AssortsAreGenerated()
{
throw new NotImplementedException();
return generatedAssortItems.Count > 0;
}
/// <summary>
/// Generate an array of arrays (item + children) the flea can sell
/// </summary>
/// <returns>List of Lists (item + children)</returns>
/**
* Generate an array of arrays (item + children) the flea can sell
* @returns array of arrays (item + children)
*/
protected List<List<Item>> GenerateRagfairAssortItems()
{
throw new NotImplementedException();
List<List<Item>> results = [];
/** Get cloned items from db */
var dbItemsClone = itemHelper.GetItems().Where((item) => item.Type != "Node");
/** Store processed preset tpls so we dont add them when procesing non-preset items */
List<string> processedArmorItems = [];
var seasonalEventActive = seasonalEventService.SeasonalEventEnabled();
var seasonalItemTplBlacklist = seasonalEventService.GetInactiveSeasonalEventItems();
var presets = GetPresetsToAdd();
foreach (var preset in presets)
{
// Update Ids and clone
var presetAndMods = itemHelper.ReplaceIDs(preset.Items);
itemHelper.RemapRootItemId(presetAndMods);
// Add presets base item tpl to the processed list so its skipped later on when processing items
processedArmorItems.Add(preset.Items[0].Template);
presetAndMods[0].ParentId = "hideout";
presetAndMods[0].SlotId = "hideout";
presetAndMods[0].Upd = new Upd()
{ StackObjectsCount = 99999999, UnlimitedCount = true, SptPresetId = preset.Id };
results.Add(presetAndMods);
}
foreach (var item in dbItemsClone)
{
if (!itemHelper.IsValidItem(item.Id, ragfairItemInvalidBaseTypes))
{
continue;
}
// Skip seasonal items when not in-season
if (
ragfairConfig.Dynamic.RemoveSeasonalItemsWhenNotInEvent &&
!seasonalEventActive &&
seasonalItemTplBlacklist.Contains(item.Id)
)
{
continue;
}
if (processedArmorItems.Contains(item.Id))
{
// Already processed
continue;
}
var ragfairAssort = CreateRagfairAssortRootItem(
item.Id,
item.Id
); // tplid and id must be the same so hideout recipe rewards work
results.Add([ragfairAssort]);
}
return results;
}
/// <summary>
/// Get presets from globals to add to flea
/// ragfairConfig.dynamic.showDefaultPresetsOnly decides if its all presets or just defaults
/// </summary>
/// <returns>Dictionary</returns>
/**
* Get presets from globals to add to flea
* ragfairConfig.dynamic.showDefaultPresetsOnly decides if its all presets or just defaults
* @returns IPreset array
*/
protected List<Preset> GetPresetsToAdd()
{
throw new NotImplementedException();
return ragfairConfig.Dynamic.ShowDefaultPresetsOnly
? presetHelper.GetDefaultPresets().Values.ToList()
: presetHelper.GetAllPresets();
}
/// <summary>
/// Create a base assort item and return it with populated values + 999999 stack count + unlimited count = true
/// </summary>
/// <param name="tplId">tplid to add to item</param>
/// <param name="id">id to add to item</param>
/// <returns>Hydrated Item object</returns>
protected Item CreateRagfairAssortRootItem(string tplId, string id_checkTodoComment) // TODO: string id = this.hashUtil.generate()
/**
* Create a base assort item and return it with populated values + 999999 stack count + unlimited count = true
* @param tplId tplid to add to item
* @param id id to add to item
* @returns Hydrated Item object
*/
protected Item CreateRagfairAssortRootItem(string tplId, string? id = null)
{
throw new NotImplementedException();
if (string.IsNullOrEmpty(id))
id = hashUtil.Generate();
return new Item()
{
Id = id,
Template = tplId,
ParentId = "hideout",
SlotId = "hideout",
Upd = new Upd() { StackObjectsCount = 99999999, UnlimitedCount = true }
};
}
}
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -154,11 +154,11 @@ public class HandbookHelper(
/// <param name="nonRoubleCurrencyCount">Currency count to convert</param>
/// <param name="currencyTypeFrom">What current currency is</param>
/// <returns>Count in roubles</returns>
public double InRUB(double nonRoubleCurrencyCount, string currencyTypeFrom)
public int InRUB(double nonRoubleCurrencyCount, string currencyTypeFrom)
{
return currencyTypeFrom == Money.ROUBLES
return (int) (currencyTypeFrom == Money.ROUBLES
? nonRoubleCurrencyCount
: Math.Round(nonRoubleCurrencyCount * (GetTemplatePrice(currencyTypeFrom) ?? 0));
: Math.Round(nonRoubleCurrencyCount * (GetTemplatePrice(currencyTypeFrom) ?? 0)));
}
/// <summary>
@@ -167,15 +167,15 @@ public class HandbookHelper(
/// <param name="roubleCurrencyCount">roubles to convert</param>
/// <param name="currencyTypeTo">Currency to convert roubles into</param>
/// <returns>currency count in desired type</returns>
public double FromRUB(double roubleCurrencyCount, string currencyTypeTo)
public int FromRUB(double roubleCurrencyCount, string currencyTypeTo)
{
if (currencyTypeTo == Money.ROUBLES) {
return roubleCurrencyCount;
return (int) roubleCurrencyCount;
}
// Get price of currency from handbook
var price = GetTemplatePrice(currencyTypeTo);
return price is not null ? Math.Max(1, Math.Round((double)(roubleCurrencyCount / price))) : 0;
return (int) (price is not null ? Math.Max(1, Math.Round((double)(roubleCurrencyCount / price))) : 0);
}
public HandbookCategory GetCategoryById(string handbookId)
+190 -67
View File
@@ -1,92 +1,215 @@
using SptCommon.Annotations;
using System.Runtime.InteropServices.JavaScript;
using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Servers;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
namespace Core.Helpers;
[Injectable]
public class RagfairServerHelper
public class RagfairServerHelper(
ISptLogger<RagfairServerHelper> logger,
RandomUtil randomUtil,
TimeUtil timeUtil,
SaveServer saveServer,
DatabaseService databaseService,
ProfileHelper profileHelper,
ItemHelper itemHelper,
TraderHelper traderHelper,
MailSendService mailSendService,
LocalisationService localisationService,
ItemFilterService itemFilterService,
ConfigServer configServer,
ICloner cloner
)
{
/// <summary>
/// Is item valid / on blacklist / quest item
/// </summary>
/// <param name="itemDetails"></param>
/// <returns>boolean</returns>
public bool IsItemValidRagfairItem(bool[] itemDetails)
protected RagfairConfig ragfairConfig = configServer.GetConfig<RagfairConfig>();
protected QuestConfig questConfig = configServer.GetConfig<QuestConfig>();
protected static string goodsReturnedTemplate = "5bdabfe486f7743e1665df6e 0"; // Your item was not sold
/**
* Is item valid / on blacklist / quest item
* @param itemDetails
* @returns boolean
*/
public bool IsItemValidRagfairItem(KeyValuePair<bool, TemplateItem?> itemDetails)
{
throw new NotImplementedException();
var blacklistConfig = ragfairConfig.Dynamic.Blacklist;
// Skip invalid items
if (!itemDetails.Key) {
return false;
}
if (!itemHelper.IsValidItem(itemDetails.Value.Id)) {
return false;
}
// Skip bsg blacklisted items
if (blacklistConfig.EnableBsgList && !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false)) {
return false;
}
// Skip custom blacklisted items and flag as unsellable by players
if (IsItemOnCustomFleaBlacklist(itemDetails.Value.Id)) {
itemDetails.Value.Properties.CanSellOnRagfair = false;
return false;
}
// Skip custom category blacklisted items
if (
blacklistConfig.EnableCustomItemCategoryList &&
IsItemCategoryOnCustomFleaBlacklist(itemDetails.Value.Parent)
) {
return false;
}
// Skip quest items
if (blacklistConfig.EnableQuestList && itemHelper.IsQuestItem(itemDetails.Value.Id)) {
return false;
}
// Don't include damaged ammo packs
if (
ragfairConfig.Dynamic.Blacklist.DamagedAmmoPacks &&
itemDetails.Value.Parent == BaseClasses.AMMO_BOX &&
itemDetails[1]._name.includes("_damaged")
) {
return false;
}
return true;
}
/// <summary>
/// Is supplied item tpl on the ragfair custom blacklist from configs/ragfair.json/dynamic
/// </summary>
/// <param name="itemTemplateId">Item tpl to check is blacklisted</param>
/// <returns>True if its blacklsited</returns>
protected bool IsItemOnCustomFleaBlacklist(string itemTemplateId)
{
throw new NotImplementedException();
/**
* Is supplied item tpl on the ragfair custom blacklist from configs/ragfair.json/dynamic
* @param itemTemplateId Item tpl to check is blacklisted
* @returns True if its blacklsited
*/
protected isItemOnCustomFleaBlacklist(itemTemplateId: string): boolean {
return ragfairConfig.dynamic.blacklist.custom.includes(itemTemplateId);
}
/// <summary>
/// Is supplied parent id on the ragfair custom item category blacklist
/// </summary>
/// <param name="parentId">Parent Id to check is blacklisted</param>
/// <returns>true if blacklisted</returns>
protected bool IsItemCategoryOnCustomFleaBlacklist(string itemParentId)
{
throw new NotImplementedException();
/**
* Is supplied parent id on the ragfair custom item category blacklist
* @param parentId Parent Id to check is blacklisted
* @returns true if blacklisted
*/
protected isItemCategoryOnCustomFleaBlacklist(itemParentId: string): boolean {
return ragfairConfig.dynamic.blacklist.customItemCategoryList.includes(itemParentId);
}
/// <summary>
/// is supplied id a trader
/// </summary>
/// <param name="traderId"></param>
/// <returns>True if id was a trader</returns>
public bool IsTrader(string traderId)
{
throw new NotImplementedException();
/**
* is supplied id a trader
* @param traderId
* @returns True if id was a trader
*/
public isTrader(traderId: string): boolean {
return traderId in databaseService.getTraders();
}
/// <summary>
/// Send items back to player
/// </summary>
/// <param name="sessionID">Player to send items to</param>
/// <param name="returnedItems">Items to send to player</param>
public void ReturnItems(string sessionID, List<Item> returnedItems)
{
throw new NotImplementedException();
/**
* Send items back to player
* @param sessionID Player to send items to
* @param returnedItems Items to send to player
*/
public returnItems(sessionID: string, returnedItems: IItem[]): void {
mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID,
traderHelper.getTraderById(Traders.RAGMAN),
MessageType.MESSAGE_WITH_ITEMS,
RagfairServerHelper.goodsReturnedTemplate,
returnedItems,
timeUtil.getHoursAsSeconds(
databaseService.getGlobals().config.RagFair.yourOfferDidNotSellMaxStorageTimeInHour,
),
);
}
public int CalculateDynamicStackCount(string tplId, bool isWeaponPreset)
{
throw new NotImplementedException();
public calculateDynamicStackCount(tplId: string, isWeaponPreset: boolean): number {
var config = ragfairConfig.dynamic;
// Lookup item details - check if item not found
var itemDetails = itemHelper.getItem(tplId);
if (!itemDetails[0]) {
throw new JSType.Error(
localisationService.getText(
"ragfair-item_not_in_db_unable_to_generate_dynamic_stack_count",
tplId,
),
);
}
// Item Types to return one of
if (
isWeaponPreset ||
itemHelper.isOfBaseclasses(itemDetails[1]._id, ragfairConfig.dynamic.showAsSingleStack)
) {
return 1;
}
// Get max stack count
var maxStackCount = itemDetails[1]._props.StackMaxSize;
// non-stackable - use different values to calculate stack size
if (!maxStackCount || maxStackCount === 1) {
return Math.round(randomUtil.getInt(config.nonStackableCount.min, config.nonStackableCount.max));
}
var stackPercent = Math.round(
randomUtil.getInt(config.stackablePercent.min, config.stackablePercent.max),
);
return Math.round((maxStackCount / 100) * stackPercent);
}
/// <summary>
/// Choose a currency at random with bias
/// </summary>
/// <returns>currency tpl</returns>
public string GetDynamicOfferCurrency()
{
throw new NotImplementedException();
/**
* Choose a currency at random with bias
* @returns currency tpl
*/
public getDynamicOfferCurrency(): string {
var currencies = ragfairConfig.dynamic.currencies;
var bias: string[] = [];
for (var item in currencies) {
for (let i = 0; i < currencies[item]; i++) {
bias.push(item);
}
}
return bias[Math.floor(Math.random() * bias.length)];
}
/// <summary>
/// Given a preset id from globals.json, return a list of items with unique ids
/// </summary>
/// <param name="item">Preset item</param>
/// <returns>List of weapon and its children</returns>
public List<Item> GetPresetItems(Item item)
{
throw new NotImplementedException();
/**
* Given a preset id from globals.json, return an array of items[] with unique ids
* @param item Preset item
* @returns Array of weapon and its children
*/
public getPresetItems(item: IItem): IItem[] {
var preset = cloner.clone(databaseService.getGlobals().ItemPresets[item._id]._items);
return itemHelper.reparentItemAndChildren(item, preset);
}
/// <summary>
/// Possible bug, returns all items associated with an items tpl, could be multiple presets from globals.json
/// </summary>
/// <param name="item">Preset item</param>
/// <returns></returns>
public List<Item> GetPresetItemsByTpl(Item item)
{
throw new NotImplementedException();
/**
* Possible bug, returns all items associated with an items tpl, could be multiple presets from globals.json
* @param item Preset item
* @returns
*/
public getPresetItemsByTpl(item: IItem): IItem[] {
var presets = [];
for (var itemId in databaseService.getGlobals().ItemPresets) {
if (databaseService.getGlobals().ItemPresets[itemId]._items[0]._tpl === item._tpl) {
var presetItems = cloner.clone(databaseService.getGlobals().ItemPresets[itemId]._items);
presets.push(itemHelper.reparentItemAndChildren(item, presetItems));
}
}
return presets;
}
}
@@ -233,7 +233,7 @@ public record BarterScheme
public bool? SptQuestLocked { get; set; }
[JsonPropertyName("level")]
public double? Level { get; set; }
public int? Level { get; set; }
[JsonPropertyName("side")]
[JsonConverter(typeof(JsonStringEnumConverter))]
@@ -26,7 +26,7 @@ public record RagfairOffer
/** Handbook price */
[JsonPropertyName("itemsCost")]
public decimal? ItemsCost { get; set; }
public double? ItemsCost { get; set; }
/** Rouble price per item */
[JsonPropertyName("requirementsCost")]
@@ -72,7 +72,7 @@ public record OfferRequirement
public string? Template { get; set; }
[JsonPropertyName("count")]
public int? Count { get; set; }
public double? Count { get; set; }
[JsonPropertyName("onlyFunctional")]
public bool? OnlyFunctional { get; set; }
@@ -93,7 +93,7 @@ public record RagfairOfferUser
public string? Nickname { get; set; }
[JsonPropertyName("rating")]
public decimal? Rating { get; set; }
public double? Rating { get; set; }
[JsonPropertyName("memberType")]
public MemberCategory? MemberType { get; set; }
@@ -9,5 +9,5 @@ public record TplWithFleaPrice
// Roubles
[JsonPropertyName("price")]
public decimal? Price { get; set; }
public double? Price { get; set; }
}
+3 -5
View File
@@ -1222,7 +1222,7 @@ public class FenceService(
foreach (var plateSlot in plateSlots)
{
var plateTpl = plateSlot.Props.Filters[0].Plate;
if (plateTpl == null)
if (string.IsNullOrEmpty(plateTpl))
{
// Bsg data lacks a default plate, skip randomisng for this mod
continue;
@@ -1233,13 +1233,11 @@ public class FenceService(
var modItemDbDetails = itemHelper.GetItem(plateTpl).Value;
// Chance to remove plate
var plateExistsChance =
traderConfig.Fence.ChancePlateExistsInArmorPercent[
modItemDbDetails?.Properties?.ArmorClass?.ToString() ?? "3"];
var plateExistsChance = traderConfig.Fence.ChancePlateExistsInArmorPercent[modItemDbDetails?.Properties?.ArmorClass?.ToString() ?? "3"];
if (!randomUtil.GetChance100(plateExistsChance))
{
// Remove plate from armor
armorWithMods = armorItemAndMods.Where(item => item.SlotId.ToLower() != plateSlot.Name.ToLower())
armorItemAndMods = armorItemAndMods.Where(item => item.SlotId.ToLower() != plateSlot.Name.ToLower())
.ToList();
continue;
+2 -2
View File
@@ -175,7 +175,7 @@ public class PaymentService(
if (item.Upd.StackObjectsCount < currencyMaxStackSize) {
if (item.Upd.StackObjectsCount + calcAmount > currencyMaxStackSize) {
// calculate difference
calcAmount -= (currencyMaxStackSize - item.Upd.StackObjectsCount) ?? 0;
calcAmount -= (int) ((currencyMaxStackSize - item.Upd.StackObjectsCount) ?? 0);
item.Upd.StackObjectsCount = currencyMaxStackSize;
} else {
skipSendingMoneyToStash = true;
@@ -195,7 +195,7 @@ public class PaymentService(
Item rootCurrencyReward = new Item {
Id = _hashUtil.Generate(),
Template = currencyTpl,
Upd = new Upd { StackObjectsCount = Math.Round(calcAmount) },
Upd = new Upd { StackObjectsCount = Math.Round((double) calcAmount) }
};
// Ensure money is properly split to follow its max stack size limit
+3 -3
View File
@@ -13,9 +13,9 @@ public class RagfairOfferHolder(
ConfigServer configServer)
{
protected Dictionary<string, RagfairOffer> _offersById;
protected Dictionary<string, Dictionary<string, RagfairOffer>> _offersByTemplate;
protected Dictionary<string, Dictionary<string, RagfairOffer>> _offersByTrader;
protected Dictionary<string, RagfairOffer> _offersById = new();
protected Dictionary<string, Dictionary<string, RagfairOffer>> _offersByTemplate = new();
protected Dictionary<string, Dictionary<string, RagfairOffer>> _offersByTrader = new();
protected int _maxOffersPerTemplate = (int) configServer.GetConfig<RagfairConfig>().Dynamic.OfferItemCount.Max;
public RagfairOffer? GetOfferById(string id)
+1
View File
@@ -55,6 +55,7 @@ public class RandomUtil(ISptLogger<RandomUtil> _logger, ICloner _cloner)
return (float)GetSecureRandomNumber() * (max - min) + min;
}
/// <summary>
/// Generates a random floating-point number within the specified range ~15-17 digits (8 bytes).
/// </summary>
@@ -27,8 +27,7 @@
"5a7c2eca46aef81a7ca2145d": true,
"5ac3b934156ae10c4430e83c": true,
"5c0647fdd443bc2504c2d371": true,
"6617beeaa9cfa777ca915b7c": true,
"ragfair": false
"6617beeaa9cfa777ca915b7c": true
},
"dynamic": {
"purchasesAreFoundInRaid": false,