Compare commits

...

3 commits

Author SHA1 Message Date
3755d6e2a6 js files 2025-07-18 16:25:09 -05:00
57c563a139 feat(VCQL) 2025-07-18 16:25:03 -05:00
d5ece8e9b7 feat(VCQL) 2025-07-18 16:24:54 -05:00
30 changed files with 1862 additions and 0 deletions

Binary file not shown.

View file

@ -0,0 +1,413 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const itemTemplate = require("../templates/item_template.json");
const configJson = require("../config.json");
const modTGC_items_json_1 = __importDefault(require("../database/modTGC_items.json"));
const modTGC_clothes_json_1 = __importDefault(require("../database/modTGC_clothes.json"));
const globals_json_1 = __importDefault(require("../database/globals.json"));
const assort_json_1 = __importDefault(require("../database/traders/668aaff35fd574b6dcc4a686/assort.json"));
const suits_json_1 = __importDefault(require("../database/traders/668aaff35fd574b6dcc4a686/suits.json"));
class TGCItems {
db;
mydb;
logger;
jsonUtil;
async postDBLoad(container) {
this.logger = container.resolve("WinstonLogger");
this.jsonUtil = container.resolve("JsonUtil");
const databaseServer = container.resolve("DatabaseServer");
const modLoader = container.resolve("PreSptModLoader");
//Mod Info
const modFolderName = "MoxoPixel-TacticalGearComponent";
const modFullName = "Tactical Gear Component";
//Trader IDs
const traders = {
"PAINTERSHOP": "668aaff35fd574b6dcc4a686"
};
//Currency IDs
const currencies = {
"roubles": "5449016a4bdc2d6f028b456f",
"dollars": "5696686a4bdc2da3298b456a",
"euros": "569668774bdc2da2298b4568",
"bitcoin": "59faff1d86f7746c51718c9c",
"gp": "5d235b4d86f7742e017bc88a"
};
//Get the server database and our custom database
this.db = databaseServer.getTables();
this.mydb = {
modTgcItems: modTGC_items_json_1.default,
modTgcClothes: modTGC_clothes_json_1.default,
globals: globals_json_1.default,
traders: {
"668aaff35fd574b6dcc4a686": {
assort: assort_json_1.default,
suits: suits_json_1.default
}
}
};
this.logger.info("Loading: " + modFullName);
const secureContainerIds = [
"665ee77ccf2d642e98220bca",
"5857a8bc2459772bad15db29",
"64f6f4c5911bcdfe8b03b0dc",
"544a11ac4bdc2d470e8b456a",
"5857a8b324597729ab0a0e7d",
"59db794186f77448bc595262",
"664a55d84a90fc2c8a6305c9",
"5c093ca986f7740a1867ab12",
"676008db84e242067d0dc4c9"
];
const armbandId = "55d7217a4bdc2d86028b456d";
//Items
if (this.mydb.modTgcItems && typeof this.mydb.modTgcItems === "object" && Object.keys(this.mydb.modTgcItems).length > 0) {
for (const [modTGCID, modTGCItem] of Object.entries(this.mydb.modTgcItems)) {
//Items + Handbook
if (typeof modTGCItem === 'object' && "clone" in modTGCItem) {
this.cloneItem(modTGCItem.clone, modTGCID);
this.copyToFilters(modTGCItem.clone, modTGCID);
if ("PutInArmband" in modTGCItem && this.db.templates.items[armbandId]) {
this.db.templates.items[armbandId]._props.Slots[14]._props.filters[0].Filter.push(modTGCID);
}
if (configJson.PouchesInSecureContainer && "putInSecureContainer" in modTGCItem) {
for (const id of secureContainerIds) {
const item = this.db.templates.items[id];
if (item && item._props && item._props.Grids && item._props.Grids[0] && item._props.Grids[0]._props && item._props.Grids[0]._props.filters && item._props.Grids[0]._props.filters[0] && item._props.Grids[0]._props.filters[0].Filter) {
item._props.Grids[0]._props.filters[0].Filter.push(modTGCID);
}
}
}
}
else
this.createItem(modTGCID);
//Locales (Languages)
this.addLocales(modTGCID, modTGCItem);
}
}
else {
this.logger.warning("modTgcItems is missing or not an object in the mod database.");
}
//Item Filters
if (this.mydb.modTgcItems && typeof this.mydb.modTgcItems === "object" && Object.keys(this.mydb.modTgcItems).length > 0) {
for (const modTGCID in this.mydb.modTgcItems)
this.addToFilters(modTGCID);
}
//Clothing
if (this.mydb.modTgcClothes && typeof this.mydb.modTgcClothes === "object" && Object.keys(this.mydb.modTgcClothes).length > 0) {
for (const [modTGCID, modTGCArticle] of Object.entries(this.mydb.modTgcClothes)) {
//Articles + Handbook
if (typeof modTGCArticle === 'object' && "clone" in modTGCArticle) {
this.cloneClothing(modTGCArticle.clone, modTGCID);
}
//Locales (Languages)
this.addLocales(modTGCID, undefined, modTGCArticle);
}
}
else {
this.logger.warning("modTgcClothes is missing or not an object in the mod database.");
}
//Presets
if (this.mydb.globals && this.mydb.globals.ItemPresets) {
for (const preset in this.mydb.globals.ItemPresets) {
this.db.globals.ItemPresets[preset] = this.mydb.globals.ItemPresets[preset];
}
}
else {
this.logger.warning("modTGC: globals.ItemPresets is missing in the mod database.");
}
//Traders
for (const trader in traders) {
if (this.mydb.traders[traders[trader]] && this.mydb.traders[traders[trader]].suits) {
this.addTraderSuits(traders[trader]);
}
else {
this.logger.warning(`No suits for trader ${trader}`);
}
if (this.mydb.traders[traders[trader]] && this.mydb.traders[traders[trader]].assort) {
this.addTraderAssort(traders[trader]);
}
else {
this.logger.warning(`No assort for trader ${trader}`);
}
}
}
cloneItem(itemToClone, modTGCID) {
//If the item is enabled in the json
if (this.mydb.modTgcItems[modTGCID].enable == true) {
//Get a clone of the original item from the database
let modTGCItemOut = this.jsonUtil.clone(this.db.templates.items[itemToClone]);
//Change the necessary item attributes using the info in our database file modTgcItems.json
modTGCItemOut._id = modTGCID;
modTGCItemOut = this.compareAndReplace(modTGCItemOut, this.mydb.modTgcItems[modTGCID]["item"]);
//Add the new item to the database
this.db.templates.items[modTGCID] = modTGCItemOut;
//Create the handbook entry for the items
const handbookEntry = {
"Id": modTGCID,
"ParentId": this.mydb.modTgcItems[modTGCID]["handbook"]["ParentId"],
"Price": this.mydb.modTgcItems[modTGCID]["handbook"]["Price"]
};
//Add the handbook entry to the database
this.db.templates.handbook.Items.push(handbookEntry);
}
}
createItem(itemToCreate) {
//Create an item from scratch instead of cloning it
//Requires properly formatted entry in modTgcItems.json with NO "clone" attribute
//Get the new item object from the json
const newItem = this.mydb.modTgcItems[itemToCreate];
//If the item is enabled in the json
if (newItem.enable) {
//Check the structure of the new item in modTgcItems
const [pass, checkedItem] = this.checkItem(newItem);
if (!pass)
return;
//Add the new item to the database
this.db.templates.items[itemToCreate] = checkedItem;
//Create the handbook entry for the items
const handbookEntry = {
"Id": itemToCreate,
"ParentId": newItem["handbook"]["ParentId"],
"Price": newItem["handbook"]["Price"]
};
//Add the handbook entry to the database
this.db.templates.handbook.Items.push(handbookEntry);
}
}
checkItem(itemToCheck) {
let pass = true;
for (const level1 in itemTemplate) {
if (itemTemplate.hasOwnProperty(level1) && !(level1 in itemToCheck.item)) {
this.logger.error("ERROR - Missing attribute: \"" + level1 + "\" in your item entry!");
pass = false;
}
}
for (const prop in itemToCheck.item._props) {
if (itemToCheck.item._props.hasOwnProperty(prop) && !(prop in itemTemplate._props)) {
this.logger.warning("WARNING - Attribute: \"" + prop + "\" not found in item template!");
}
}
const itemOUT = {
"_id": itemToCheck.item._id,
"_name": itemToCheck.item._name,
"_parent": itemToCheck.item._parent,
"_props": itemToCheck.item._props,
"_type": itemToCheck.item._type,
"_proto": itemToCheck.item._proto
};
return [pass, itemOUT];
}
compareAndReplace(originalItem, attributesToChange) {
for (const key in attributesToChange) {
if (attributesToChange.hasOwnProperty(key)) {
if ((["boolean", "string", "number"].includes(typeof attributesToChange[key])) || Array.isArray(attributesToChange[key])) {
if (key in originalItem) {
originalItem[key] = attributesToChange[key];
}
else {
this.logger.warning("(Item: " + originalItem._id + ") WARNING: Could not find the attribute: \"" + key + "\" in the original item, make sure this is intended!");
originalItem[key] = attributesToChange[key];
}
}
else {
originalItem[key] = this.compareAndReplace(originalItem[key], attributesToChange[key]);
}
}
}
return originalItem;
}
getFilters(item) {
//Get the slots, chambers, cartridges, and conflicting items objects and return them.
const slots = (typeof this.db.templates.items[item]._props.Slots === "undefined") ? [] : this.db.templates.items[item]._props.Slots;
const chambers = (typeof this.db.templates.items[item]._props.Chambers === "undefined") ? [] : this.db.templates.items[item]._props.Chambers;
const cartridges = (typeof this.db.templates.items[item]._props.Cartridges === "undefined") ? [] : this.db.templates.items[item]._props.Cartridges;
const filters = slots.concat(chambers, cartridges);
const conflictingItems = (typeof this.db.templates.items[item]._props.ConflictingItems === "undefined") ? [] : this.db.templates.items[item]._props.ConflictingItems;
return [filters, conflictingItems];
}
copyToFilters(itemClone, modTGCID) {
//Find the original item in all compatible and conflict filters and add the clone to those filters as well
for (const item in this.db.templates.items) {
if (item in this.mydb.modTgcItems)
continue;
const [filters, conflictingItems] = this.getFilters(item);
for (const filter of filters) {
for (const id of filter._props.filters[0].Filter) {
if (id === itemClone)
filter._props.filters[0].Filter.push(modTGCID);
}
}
for (const conflictID of conflictingItems)
if (conflictID === itemClone)
conflictingItems.push(modTGCID);
}
}
addToFilters(modTGCID) {
//Add a new item to compatibility & conflict filters of pre-existing items
//Add additional compatible and conflicting items to new item filters (manually adding more than the ones that were cloned)
const modTGCNewItem = this.mydb.modTgcItems[modTGCID];
//If the item is enabled in the json
if (modTGCNewItem.enable) {
this.logger.debug("addToFilters: " + modTGCID);
//Manually add items into an THISMOD item's filters
if ("addToThisItemsFilters" in modTGCNewItem) {
const modTGCItemFilters = this.getFilters(modTGCID)[0];
let modTGCConflictingItems = this.getFilters(modTGCID)[1];
for (const modSlotName in modTGCNewItem.addToThisItemsFilters) {
if (modSlotName === "conflicts")
modTGCConflictingItems = modTGCConflictingItems.concat(modTGCNewItem.addToThisItemsFilters.conflicts);
else {
for (const filter in modTGCItemFilters) {
if (modSlotName === modTGCItemFilters[filter]._name) {
const slotFilter = modTGCItemFilters[filter]._props.filters[0].Filter;
const newFilter = slotFilter.concat(modTGCNewItem.addToThisItemsFilters[modSlotName]);
modTGCItemFilters[filter]._props.filters[0].Filter = newFilter;
}
}
}
}
}
//Manually add THISMOD items to pre-existing item filters.
if ("addToExistingItemFilters" in modTGCNewItem) {
for (const modSlotName in modTGCNewItem.addToExistingItemFilters) {
if (modSlotName === "conflicts") {
for (const conflictingItem of modTGCNewItem.addToExistingItemFilters[modSlotName]) {
const conflictingItems = this.getFilters(conflictingItem)[1];
conflictingItems.push(modTGCID);
}
}
else {
for (const compatibleItem of modTGCNewItem.addToExistingItemFilters[modSlotName]) {
const filters = this.getFilters(compatibleItem)[0];
for (const filter of filters) {
if (modSlotName === filter._name)
filter._props.filters[0].Filter.push(modTGCID);
}
}
}
}
}
}
}
cloneClothing(itemToClone, modTGCID) {
if (this.mydb.modTgcClothes[modTGCID].enable || !("enable" in this.mydb.modTgcClothes[modTGCID])) {
//Get a clone of the original item from the database
let tgcClothingOut = this.jsonUtil.clone(this.db.templates.customization[itemToClone]);
//Change the necessary clothing item attributes using the info in our database file modTgcClothes.json
tgcClothingOut._id = modTGCID;
tgcClothingOut._name = modTGCID;
tgcClothingOut = this.compareAndReplace(tgcClothingOut, this.mydb.modTgcClothes[modTGCID]["customization"]);
//Add the new item to the database
this.db.templates.customization[modTGCID] = tgcClothingOut;
this.logger.debug("Clothing item " + modTGCID + " created as a clone of " + itemToClone + " and added to database.");
}
}
addTraderAssort(trader) {
if (this.mydb.traders[trader] &&
this.mydb.traders[trader].assort &&
this.db.traders[trader] &&
this.db.traders[trader].assort) {
// Merge items
if (Array.isArray(this.mydb.traders[trader].assort.items)) {
for (const item of this.mydb.traders[trader].assort.items) {
this.db.traders[trader].assort.items.push(item);
}
}
// Merge barter_scheme
if (typeof this.mydb.traders[trader].assort.barter_scheme === "object") {
Object.assign(this.db.traders[trader].assort.barter_scheme, this.mydb.traders[trader].assort.barter_scheme);
}
// Merge loyal_level_items
if (typeof this.mydb.traders[trader].assort.loyal_level_items === "object") {
Object.assign(this.db.traders[trader].assort.loyal_level_items, this.mydb.traders[trader].assort.loyal_level_items);
}
}
else {
this.logger.warning(`Trader ${trader} does not exist in SPT database or has no assort property. Cannot add assort.`);
}
}
addTraderSuits(trader) {
// Only do anything if a suits.json file is included for trader in this mod
if (this.mydb.traders[trader] &&
typeof this.mydb.traders[trader].suits !== "undefined" &&
this.db.traders[trader] &&
this.db.traders[trader].base) {
// Enable customization for that trader
this.db.traders[trader].base.customization_seller = true;
// Create the suits array if it doesn't already exist in SPT database so we can push to it
if (typeof this.db.traders[trader].suits === "undefined")
this.db.traders[trader].suits = [];
// Push all suits
for (const suit of this.mydb.traders[trader].suits)
this.db.traders[trader].suits.push(suit);
}
else {
this.logger.warning(`Trader ${trader} does not exist in SPT database or has no base property. Cannot add suits.`);
}
}
addLocales(modTGCID, modTGCItem, modTGCArticle) {
const name = modTGCID + " Name";
const shortname = modTGCID + " ShortName";
const description = modTGCID + " Description";
const isItem = typeof modTGCItem !== "undefined";
const modTGCEntry = isItem ? modTGCItem : modTGCArticle;
for (const localeID in this.db.locales.global) //For each possible locale/language in SPT's database
{
let localeEntry;
if (isItem && "locales" in modTGCEntry) {
if (localeID in modTGCEntry.locales) //If the language is entered in modTgcItems, use that
{
localeEntry = {
"Name": modTGCEntry.locales[localeID].Name,
"ShortName": modTGCEntry.locales[localeID].ShortName,
"Description": modTGCEntry.locales[localeID].Description
};
}
else //Otherwise use english as the default
{
localeEntry = {
"Name": modTGCEntry.locales.en.Name,
"ShortName": modTGCEntry.locales.en.ShortName,
"Description": modTGCEntry.locales.en.Description
};
}
this.db.locales.global[localeID][name] = localeEntry.Name;
this.db.locales.global[localeID][shortname] = localeEntry.ShortName;
this.db.locales.global[localeID][description] = localeEntry.Description;
}
else if (!isItem && "locales" in modTGCEntry) {
const locales = modTGCEntry.locales;
if (localeID in locales) {
localeEntry = {
"Name": locales[localeID].Name,
"Description": locales[localeID].Description || ""
};
}
else {
localeEntry = {
"Name": locales.en.Name,
"Description": locales.en.Description || ""
};
}
this.db.locales.global[localeID][name] = localeEntry.Name;
this.db.locales.global[localeID][description] = localeEntry.Description;
}
else {
if (isItem)
this.logger.warning("WARNING: Missing locale entry for item: " + modTGCID);
else
this.logger.debug("No locale entries for item/clothing: " + modTGCID);
}
//Also add the necessary preset locale entries if they exist
if (isItem && modTGCItem.presets) {
for (const preset in modTGCItem.presets) {
this.db.locales.global[localeID][preset] = modTGCItem.presets[preset];
}
}
}
}
}
module.exports = { mod: new TGCItems() };
//# sourceMappingURL=mod.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,438 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomItemsManager = void 0;
const ConfigTypes_1 = require("C:/snapshot/project/obj/models/enums/ConfigTypes");
/**
* Manages creation of custom items for the Painter mod
*/
class CustomItemsManager {
logger;
constructor(logger) {
this.logger = logger;
}
/**
* Create all custom items for the Painter mod
*/
createCustomItems(customItemService, configServer, tables, inventoryConfig, enableLootBoxes) {
// Create figurines
this.createFigurines(customItemService, configServer, tables);
// Create loot boxes if enabled
if (enableLootBoxes) {
this.createLootBoxes(customItemService, tables, inventoryConfig);
}
}
/**
* Create Batman and Golden Turd figurines
*/
createFigurines(customItemService, configServer, tables) {
const batmanFigurine = {
itemTplToClone: "655c67782a1356436041c9c5",
overrideProperties: {
Name: "Batman figurine",
ShortName: "Batman",
Description: "Rare larger figurine of Batman, a superhero of the American comic book publisher DC Comics, Batman.",
Prefab: {
"path": "figurine_batman.bundle",
"rcid": ""
},
CanRequireOnRagfair: false,
CanSellOnRagfair: true
},
parentId: "57864a3d24597754843f8721",
newId: "672e2e75d78fe9e90c8cb393",
fleaPriceRoubles: 154000,
handbookPriceRoubles: 154000,
handbookParentId: "5b47574386f77428ca22b2f1",
locales: {
en: {
name: "Batman figurine",
shortName: "Batman",
description: "Rare larger figurine of Batman, a superhero of the American comic book publisher DC Comics, Batman."
}
}
};
const dodoFigurine = {
itemTplToClone: "59e3647686f774176a362507",
overrideProperties: {
Name: "Golden Turd figurine",
ShortName: "Turd",
Description: "Awarded to the master of annoying tasks, it is said that it was made from the golden poop of a dodo bird.",
Prefab: {
"path": "dodo338383899.bundle",
"rcid": ""
},
CanRequireOnRagfair: false,
CanSellOnRagfair: false,
Weight: 69,
ExaminedByDefault: false
},
parentId: "57864a3d24597754843f8721",
newId: "684db00229850b2f1f7832c1",
fleaPriceRoubles: 69,
handbookPriceRoubles: 69,
handbookParentId: "5b47574386f77428ca22b2f1",
locales: {
en: {
name: "Golden Turd figurine",
shortName: "Turd",
description: "Awarded to the master of annoying tasks, it is said that it was made from the golden poop of a dodo bird."
}
}
};
const mosFigurine = {
itemTplToClone: "59e3647686f774176a362507",
overrideProperties: {
Name: "Moscovium (Material 115)",
ShortName: "MC-115",
Description: "Sealed compound made from Moscovium, a synthetic element with atomic number 115, known for its unique properties. Does it bend space and time? Who knows.",
Prefab: {
"path": "mos115.bundle",
"rcid": ""
},
CanRequireOnRagfair: false,
CanSellOnRagfair: false,
Weight: 69,
ExaminedByDefault: false
},
parentId: "57864a3d24597754843f8721",
newId: "685867727d49afb420c2b29e",
fleaPriceRoubles: 69,
handbookPriceRoubles: 69,
handbookParentId: "5b47574386f77428ca22b2f1",
locales: {
en: {
name: "Moscovium (Material 115)",
shortName: "MC-115",
description: "Sealed compound made from Moscovium, a synthetic element with atomic number 115, known for its unique properties. Does it bend space and time? Who knows."
}
}
};
customItemService.createItemFromClone(batmanFigurine);
customItemService.createItemFromClone(dodoFigurine);
customItemService.createItemFromClone(mosFigurine);
this.logger.info("Painter custom items added");
// Add figurines to Hall of Fame
this.addToHallOfFame(tables, "672e2e75d78fe9e90c8cb393", "655c67782a1356436041c9c5", "Batman figurine");
this.addToHallOfFame(tables, "684db00229850b2f1f7832c1", "59e3647686f774176a362507", "Golden Turd figurine");
this.addToHallOfFame(tables, "685867727d49afb420c2b29e", "59e3647686f774176a362507", "Moscovium (Material 115)");
// Prevent dodoFigurine from being lootable
this.preventDodoFromBeingLootable(configServer);
this.preventMosFromBeingLootable(configServer);
}
/**
* Create mystery loot boxes
*/
createLootBoxes(customItemService, tables, inventoryConfig) {
const mysteryBoxOne = {
itemTplToClone: "6489b2b131a2135f0d7d0fcb",
overrideProperties: {
Name: "Painter's Special Delivery",
ShortName: "Painter Lootbox",
Description: "A lootbox filled with various items, including some of the most sought after barter items.",
Weight: 25,
Prefab: {
"path": "mysterybox.bundle",
"rcid": ""
},
Width: 4,
Height: 4,
BackgroundColor: "blue",
CanRequireOnRagfair: false,
CanSellOnRagfair: false
},
parentId: "62f109593b54472778797866",
newId: "668ff5bde41a0cce3b142464",
fleaPriceRoubles: 500000,
handbookPriceRoubles: 500000,
handbookParentId: "5b5f6fa186f77409407a7eb7",
locales: {
en: {
name: "Painter's Special Delivery",
shortName: "Painter Lootbox",
description: "A lootbox filled with 20 items, including some of the most sought after barter items. Get a LEDX or go broke. The choise is yours."
}
}
};
customItemService.createItemFromClone(mysteryBoxOne);
// Change item _name to remove it from the *actual* sealed weapon crate logic, this removes it from airdrops and allows easier access to change the contents
const customIteminDB = tables.templates.items["668ff5bde41a0cce3b142464"];
customIteminDB._name = "668ff5bde41a0cce3b142464";
// Configure loot pool for mystery box
this.configureMysteryBoxLoot(inventoryConfig);
const mysteryBoxTwo = {
itemTplToClone: "6489981f7063b903ff4b8565",
overrideProperties: {
Name: "Painter's War Box",
ShortName: "Painter Warbox",
Description: "A lootbox filled with various items, some of the most sought after military items.",
Weight: 30,
Prefab: {
"path": "mysterybox_2.bundle",
"rcid": ""
},
Width: 4,
Height: 3,
BackgroundColor: "blue",
CanRequireOnRagfair: false,
CanSellOnRagfair: false
},
parentId: "62f109593b54472778797866",
newId: "6699546547ad52e0fccf6da9",
fleaPriceRoubles: 500000,
handbookPriceRoubles: 500000,
handbookParentId: "5b5f6fa186f77409407a7eb7",
locales: {
en: {
name: "Painter's War Box",
shortName: "Painter Warbox",
description: "A lootbox filled with various items, some of the most sought after military items."
}
}
};
customItemService.createItemFromClone(mysteryBoxTwo);
this.logger.info("Painter loot boxes added");
}
/**
* Configure the mystery box loot pool with various high-value items
*/
configureMysteryBoxLoot(inventoryConfig) {
inventoryConfig.randomLootContainers["668ff5bde41a0cce3b142464"] = {
rewardCount: 20,
foundInRaid: true,
rewardTplPool: {
"5bc9be8fd4351e00334cae6e": 5,
"5d03794386f77420415576f5": 1,
"5672cb124bdc2d1a0f8b4568": 10,
"6389c85357baa773a825b356": 1,
"59faf98186f774067b6be103": 5,
"5d1b32c186f774252167a530": 5,
"590de71386f774347051a052": 5,
"590de7e986f7741b096e5f32": 5,
"573475fb24597737fb1379e1": 10,
"6389c6c7dbfd5e4b95197e68": 5,
"5e2af4d286f7746d4159f07a": 5,
"62a0a098de7ac8199358053b": 5,
"62a091170b9d3c46de5b6cf2": 5,
"5bc9c049d4351e44f824d360": 5,
"62a08f4c4f842e1bd12d9d62": 5,
"57347c5b245977448d35f6e1": 10,
"59e361e886f774176c10a2a5": 5,
"62a0a043cf4a99369e2624a5": 5,
"59e3606886f77417674759a5": 5,
"56742c324bdc2d150f8b456d": 10,
"5c1265fc86f7743f896a21c2": 5,
"5d1b309586f77425227d1676": 10,
"59e3639286f7741777737013": 1,
"619cbfeb6b8a1b37a54eebfa": 5,
"5c06779c86f77426e00dd782": 10,
"5e54f6af86f7742199090bf3": 5,
"5af0484c86f7740f02001f7f": 10,
"60391a8b3364dc22b04d0ce5": 5,
"62a09ee4cf4a99369e262453": 10,
"5c06782b86f77426df5407d2": 10,
"5733279d245977289b77ec24": 5,
"59e3658a86f7741776641ac4": 5,
"573474f924597738002c6174": 5,
"5c1267ee86f77416ec610f72": 5,
"57347b8b24597737dd42e192": 10,
"59e358a886f7741776641ac3": 5,
"590c2c9c86f774245b1f03f2": 10,
"5e2af41e86f774755a234b67": 5,
"59e35cbb86f7741778269d83": 5,
"5734779624597737e04bf329": 10,
"56742c284bdc2d98058b456d": 10,
"5e2aee0a86f774755a234b62": 5,
"590a386e86f77429692b27ab": 5,
"5bc9b9ecd4351e3bac122519": 5,
"5d1b3f2d86f774253763b735": 5,
"590a373286f774287540368b": 5,
"57347c1124597737fb1379e3": 10,
"5734781f24597737e04bf32a": 5,
"5672cb304bdc2dc2088b456a": 5,
"59e35de086f7741778269d84": 5,
"5d1b2fa286f77425227d1674": 5,
"6389c70ca33d8c4cdf4932c6": 5,
"590a3cd386f77436f20848cb": 10,
"5d1b371186f774253763a656": 5,
"6389c7f115805221fb410466": 1,
"63a0b208f444d32d6f03ea1e": 1,
"5bc9b355d4351e6d1509862a": 5,
"5d63d33b86f7746ea9275524": 10,
"5d4042a986f7743185265463": 10,
"5e2af47786f7746d404f3aaa": 5,
"5d1b2f3f86f774252167a52c": 1,
"5b43575a86f77424f443fe62": 5,
"590a3efd86f77437d351a25b": 5,
"590c595c86f7747884343ad7": 5,
"5672cb724bdc2dc2088b456b": 5,
"5bc9b720d4351e450201234b": 5,
"62a09cfe4f842e1bd12da3e4": 5,
"5734758f24597738025ee253": 5,
"5bc9bc53d4351e00367fbcee": 5,
"5d235a5986f77443f6329bc6": 5,
"57347ca924597744596b4e71": 1,
"5e2aedd986f7746d404f3aa4": 5,
"5d6fc78386f77449d825f9dc": 5,
"5d6fc87386f77449db3db94e": 5,
"590c5a7286f7747884343aea": 5,
"5d1b317c86f7742523398392": 5,
"573478bc24597738002c6175": 5,
"5e2af2bc86f7746d3f3c33fc": 5,
"5734795124597738002c6176": 10,
"5d0377ce86f774186372f689": 1,
"5e2af29386f7746d4159f077": 5,
"5c0530ee86f774697952d952": 1,
"5d1b392c86f77425243e98fe": 10,
"60b0f7057897d47c5b04ab94": 5,
"60b0f561c4449e4cb624c1d7": 5,
"590a391c86f774385a33c404": 5,
"573476d324597737da2adc13": 10,
"5b4335ba86f7744d2837a264": 5,
"619cc01e0a7c3a1a2731940c": 5,
"5d40419286f774318526545f": 10,
"5d1b36a186f7742523398433": 1,
"61bf7b6302b3924be92fa8c3": 10,
"6389c7750ef44505c87f5996": 1,
"5d0375ff86f774186372f685": 1,
"5d0376a486f7747d8050965c": 1,
"5c052f6886f7746b1e3db148": 1,
"619cbf476b8a1b37a54eebf8": 5,
"5d03784a86f774203e7e0c4d": 1,
"5d0378d486f77420421a5ff4": 1,
"5d40425986f7743185265461": 10,
"5d1b2ffd86f77425243e8d17": 10,
"5d0379a886f77420407aa271": 1,
"5bc9c377d4351e3bac12251b": 5,
"5af0534a86f7743b6f354284": 5,
"5d4041f086f7743cac3f22a7": 5,
"59e3556c86f7741776641ac2": 10,
"5e2af02c86f7746d420957d4": 10,
"590c31c586f774245e3141b2": 10,
"59e35ef086f7741777737012": 10,
"59e35abd86f7741778269d82": 5,
"59e3596386f774176c10a2a2": 5,
"5c12688486f77426843c7d32": 5,
"573477e124597737dd42e191": 10,
"5d03775b86f774203e7e0c4b": 1,
"5d1b313086f77425227d1678": 10,
"59faff1d86f7746c51718c9c": 1,
"59e366c186f7741778269d85": 10,
"5d1b3a5d86f774252167ba22": 5,
"619cbfccbedcde2f5b3f7bdd": 5,
"590c2b4386f77425357b6123": 10,
"5af04b6486f774195a3ebb49": 10,
"5c052e6986f7746b207bc3c9": 1,
"5af0561e86f7745f5f3ad6ac": 5,
"59e36c6f86f774176c10a2a7": 5,
"57347c2e24597744902c94a1": 5,
"5d1b327086f7742525194449": 5,
"62a09cb7a04c0c5c6e0a84f8": 5,
"590a3b0486f7743954552bdb": 1,
"577e1c9d2459773cd707c525": 5,
"59fafb5d86f774067a6f2084": 1,
"5d1c774f86f7746d6620f8db": 10,
"57347baf24597738002c6178": 10,
"60391afc25aff57af81f7085": 1,
"5e54f62086f774219b0f1937": 5,
"590a358486f77429692b2790": 5,
"5e2aef7986f7746d3f3c33f5": 5,
"5e2af4a786f7746d3f3c3400": 5,
"59faf7ca86f7740dbe19f6c2": 5,
"5d1b31ce86f7742523398394": 10,
"5d40412b86f7743cb332ac3a": 5,
"590c2d8786f774245b1f03f3": 10,
"57347c77245977448d35f6e2": 10,
"62a0a0bb621468534a797ad5": 5,
"61bf83814088ec1a363d7097": 5,
"590c35a486f774273531c822": 10,
"5d1b39a386f774252339976f": 10,
"5bc9bdb8d4351e003562b8a1": 5,
"5e2af00086f7746d3f3c33f7": 10,
"5c13cd2486f774072c757944": 10,
"590a3c0a86f774385a33c450": 10,
"5734770f24597738025ee254": 10,
"5e2af37686f774755a234b65": 10,
"5c12620d86f7743f8b198b72": 1,
"5c13cef886f774072e618e82": 10,
"590c2e1186f77425357b6124": 5,
"57347c93245977448d35f6e3": 10,
"60391b0fb847c71012789415": 5,
"57347cd0245977445a2d6ff1": 10,
"5e2af22086f7746d3f3c33fa": 10,
"5c052fb986f7746b2101e909": 1,
"590a3d9c86f774385926e510": 10,
"5909e99886f7740c983b9984": 10,
"5f745ee30acaeb0d490d8c5b": 5,
"5c05308086f7746b2101e90b": 1,
"5c05300686f7746dce784e5d": 1,
"5d1b385e86f774252167b98a": 5,
"590c5bbd86f774785762df04": 10,
"590c5c9f86f77477c91c36e7": 10,
"5d1c819a86f774771b0acd6c": 5,
"573476f124597737e04bf328": 10,
"59e3647686f774176a362507": 5,
"5d1b304286f774253763a528": 5,
"590c311186f77424d1667482": 5,
"590c346786f77423e50ed342": 10,
"56742c2e4bdc2d95058b456d": 10
}
};
}
/**
* Add custom figurines to Hall of Fame filters
*/
addToHallOfFame(tables, newItemId, originalItemId, itemName) {
const hallOfFame1 = tables.templates.items["63dbd45917fff4dee40fe16e"];
const hallOfFame2 = tables.templates.items["65424185a57eea37ed6562e9"];
const hallOfFame3 = tables.templates.items["6542435ea57eea37ed6562f0"];
const hallOfFames = [hallOfFame1, hallOfFame2, hallOfFame3];
let totalAdditions = 0;
hallOfFames.forEach((hall) => {
if (hall && hall._props && hall._props.Slots) {
let additionsThisHall = 0;
for (const slot of hall._props.Slots) {
if (slot._props && slot._props.filters) {
for (const filter of slot._props.filters) {
// Only add the new item if the original cloned item is already in this filter
if (filter.Filter && filter.Filter.includes(originalItemId) && !filter.Filter.includes(newItemId)) {
filter.Filter.push(newItemId);
additionsThisHall++;
totalAdditions++;
}
}
}
}
if (additionsThisHall > 0) {
this.logger.debug(`Added ${itemName} to ${additionsThisHall} slot(s) in ${hall._name}`);
}
}
});
if (totalAdditions > 0) {
this.logger.debug(`Successfully added ${itemName} (${newItemId}) to ${totalAdditions} total Hall of Fame slot(s)`);
}
else {
this.logger.debug(`WARNING: No Hall of Fame slots found for ${itemName} - original item ${originalItemId} may not be in any Hall of Fame filters`);
}
}
/**
* Prevent the Golden Turd figurine from being lootable
*/
preventDodoFromBeingLootable(configServer) {
const itemConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.ITEM);
const dodoFigurineId = "684db00229850b2f1f7832c1";
// Add to lootable item blacklist to prevent it from spawning in any loot containers or loose loot
if (!itemConfig.lootableItemBlacklist.includes(dodoFigurineId)) {
itemConfig.lootableItemBlacklist.push(dodoFigurineId);
}
}
preventMosFromBeingLootable(configServer) {
const itemConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.ITEM);
const mosFigurineId = "685867727d49afb420c2b29e";
// Add to lootable item blacklist to prevent it from spawning in any loot containers or loose loot
if (!itemConfig.lootableItemBlacklist.includes(mosFigurineId)) {
itemConfig.lootableItemBlacklist.push(mosFigurineId);
}
}
}
exports.CustomItemsManager = CustomItemsManager;
//# sourceMappingURL=CustomItemsManager.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,236 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const ConfigTypes_1 = require("C:/snapshot/project/obj/models/enums/ConfigTypes");
const Traders_1 = require("C:/snapshot/project/obj/models/enums/Traders");
const CustomItemsManager_1 = require("./CustomItemsManager");
const configJson = __importStar(require("../config.json"));
const baseJson = __importStar(require("../db/base.json"));
const assortJson = __importStar(require("../db/assort.json"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const modPath = path.normalize(path.join(__dirname, ".."));
class PainterTrader {
mod;
logger;
configServer;
ragfairConfig;
constructor() {
this.mod = "aMoxoPixel-Painter";
}
preSptLoad(container) {
this.logger = container.resolve("WinstonLogger");
const PreSptModLoader = container.resolve("PreSptModLoader");
const imageRouter = container.resolve("ImageRouter");
const configServer = container.resolve("ConfigServer");
const traderConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.TRADER);
const questConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.QUEST);
if (!traderConfig.moddedTraders) {
traderConfig.moddedTraders = { clothingService: [] };
}
if (configJson.enableRepeatableQuests) {
const PainterRepeatQuests = {
traderId: "668aaff35fd574b6dcc4a686",
name: "painter",
questTypes: ["Completion", "Exploration", "Elimination"],
rewardBaseWhitelist: [
"543be6564bdc2df4348b4568",
"5448e5284bdc2dcb718b4567",
"5485a8684bdc2da71d8b4567",
"57864a3d24597754843f8721",
"55818af64bdc2d5b648b4570",
"57864e4c24597754843f8723",
"57864a66245977548f04a81f",
"57864ee62459775490116fc1",
"590c745b86f7743cc433c5f2"
],
rewardCanBeWeapon: true,
weaponRewardChancePercent: 20
};
questConfig.repeatableQuests[0].traderWhitelist.push(PainterRepeatQuests); // Daily quests
questConfig.repeatableQuests[1].traderWhitelist.push(PainterRepeatQuests); // Weekly quests
this.logger.info("Painter repeatable quests added to quest config");
}
this.registerProfileImage(PreSptModLoader, imageRouter);
this.setupTraderUpdateTime(traderConfig);
this.setupTraderServices(traderConfig);
Traders_1.Traders["668aaff35fd574b6dcc4a686"] = "668aaff35fd574b6dcc4a686";
}
postDBLoad(container) {
this.configServer = container.resolve("ConfigServer");
this.ragfairConfig = this.configServer.getConfig(ConfigTypes_1.ConfigTypes.RAGFAIR);
const configServer = container.resolve("ConfigServer");
const imageRouter = container.resolve("ImageRouter");
const jsonUtil = container.resolve("JsonUtil");
const databaseServer = container.resolve("DatabaseServer");
const databaseService = container.resolve("DatabaseService");
const customItem = container.resolve("CustomItemService");
const inventoryConfig = configServer.getConfig(ConfigTypes_1.ConfigTypes.INVENTORY);
const tables = databaseService.getTables();
if (configJson.enableRepeatableQuests) {
const repeatableQuests = databaseServer.getTables().templates.repeatableQuests;
const rqLocales = databaseServer.getTables().locales.global.en;
if (repeatableQuests.templates.Elimination) {
repeatableQuests.templates.Elimination.successMessageText = "A damn beast you are, hehe. Good work, here's your share.";
repeatableQuests.templates.Elimination.description = "I have a mission for you. I need you to eliminate some trash from Tarkov's streets. You up for it?";
}
if (repeatableQuests.templates.Completion) {
repeatableQuests.templates.Completion.successMessageText = "There you are! You got everything? Good stuff.";
repeatableQuests.templates.Completion.description = "I have a mission for you. I need you to gather some items for me. You up for it?";
}
if (repeatableQuests.templates.Exploration) {
repeatableQuests.templates.Exploration.successMessageText = "Marvelous, young man. Thank you for some fine work.";
repeatableQuests.templates.Exploration.description = "Ah, mercenary, do you want to do a good deed? My clients are asking to ensure a safe area to conduct a specific secret operation. I would like to appoint you for this, as you are the most competent of the local workers. You will have to survey the area and report back to me. Good luck.";
}
// Update localization files
rqLocales["616041eb031af660100c9967 successMessageText 668aaff35fd574b6dcc4a686 0"] = "Marvelous, young man. Thank you for the work.";
rqLocales["616041eb031af660100c9967 description 668aaff35fd574b6dcc4a686 0"] = "Ah, mercenary, do you want to do a good deed? My clients are asking to ensure a safe area to conduct a specific secret operation. I would like to appoint you for this, as you are the most competent of the local workers. You will have to survey the area and report back to me. Good luck.";
rqLocales["61604635c725987e815b1a46 successMessageText 668aaff35fd574b6dcc4a686 0"] = "There you are! You got everything? Good stuff.";
rqLocales["61604635c725987e815b1a46 description 668aaff35fd574b6dcc4a686 0"] = "I have a mission for you. I need you to gather some items for me. You up for it?";
rqLocales["616052ea3054fc0e2c24ce6e successMessageText 668aaff35fd574b6dcc4a686 0"] = "A damn beast you are, hehe. Good work, here's your share.";
rqLocales["616052ea3054fc0e2c24ce6e description 668aaff35fd574b6dcc4a686 0"] = "I have a mission for you. I need you to eliminate some trash from Tarkov's streets. You up for it?";
this.logger.info("Painter repeatable quest messages added to localization files");
}
this.addTraderToDb(baseJson, tables, jsonUtil);
this.addTraderToLocales(tables, baseJson.name, "Ivan Samoylov", baseJson.nickname, baseJson.location, "Ivan Samoylov is a master craftsman renowned for his exceptional skill in creating exquisite weapon cosmetics. With an innate talent for blending artistry and functionality, he transforms ordinary weapons into mesmerizing works of art.");
this.ragfairConfig.traders[baseJson._id] = true;
this.importQuests(tables);
this.importQuestLocales(tables);
this.routeQuestImages(imageRouter);
// Create all custom items using the CustomItemsManager
const customItemsManager = new CustomItemsManager_1.CustomItemsManager(this.logger);
customItemsManager.createCustomItems(customItem, configServer, tables, inventoryConfig, configJson.enableLootBoxes);
}
registerProfileImage(preSptModLoader, imageRouter) {
const imageFilepath = `./${preSptModLoader.getModPath(this.mod)}res`;
imageRouter.addRoute(baseJson.avatar.replace(".jpg", ""), `${imageFilepath}/painter.jpg`);
}
setupTraderUpdateTime(traderConfig) {
const traderRefreshRecord = { traderId: baseJson._id, seconds: { min: 2000, max: 6600 } };
traderConfig.updateTime.push(traderRefreshRecord);
}
setupTraderServices(traderConfig) {
const traderId = baseJson._id;
if (!traderConfig.moddedTraders) {
traderConfig.moddedTraders = { clothingService: [] };
}
traderConfig.moddedTraders.clothingService.push(traderId);
}
addTraderToDb(traderDetailsToAdd, tables, jsonUtil) {
tables.traders[traderDetailsToAdd._id] = {
assort: jsonUtil.deserialize(jsonUtil.serialize(assortJson)),
base: jsonUtil.deserialize(jsonUtil.serialize(traderDetailsToAdd)),
questassort: {
started: {},
success: {
"672e2804a0529208b4e10e18": "668aad3c3ff8f5b258e3a65b",
"672e284a363b798192b802af": "668c18eb12542b3c3ff6e20f",
"672e289bb4096716fcb918a7": "668c18eb12542b3c3ff6e20f"
},
fail: {}
}
};
}
addTraderToLocales(tables, fullName, firstName, nickName, location, description) {
const locales = Object.values(tables.locales.global);
for (const locale of locales) {
locale[`${baseJson._id} FullName`] = fullName;
locale[`${baseJson._id} FirstName`] = firstName;
locale[`${baseJson._id} Nickname`] = nickName;
locale[`${baseJson._id} Location`] = location;
locale[`${baseJson._id} Description`] = description;
}
}
loadFiles(dirPath, extName, cb) {
if (!fs.existsSync(dirPath))
return;
const dir = fs.readdirSync(dirPath, { withFileTypes: true });
dir.forEach(item => {
const itemPath = path.normalize(`${dirPath}/${item.name}`);
if (item.isDirectory())
this.loadFiles(itemPath, extName, cb);
else if (extName.includes(path.extname(item.name)))
cb(itemPath);
});
}
importQuests(tables) {
this.loadFiles(`${modPath}/db/quests/`, [".json"], function (filePath) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const item = require(filePath);
if (Object.keys(item).length < 1)
return;
for (const quest in item) {
tables.templates.quests[quest] = item[quest];
}
});
}
importQuestLocales(tables) {
const serverLocales = ["ch", "cz", "en", "es", "es-mx", "fr", "ge", "hu", "it", "jp", "pl", "po", "ru", "sk", "tu"];
const addedLocales = {};
for (const locale of serverLocales) {
this.loadFiles(`${modPath}/db/locales/${locale}`, [".json"], function (filePath) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const localeFile = require(filePath);
if (Object.keys(localeFile).length < 1)
return;
for (const currentItem in localeFile) {
tables.locales.global[locale][currentItem] = localeFile[currentItem];
if (!Object.keys(addedLocales).includes(locale))
addedLocales[locale] = {};
addedLocales[locale][currentItem] = localeFile[currentItem];
}
});
}
for (const locale of serverLocales) {
if (locale == "en")
continue;
for (const englishItem in addedLocales["en"]) {
if (locale in addedLocales) {
if (englishItem in addedLocales[locale])
continue;
}
if (tables.locales.global[locale] != undefined)
tables.locales.global[locale][englishItem] = addedLocales["en"][englishItem];
}
}
}
routeQuestImages(imageRouter) {
this.loadFiles(`${modPath}/res/quests/`, [".png", ".jpg"], function (filePath) {
imageRouter.addRoute(`/files/quest/icon/${path.basename(filePath, path.extname(filePath))}`, filePath);
});
}
}
module.exports = { mod: new PainterTrader() };
//# sourceMappingURL=mod.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,41 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mod = void 0;
const tsyringe_1 = require("C:/snapshot/project/node_modules/tsyringe");
const jsonc_1 = require("C:/snapshot/project/node_modules/jsonc");
const path_1 = __importDefault(require("path"));
class BossesHaveLegaMedals {
logger;
static fileSystemSync = tsyringe_1.container.resolve("FileSystemSync");
static config = jsonc_1.jsonc.parse(BossesHaveLegaMedals.fileSystemSync.read(path_1.default.resolve(__dirname, "../config/config.jsonc")));
postDBLoad(container) {
const databaseService = container.resolve("DatabaseService");
this.logger = container.resolve("WinstonLogger");
const tables = databaseService.getTables();
let chance = BossesHaveLegaMedals.config.legaMedalChance;
if (chance <= 0)
chance = 1;
for (const botType in tables.bots.types) {
if (!botType.includes("boss") || botType == "bosstest") {
continue;
}
const bossPockets = tables.bots.types[botType].inventory.items.Pockets;
const bossTotal = Object.values(bossPockets).reduce((a, b) => a + b, 0);
let value = 0;
let guess = 0;
let rollChance = 0;
guess = chance / 100 * bossTotal;
value = Math.round((chance / 100) * (bossTotal + guess));
rollChance = value / (bossTotal + value);
//this.logger.debug(`[BossesHaveLegaMedals] ${botType}: ${(bossTotal + value)} --- if value: ${value} then chance is ${rollChance}`);
if (BossesHaveLegaMedals.config.debugLogging)
this.logger.debug(`[BossesHaveLegaMedals] ${botType}: Chance is ${Number(rollChance).toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 2 })}`);
bossPockets["6656560053eaaa7a23349c86"] = value;
}
}
}
exports.mod = new BossesHaveLegaMedals();
//# sourceMappingURL=mod.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "mod.js",
"sourceRoot": "",
"sources": [
"mod.ts"
],
"names": [],
"mappings": ";;;;;;AAAA,wEAA2F;AAM3F,kEAA+D;AAC/D,gDAAwB;AAGxB,MAAM,oBAAoB;IAEd,MAAM,CAAS;IAEf,MAAM,CAAC,cAAc,GAAG,oBAAS,CAAC,OAAO,CAAiB,gBAAgB,CAAC,CAAC;IAC5E,MAAM,CAAC,MAAM,GAAW,aAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,cAAc,CAAC,IAAI,CAAC,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC;IAElI,UAAU,CAAC,SAA8B;QAE5C,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAkB,iBAAiB,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,OAAO,CAAU,eAAe,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAoB,eAAe,CAAC,SAAS,EAAE,CAAC;QAE5D,IAAI,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,eAAe,CAAC;QACzD,IAAI,MAAM,IAAI,CAAC;YAAE,MAAM,GAAG,CAAC,CAAC;QAE5B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EACvC,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,IAAI,UAAU,EACtD,CAAC;gBACG,SAAS;YACb,CAAC;YACD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;YACvE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAExE,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,IAAI,UAAU,GAAG,CAAC,CAAC;YAEnB,KAAK,GAAG,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;YACzD,UAAU,GAAG,KAAK,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,CAAA;YACxC,qIAAqI;YACrI,IAAI,oBAAoB,CAAC,MAAM,CAAC,YAAY;gBAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,OAAO,eAAe,MAAM,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,SAAS,EAAC,EAAC,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAC,CAAC,EAAC,CAAC,EAAE,CAAC,CAAC;YAC5M,WAAW,CAAC,0BAA0B,CAAC,GAAG,KAAK,CAAC;QACpD,CAAC;IACL,CAAC;;AASQ,QAAA,GAAG,GAAG,IAAI,oBAAoB,EAAE,CAAC"
}

View file

@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mod = void 0;
class RefSPTFriendlyQuests {
customQuestTable = require("../db/Ref_quests.json");
postDBLoad(container) {
const databaseService = container.resolve("DatabaseService");
const tables = databaseService.getTables();
const localeTable = tables.locales.global;
const questTable = tables.templates.quests;
const refTraderID = "6617beeaa9cfa777ca915b7c";
const refBase = tables.traders[refTraderID].base;
const refQuestAssort = tables.traders[refTraderID].questassort;
//Update assort to have quest reward
refQuestAssort["success"]["66c1beaefa6e5a0c120f0d08"] = "668caeedbdb70c05d702f1b6";
questTable["66058cc1da30b620a34e6e86"] = this.customQuestTable["66058cc1da30b620a34e6e86"]; //tgh p1
questTable["66058cc208308761cf390993"] = this.customQuestTable["66058cc208308761cf390993"]; //tgh p2
questTable["66058cc5bb83da7ba474aba9"] = this.customQuestTable["66058cc5bb83da7ba474aba9"]; //tgh p3
for (const language in localeTable) {
localeTable[language]["668caeedbdb70c05d702f0fc"] = "Eliminate PMCs"; // tgh p1
localeTable[language]["662ba78e19c86d3199ae0a93"] = "Eliminate PMCs"; // tgh p2
localeTable[language]["662ba61d3ed61b6b78187b71"] = "Eliminate PMCs"; // tgh p3
}
// Change LL4 requirements for Ref to be 1.00
refBase.loyaltyLevels[3].minStanding = 1.0;
}
}
exports.mod = new RefSPTFriendlyQuests();
//# sourceMappingURL=mod.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "mod.js",
"sourceRoot": "",
"sources": [
"mod.ts"
],
"names": [],
"mappings": ";;;AAOA,MAAM,oBAAoB;IAEd,gBAAgB,GAA2B,OAAO,CAAC,uBAAuB,CAAC,CAAA;IAE5E,UAAU,CAAC,SAA8B;QAE5C,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAkB,iBAAiB,CAAC,CAAC;QAC9E,MAAM,MAAM,GAAoB,eAAe,CAAC,SAAS,EAAE,CAAC;QAC5D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAC3C,MAAM,WAAW,GAAG,0BAA0B,CAAC;QAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;QACjD,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC;QAE/D,oCAAoC;QACpC,cAAc,CAAC,SAAS,CAAC,CAAC,0BAA0B,CAAC,GAAG,0BAA0B,CAAE;QAEpF,UAAU,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,CAAC,CAAC,QAAQ;QACpG,UAAU,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,CAAC,CAAC,QAAQ;QACpG,UAAU,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,CAAC,CAAC,QAAQ;QAEpG,KAAK,MAAM,QAAQ,IAAI,WAAW,EAClC,CAAC;YACG,WAAW,CAAC,QAAQ,CAAC,CAAC,0BAA0B,CAAC,GAAG,gBAAgB,CAAA,CAAC,SAAS;YAC9E,WAAW,CAAC,QAAQ,CAAC,CAAC,0BAA0B,CAAC,GAAG,gBAAgB,CAAA,CAAC,SAAS;YAC9E,WAAW,CAAC,QAAQ,CAAC,CAAC,0BAA0B,CAAC,GAAG,gBAAgB,CAAA,CAAC,SAAS;QAClF,CAAC;QAED,6CAA6C;QAC7C,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,GAAG,CAAC;IAC/C,CAAC;CACJ;AAEY,QAAA,GAAG,GAAG,IAAI,oBAAoB,EAAE,CAAC"
}

View file

@ -0,0 +1,65 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.mod = void 0;
const jsonc_1 = require("C:/snapshot/project/node_modules/jsonc");
const path = __importStar(require("path"));
class Mod {
postDBLoad(container) {
const LocaleHelper = container.resolve("LocaleService");
const locale = LocaleHelper.getDesiredGameLocale();
const localeDB = LocaleHelper.getLocaleDb();
const fileSystem = container.resolve("FileSystemSync");
//Config Variables
const hearingColor = "#598559";
const ambientColor = "#f59542";
//load config file
const headsetConfigs = jsonc_1.jsonc.parse(fileSystem.read(path.resolve(__dirname, "../config/headset-config.json")));
//Update function
const headsetDescUpdate = (itemID, Hearing, Ambient) => {
const headsetDesc = localeDB[`${itemID} Description`];
const headsetDescNew = headsetDesc +
`\n\n <color=${hearingColor}>Rated Hearing Distance: ${Hearing} meters</color>\n` +
` <color=${ambientColor}>Ambient Noise Canceling: -${Ambient}db</color>`;
localeDB[`${itemID} Description`] = headsetDescNew;
};
// Loop and apply
for (const [itemID, hearing, ambient] of headsetConfigs) {
headsetDescUpdate(itemID, hearing, ambient);
}
}
}
exports.mod = new Mod();
//# sourceMappingURL=mod.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "mod.js",
"sourceRoot": "",
"sources": [
"mod.ts"
],
"names": [],
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,kEAA+D;AAC/D,2CAA6B;AAE7B,MAAM,GAAG;IAEE,UAAU,CAAC,SAA8B;QAC5C,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAgB,eAAe,CAAC,CAAC;QACvE,MAAM,MAAM,GAAkB,YAAY,CAAC,oBAAoB,EAAE,CAAC;QAClE,MAAM,QAAQ,GAA2B,YAAY,CAAC,WAAW,EAAE,CAAC;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAiB,gBAAgB,CAAC,CAAC;QAEvE,kBAAkB;QAClB,MAAM,YAAY,GAAG,SAAS,CAAC;QAC/B,MAAM,YAAY,GAAG,SAAS,CAAC;QAC/B,kBAAkB;QAClB,MAAM,cAAc,GAA+B,aAAK,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,+BAA+B,CAAC,CAAC,CAAC,CAAC;QAE1I,iBAAiB;QACjB,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAAE,OAAe,EAAE,OAAe,EAAQ,EAAE;YACjF,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,MAAM,cAAc,CAAC,CAAC;YACtD,MAAM,cAAc,GAAG,WAAW;gBAC9B,eAAe,YAAY,4BAA4B,OAAO,mBAAmB;gBACjF,WAAW,YAAY,8BAA8B,OAAO,YAAY,CAAC;YAC7E,QAAQ,CAAC,GAAG,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;QACvD,CAAC,CAAA;QAED,iBAAiB;QACjB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,cAAc,EAAE,CAAC;YACtD,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;IAEL,CAAC;CACJ;AAEY,QAAA,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC"
}

View file

@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assortUnlocks = void 0;
const assortUnlocks = (container) => {
const logger = container.resolve("PrimaryLogger");
const staticRouterModService = container.resolve("StaticRouterModService");
const databaseService = container.resolve("DatabaseService");
const loadAssortmentUnlocks = () => {
const traders = databaseService.getTraders();
const quests = databaseService.getQuests();
const result = {};
for (const traderId in traders) {
const trader = traders[traderId];
if (trader.questassort) {
for (const questStatus in trader.questassort) {
// Explicitly check that quest status is an expected value - some mods accidently import in such a way that adds a "default" value
if (!["started", "success", "fail"].includes(questStatus)) {
continue;
}
for (const assortId in trader.questassort[questStatus]) {
const questId = trader.questassort[questStatus][assortId];
if (!quests[questId]) {
logger.warning(`UIFixes: Trader ${traderId} questassort references unknown quest ${JSON.stringify(questId)}! Check that whatever mod added that trader and/or quest is installed correctly.`);
continue;
}
result[assortId] = quests[questId].name;
}
}
}
}
return result;
};
staticRouterModService.registerStaticRouter("UIFixesRoutes", [
{
url: "/uifixes/assortUnlocks",
action: async (url, info, sessionId, output) => {
return JSON.stringify(loadAssortmentUnlocks());
}
}
], "custom-static-ui-fixes");
};
exports.assortUnlocks = assortUnlocks;
//# sourceMappingURL=assortUnlocks.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "assortUnlocks.js",
"sourceRoot": "",
"sources": [
"assortUnlocks.ts"
],
"names": [],
"mappings": ";;;AAMO,MAAM,aAAa,GAAG,CAAC,SAA8B,EAAE,EAAE;IAC5D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAU,eAAe,CAAC,CAAC;IAC3D,MAAM,sBAAsB,GAAG,SAAS,CAAC,OAAO,CAAyB,wBAAwB,CAAC,CAAC;IACnG,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAkB,iBAAiB,CAAC,CAAC;IAE9E,MAAM,qBAAqB,GAAG,GAAG,EAAE;QAC/B,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,EAAE,CAAC;QAC3C,MAAM,MAAM,GAA2B,EAAE,CAAC;QAE1C,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;YACjC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;gBACrB,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;oBAC3C,kIAAkI;oBAClI,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;wBACxD,SAAS;oBACb,CAAC;oBAED,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;wBACrD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC;wBAE1D,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;4BACnB,MAAM,CAAC,OAAO,CACV,mBAAmB,QAAQ,yCAAyC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kFAAkF,CAChL,CAAC;4BACF,SAAS;wBACb,CAAC;wBAED,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;oBAC5C,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;IAEF,sBAAsB,CAAC,oBAAoB,CACvC,eAAe,EACf;QACI;YACI,GAAG,EAAE,wBAAwB;YAC7B,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;gBAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE,CAAC,CAAC;YACnD,CAAC;SACJ;KACJ,EACD,wBAAwB,CAC3B,CAAC;AACN,CAAC,CAAC;AAlDW,QAAA,aAAa,iBAkDxB"
}

View file

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.keepQuickBinds = void 0;
const keepQuickBinds = (container) => {
const logger = container.resolve("PrimaryLogger");
const cloner = container.resolve("RecursiveCloner");
container.afterResolution("InRaidHelper", (_, inRaidHelper) => {
const original = inRaidHelper.deleteInventory;
inRaidHelper.deleteInventory = (pmcData, sessionId) => {
// Copy the existing quickbinds
const fastPanel = cloner.clone(pmcData.Inventory.fastPanel);
// Nukes the inventory and the fastpanel
const result = original.call(inRaidHelper, pmcData, sessionId);
// Restore the quickbinds for items that still exist
try {
for (const index in fastPanel) {
if (pmcData.Inventory.items.find(i => i._id == fastPanel[index])) {
pmcData.Inventory.fastPanel[index] = fastPanel[index];
}
}
}
catch (error) {
logger.error(`UIFixes: Failed to restore quickbinds\n ${error}`);
}
return result;
};
}, { frequency: "Always" });
};
exports.keepQuickBinds = keepQuickBinds;
//# sourceMappingURL=keepQuickBinds.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "keepQuickBinds.js",
"sourceRoot": "",
"sources": [
"keepQuickBinds.ts"
],
"names": [],
"mappings": ";;;AAMO,MAAM,cAAc,GAAG,CAAC,SAA8B,EAAE,EAAE;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAU,eAAe,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAU,iBAAiB,CAAC,CAAC;IAE7D,SAAS,CAAC,eAAe,CACrB,cAAc,EACd,CAAC,CAAC,EAAE,YAA0B,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAG,YAAY,CAAC,eAAe,CAAC;QAE9C,YAAY,CAAC,eAAe,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE;YAClD,+BAA+B;YAC/B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAE5D,wCAAwC;YACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YAE/D,oDAAoD;YACpD,IAAI,CAAC;gBACD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;oBAC5B,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;wBAC/D,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;oBAC1D,CAAC;gBACL,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;YACrE,CAAC;YAED,OAAO,MAAM,CAAC;QAClB,CAAC,CAAC;IACN,CAAC,EACD,EAAE,SAAS,EAAE,QAAQ,EAAE,CAC1B,CAAC;AACN,CAAC,CAAC;AAhCW,QAAA,cAAc,kBAgCzB"
}

View file

@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.linkedSlotSearch = void 0;
const linkedSlotSearch = (container) => {
const logger = container.resolve("PrimaryLogger");
const itemHelper = container.resolve("ItemHelper");
const databaseService = container.resolve("DatabaseService");
container.afterResolution("RagfairLinkedItemService", (_, linkedItemService) => {
const original = linkedItemService.getLinkedItems;
linkedItemService.getLinkedItems = linkedSearchId => {
const [tpl, slotName] = linkedSearchId.split(":", 2);
if (slotName) {
logger.info(`UIFixes: Finding items for specific slot ${tpl}:${slotName}`);
const allItems = databaseService.getItems();
const resultSet = getSpecificFilter(allItems[tpl], slotName);
// Default Inventory, for equipment slots
if (tpl === "55d7217a4bdc2d86028b456d") {
const categories = [...resultSet];
const items = Object.keys(allItems).filter(tpl => itemHelper.isOfBaseclasses(tpl, categories));
// Send the categories along too, some of them might actually be items
return new Set(items.concat(categories));
}
return resultSet;
}
return original.call(linkedItemService, tpl);
};
}, { frequency: "Always" });
};
exports.linkedSlotSearch = linkedSlotSearch;
const getSpecificFilter = (item, slotName) => {
const results = new Set();
// For whatever reason, all chamber slots have the name "patron_in_weapon"
const groupName = slotName === "patron_in_weapon" ? "Chambers" : "Slots";
const group = item._props[groupName] ?? [];
const sub = group.find(slot => slot._name === slotName);
for (const filter of sub?._props?.filters ?? []) {
for (const f of filter.Filter) {
results.add(f);
}
}
return results;
};
//# sourceMappingURL=linkedSlotSearch.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "linkedSlotSearch.js",
"sourceRoot": "",
"sources": [
"linkedSlotSearch.ts"
],
"names": [],
"mappings": ";;;AAQO,MAAM,gBAAgB,GAAG,CAAC,SAA8B,EAAE,EAAE;IAC/D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAU,eAAe,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAa,YAAY,CAAC,CAAC;IAC/D,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAkB,iBAAiB,CAAC,CAAC;IAE9E,SAAS,CAAC,eAAe,CACrB,0BAA0B,EAC1B,CAAC,CAAC,EAAE,iBAA2C,EAAE,EAAE;QAC/C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,cAAc,CAAC;QAElD,iBAAiB,CAAC,cAAc,GAAG,cAAc,CAAC,EAAE;YAChD,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAErD,IAAI,QAAQ,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,CAAC,4CAA4C,GAAG,IAAI,QAAQ,EAAE,CAAC,CAAC;gBAC3E,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC;gBAC5C,MAAM,SAAS,GAAG,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;gBAE7D,yCAAyC;gBACzC,IAAI,GAAG,KAAK,0BAA0B,EAAE,CAAC;oBACrC,MAAM,UAAU,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;oBAClC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;oBAE/F,sEAAsE;oBACtE,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC7C,CAAC;gBAED,OAAO,SAAS,CAAC;YACrB,CAAC;YAED,OAAO,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC,CAAC;IACN,CAAC,EACD,EAAE,SAAS,EAAE,QAAQ,EAAE,CAC1B,CAAC;AACN,CAAC,CAAC;AAnCW,QAAA,gBAAgB,oBAmC3B;AAEF,MAAM,iBAAiB,GAAG,CAAC,IAAmB,EAAE,QAAgB,EAAe,EAAE;IAC7E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,0EAA0E;IAC1E,MAAM,SAAS,GAAG,QAAQ,KAAK,kBAAkB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAE3C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;IACxD,KAAK,MAAM,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC"
}

View file

@ -0,0 +1,27 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mod = void 0;
const assortUnlocks_1 = require("./assortUnlocks");
const keepQuickBinds_1 = require("./keepQuickBinds");
const linkedSlotSearch_1 = require("./linkedSlotSearch");
const putToolsBack_1 = require("./putToolsBack");
const config_json_1 = __importDefault(require("../config/config.json"));
class UIFixes {
preSptLoad(container) {
// Keep quickbinds for items that aren't actually lost on death
(0, keepQuickBinds_1.keepQuickBinds)(container);
// Better tool return - starting production
if (config_json_1.default.putToolsBack) {
(0, putToolsBack_1.putToolsBack)(container);
}
// Slot-specific linked search
(0, linkedSlotSearch_1.linkedSlotSearch)(container);
// Show unlocking quest on locked offers
(0, assortUnlocks_1.assortUnlocks)(container);
}
}
exports.mod = new UIFixes();
//# sourceMappingURL=mod.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "mod.js",
"sourceRoot": "",
"sources": [
"mod.ts"
],
"names": [],
"mappings": ";;;;;;AAIA,mDAAgD;AAChD,qDAAkD;AAClD,yDAAsD;AACtD,iDAA8C;AAE9C,wEAA2C;AAE3C,MAAM,OAAO;IACF,UAAU,CAAC,SAA8B;QAC5C,+DAA+D;QAC/D,IAAA,+BAAc,EAAC,SAAS,CAAC,CAAC;QAE1B,2CAA2C;QAC3C,IAAI,qBAAM,CAAC,YAAY,EAAE,CAAC;YACtB,IAAA,2BAAY,EAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;QAED,8BAA8B;QAC9B,IAAA,mCAAgB,EAAC,SAAS,CAAC,CAAC;QAE5B,wCAAwC;QACxC,IAAA,6BAAa,EAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;CACJ;AAEY,QAAA,GAAG,GAAG,IAAI,OAAO,EAAE,CAAC"
}

View file

@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.putToolsBack = void 0;
const returnToProperty = "uifixes.returnTo";
const putToolsBack = (container) => {
const logger = container.resolve("PrimaryLogger");
const cloner = container.resolve("RecursiveCloner");
const itemHelper = container.resolve("ItemHelper");
container.afterResolution("HideoutHelper", (_, hideoutHelper) => {
const original = hideoutHelper.registerProduction;
hideoutHelper.registerProduction = (pmcData, body, sessionID) => {
const result = original.call(hideoutHelper, pmcData, body, sessionID);
// The items haven't been deleted yet, augment the list with their parentId
try {
const bodyAsSingle = body;
if (bodyAsSingle && bodyAsSingle.tools?.length > 0) {
const requestTools = bodyAsSingle.tools;
const tools = pmcData.Hideout.Production[body.recipeId].sptRequiredTools;
for (let i = 0; i < tools.length; i++) {
const originalTool = pmcData.Inventory.items.find(x => x._id === requestTools[i].id);
// If the tool is in the stash itself, skip it. Same check as InventoryHelper.isItemInStash
if (originalTool.parentId === pmcData.Inventory.stash &&
originalTool.slotId === "hideout") {
continue;
}
tools[i][returnToProperty] = [originalTool.parentId, originalTool.slotId];
}
}
}
catch (error) {
logger.error(`UIFixes: Failed to save tool origin\n ${error}`);
}
return result;
};
}, { frequency: "Always" });
// Better tool return - returning the tool
container.afterResolution("InventoryHelper", (_, inventoryHelper) => {
const original = inventoryHelper.addItemToStash;
inventoryHelper.addItemToStash = (sessionId, request, pmcData, output) => {
const itemWithModsToAddClone = cloner.clone(request.itemWithModsToAdd);
// If a tool marked with uifixes is there, try to return it to its original container
const tool = itemWithModsToAddClone[0];
if (tool[returnToProperty]) {
try {
const [containerId, slotId] = tool[returnToProperty];
// Clean the item
delete tool[returnToProperty];
const [foundContainerFS2D, foundSlotId] = findGridFS2DForItems(inventoryHelper, containerId, slotId, itemWithModsToAddClone, pmcData);
if (foundContainerFS2D) {
// At this point everything should succeed
inventoryHelper.placeItemInContainer(foundContainerFS2D, itemWithModsToAddClone, containerId, foundSlotId);
// protected function, bypass typescript
inventoryHelper["setFindInRaidStatusForItem"](itemWithModsToAddClone, request.foundInRaid);
// Add item + mods to output and profile inventory
output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone);
pmcData.Inventory.items.push(...itemWithModsToAddClone);
logger.debug(`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${itemWithModsToAddClone[0]._tpl} with: ${itemWithModsToAddClone.length - 1} mods to ${containerId}`);
return;
}
}
catch (error) {
logger.error(`UIFixes: Encounted an error trying to put tool back.\n ${error}`);
}
logger.info("UIFixes: Unable to put tool back in its original container, returning it to stash.");
}
return original.call(inventoryHelper, sessionId, request, pmcData, output);
};
}, { frequency: "Always" });
function findGridFS2DForItems(inventoryHelper, containerId, startingGrid, items, pmcData) {
const container = pmcData.Inventory.items.find(x => x._id === containerId);
if (!container) {
return;
}
const [foundTemplate, containerTemplate] = itemHelper.getItem(container._tpl);
if (!foundTemplate || !containerTemplate) {
return;
}
let originalGridIndex = containerTemplate._props.Grids.findIndex(g => g._name === startingGrid);
if (originalGridIndex < 0) {
originalGridIndex = 0;
}
// Loop through grids, starting from the original grid
for (let gridIndex = originalGridIndex; gridIndex < containerTemplate._props.Grids.length + originalGridIndex; gridIndex++) {
const grid = containerTemplate._props.Grids[gridIndex % containerTemplate._props.Grids.length];
const gridItems = pmcData.Inventory.items.filter(x => x.parentId === containerId && x.slotId === grid._name);
const containerFS2D = inventoryHelper.getContainerMap(grid._props.cellsH, grid._props.cellsV, gridItems, containerId);
// will change the array so clone it
if (inventoryHelper.canPlaceItemInContainer(cloner.clone(containerFS2D), items)) {
return [containerFS2D, grid._name];
}
}
}
};
exports.putToolsBack = putToolsBack;
//# sourceMappingURL=putToolsBack.js.map

View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "putToolsBack.js",
"sourceRoot": "",
"sources": [
"putToolsBack.ts"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2022 Virtual (https://hub.sp-tarkov.com/user/26520-virtual/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This license refers to the files included within the folder that it resides in from the original download. Any other modification or assets surrounding the use of said files created and maintained by another entity are to be processed under a seperate license and are not the product from this copyright individual.

View file

@ -0,0 +1,5 @@
{
"enableLogging": true,
"enableDebugLogging": false,
"ignoreSideExclusive": false
}

View file

@ -0,0 +1 @@
Locales files need to be placed into the correct folder so they are loaded to the correct language setting. Locales are loaded from the server, so if a locale folder doesn't exist, you can create it.

View file

@ -0,0 +1,11 @@
Here is where you place all .json files that contain the contents of the quests. They should have the same format as quests.json in aki-data/server/database/templates/. There is no required name and you can call them whatever you like.
The only additional properties that can be added are
"startMonth": 12,
"startDay": 1,
"endDay": 31
This allows you to create seasonal quests (The values here for example would only have the task available during the month of december.)
Quests can be accross multiple files and you can have each json with 20 quests for example.

View file

@ -0,0 +1,23 @@
{
"name": "Virtuals Custom Quest Loader",
"version":"2.0.4",
"main": "src/mod.js",
"license": "MIT",
"author": "Virtual",
"sptVersion": "3.11.x",
"scripts": {
"setup": "npm i",
"build": "node ./packageBuild.ts"
},
"devDependencies": {
"@types/node": "16.11.62",
"@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1",
"bestzip": "2.2.1",
"eslint": "8.24.0",
"fs-extra": "10.1.0",
"glob": "8.0.3",
"tsyringe": "4.7.0",
"typescript": "4.8.4"
}
}

View file

@ -0,0 +1,2 @@
All images need to be 314 x 177 AND need to be either a .png or .jpg.
After placing them in this folder, you can select the image in the database/quests/ file and change it to the name of the image you placed in this folder.

View file

@ -0,0 +1,227 @@
import { DependencyContainer} from "tsyringe";
import { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod";
import { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"
import type {StaticRouterModService} from "@spt/services/mod/staticRouter/StaticRouterModService";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { ImageRouter } from "@spt/routers/ImageRouter";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import * as path from "path";
const fs = require('fs');
const modPath = path.normalize(path.join(__dirname, '..'));
class VCQL implements IPostDBLoadMod, IPreSptLoadMod {
private enableLogging;
private enableDebugLogging;
private ignoreSideExclusive;
private zones;
public preSptLoad(container: DependencyContainer): void {
const staticRouterModService: StaticRouterModService = container.resolve<StaticRouterModService>("StaticRouterModService")
const logger = container.resolve<ILogger>("WinstonLogger")
this.loadZones()
staticRouterModService.registerStaticRouter(
"vcql-get-zones",
[
{
url: "/vcql/zones/get",
action: (url, info, sessionId, output) =>
{
if (this.enableDebugLogging) logger.success("[VCQL-DEBUG] Zone router hit.")
return JSON.stringify(this.zones);
}
}
],
"vcql-get"
)
}
public postDBLoad(container: DependencyContainer): void
{
const database = container.resolve<DatabaseServer>("DatabaseServer").getTables()
const imageRouter = container.resolve< ImageRouter >("ImageRouter")
const logger = container.resolve<ILogger>("WinstonLogger")
const config = container.resolve<ConfigServer>("ConfigServer").getConfig(ConfigTypes.QUEST)
this.importConfig()
this.importQuests(database, logger, config)
this.importAssorts(database, logger)
this.importLocales(database)
this.routeImages(imageRouter, logger)
}
public loadFiles(dirPath, extName, cb) {
if (!fs.existsSync(dirPath)) return
const dir = fs.readdirSync(dirPath, { withFileTypes: true })
dir.forEach(item => {
const itemPath = path.normalize(`${dirPath}/${item.name}`)
if (item.isDirectory()) this.loadFiles(itemPath, extName, cb)
else if (extName.includes(path.extname(item.name))) cb(itemPath)
});
}
public loadZones() {
let zones = []
this.loadFiles(`${modPath}/database/zones/`, [".json"], function(filePath) {
const zoneFile = require(filePath)
if (Object.keys(zoneFile).length > 0)
zones.push(... zoneFile)
})
this.zones = zones
}
public importConfig() {
let config = require(`${modPath}/database/config/config.json`)
this.enableLogging = config.enableLogging
this.enableDebugLogging = config.enableDebugLogging
this.ignoreSideExclusive = config.ignoreSideExclusive
}
public importQuests(database, logger, config) {
let questCount = 0
let prunedCount = 0
let debugLogging = this.enableDebugLogging
let ignoreSideExclusive = this.ignoreSideExclusive
this.loadFiles(`${modPath}/database/quests/`, [".json"], function(filePath) {
const item = require(filePath)
if (Object.keys(item).length < 1) return
for (const quest in item) {
// Date check
if (item[quest].startMonth && item[quest].startMonth > 0) {
let currentDate = new Date()
let questStartDate = new Date(currentDate.getFullYear(), item[quest].startMonth - 1, item[quest].startDay)
let questEndDate = new Date(currentDate.getFullYear(), item[quest].endMonth - 1, item[quest].endDay)
if (currentDate < questStartDate || currentDate > questEndDate) {
if (debugLogging) logger.success(`[VCQL-DEBUG] Removing quest ${item[quest]._id} because it is outside the date range`)
prunedCount++
continue
}
}
// Cleanup
delete item[quest].startMonth; delete item[quest].endMonth; delete item[quest].startDay; delete item[quest].endDay
// Push
if (item[quest].sideExclusive) {
if (item[quest].sideExclusive == "Usec" && !ignoreSideExclusive) {
config.usecOnlyQuests.push(quest)
if (debugLogging) logger.success(`[VCQL-DEBUG] Adding quest ${item[quest]._id} as a Usec exclusive quest.`)
}
if (item[quest].sideExclusive == "Bear" && !ignoreSideExclusive) {
config.bearOnlyQuests.push(quest)
if (debugLogging) logger.success(`[VCQL-DEBUG] Adding quest ${item[quest]._id} as a Bear exclusive quest.`)
}
delete item[quest].sideExclusive
}
database.templates.quests[quest] = item[quest]
questCount++
}
})
if (this.enableLogging) {
logger.success(`[VCQL] Loaded ${questCount} custom quests.`)
logger.success(`[VCQL] ${prunedCount} custom quests were pruned due to date settings.`)
}
}
public importAssorts(database, logger) {
let debugLogging = this.enableDebugLogging
this.loadFiles(`${modPath}/database/assorts/`, [".json"], function(filePath) {
const assorts = require(filePath)
if (assorts.items == undefined || assorts.traderID == undefined || assorts.barter_scheme == undefined || assorts.loyal_level_items == undefined) return
let traderID = assorts.traderID
let databaseTrader = database.traders[traderID]
// If assort is for a custom trader, ensure that they have the appropriate assort tables
if (databaseTrader.questassort == undefined) databaseTrader.questassort = {
started: {},
success: {},
fail: {}
}
if (databaseTrader.assort == undefined) databaseTrader.assort = {
items: [],
barter_scheme: {},
loyal_level_items: {}
}
// Add barter scheme, loyalty level, quest assort and general assort, where applicable
for (const assort of assorts.items) {
if (assorts.barter_scheme[assort._id] != undefined) {
databaseTrader.assort.barter_scheme[assort._id] = assorts.barter_scheme[assort._id]
} else {
if (assort.parentId == "hideout") logger.error(`[VCQL] Parent assort ${assort._id} has no associated barter scheme.`)
}
if (assorts.loyal_level_items[assort._id] != undefined) {
databaseTrader.assort.loyal_level_items[assort._id] = assorts.loyal_level_items[assort._id]
} else {
if (assort.parentId == "hideout") logger.error(`[VCQL] Parent assort ${assort._id} has no associated loyalty level`)
}
if (assort.unlockedOn != undefined && assort.questID != undefined) {
databaseTrader.questassort[assort.unlockedOn][assort._id] = assort.questID
delete assort.unlockedOn
delete assort.questID
}
databaseTrader.assort.items.push(assort)
if (debugLogging) logger.success(`[VCQL-DEBUG] Adding assort ${assort._id} to the trader ${traderID}`)
}
})
}
public importLocales(database) {
const serverLocales = ['ch','cz','en','es','es-mx','fr','ge','hu','it','jp','kr','pl','po','ru','sk','tu']
const addedLocales = {}
for (const locale of serverLocales) {
this.loadFiles(`${modPath}/database/locales/${locale}`, [".json"], function(filePath) {
const localeFile = require(filePath)
if (Object.keys(localeFile).length < 1) return
for (const currentItem in localeFile) {
database.locales.global[locale][currentItem] = localeFile[currentItem]
if (!Object.keys(addedLocales).includes(locale)) addedLocales[locale] = {}
addedLocales[locale][currentItem] = localeFile[currentItem]
}
})
}
// placeholders
for (const locale of serverLocales) {
if (locale == "en") continue
for (const englishItem in addedLocales["en"]) {
if (locale in addedLocales) {
if (englishItem in addedLocales[locale]) continue
}
if (database.locales.global[locale] != undefined) database.locales.global[locale][englishItem] = addedLocales["en"][englishItem]
}
}
}
public routeImages(imageRouter, logger) {
let questImageCount = 0
let debugLogging = this.enableDebugLogging
this.loadFiles(`${modPath}/res/quests/`, [".png", ".jpg"], function(filePath) {
imageRouter.addRoute(`/files/quest/icon/${path.basename(filePath, path.extname(filePath))}`, filePath)
if (debugLogging) logger.success(`[VCQL-DEBUG] Adding quest image ${path.basename(filePath)}`)
questImageCount++
})
let traderImageCount = 0
this.loadFiles(`${modPath}/res/traders/`, [".png", ".jpg"], function(filePath) {
imageRouter.addRoute(`/files/trader/avatar/${path.basename(filePath, path.extname(filePath))}`, filePath)
if (debugLogging) logger.success(`[VCQL-DEBUG] Adding quest image ${path.basename(filePath)}`)
traderImageCount++
})
if (this.enableLogging) {
logger.success(`[VCQL] Loaded ${questImageCount} custom quest images.`)
logger.success(`[VCQL] Loaded ${traderImageCount} custom trader images.`)
}
}
}
module.exports = { mod: new VCQL() }