feat(scorpion)
This commit is contained in:
parent
8827f6a6b0
commit
38316d15a5
23 changed files with 52336 additions and 0 deletions
21
user/mods/acidphantasm-scorpion/LICENSE
Normal file
21
user/mods/acidphantasm-scorpion/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 acidphantasm
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"ThisIsAnExample": [
|
||||
"Replace_This_With_Modded_Weapon_TPL_ID",
|
||||
"Comma_Separate_Each_Weapon__TPL_ID",
|
||||
"MODDED_WEAPON_ID_EXAMPLE_TPL",
|
||||
"MODDED_WEAPON_ID_EXAMPLE_TPL2"
|
||||
],
|
||||
"AssaultRifles" : [],
|
||||
"SubmachineGuns" : [],
|
||||
"Snipers" : [],
|
||||
"Marksman" : [],
|
||||
"Shotguns" : [],
|
||||
"Pistols" : [],
|
||||
"LargeMachineGuns" : [],
|
||||
"Carbines" : [],
|
||||
"Melee" : [],
|
||||
"Explosives" : []
|
||||
|
||||
}
|
||||
20
user/mods/acidphantasm-scorpion/config/config.jsonc
Normal file
20
user/mods/acidphantasm-scorpion/config/config.jsonc
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"priceMultiplier": 1.25, // Multiplies the prices by the number set here.
|
||||
|
||||
"traderRefreshMin": 1800, // Minimum Refresh Timer for the trader (in seconds)
|
||||
"traderRefreshMax": 3600, // Maximum Refresh Timer for the trader (in seconds)
|
||||
|
||||
"addTraderToFlea": true, // Enable trader to have offers on flea
|
||||
|
||||
"unlimitedStock": false, // Whether you want unlimited available stock - this overrides randomizeStockAvailable
|
||||
"unlimitedBuyRestriction": false, // Whether you want unlimited item buys - this overrides randomizeBuyRestriction
|
||||
"removeLoyaltyRestriction": false, // Removes Quest Unlocks & Loyalty Level Requirements - Entire assort available at level 1
|
||||
|
||||
"randomizeBuyRestriction": false, // Randomizes the amount of items in stock that you can buy per reset
|
||||
"randomizeStockAvailable": false, // Randomizes the amount of items in stock per reset
|
||||
"outOfStockChance": 25, // Chance for an item to be out of stock per reset
|
||||
|
||||
"eventQuestsAlwaysActive" : false, // Change to true to make event quests always active
|
||||
|
||||
"debugLogging": false // Enables server console logging for above settings
|
||||
}
|
||||
3950
user/mods/acidphantasm-scorpion/db/assort.json
Normal file
3950
user/mods/acidphantasm-scorpion/db/assort.json
Normal file
File diff suppressed because it is too large
Load diff
143
user/mods/acidphantasm-scorpion/db/base.json
Normal file
143
user/mods/acidphantasm-scorpion/db/base.json
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
{
|
||||
"_id": "6688d464bc40c867f60e7d7e",
|
||||
"working": true,
|
||||
"availableInRaid": false,
|
||||
"items_buy": {
|
||||
"category": [
|
||||
"5422acb9af1c889c16000029",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"543be6564bdc2df4348b4568",
|
||||
"543be5664bdc2dd4348b4569",
|
||||
"5448e54d4bdc2dcc718b4568",
|
||||
"5448fe124bdc2da5018b4567",
|
||||
"5448e5284bdc2dcb718b4567",
|
||||
"5448e53e4bdc2d60728b4567",
|
||||
"543be5f84bdc2dd4348b456a",
|
||||
"543be6674bdc2df1348b4569",
|
||||
"5448e8d04bdc2ddf718b4569",
|
||||
"5448e8d64bdc2dce718b4568"
|
||||
],
|
||||
"id_list": []
|
||||
},
|
||||
"items_buy_prohibited": {
|
||||
"category": [],
|
||||
"id_list": [
|
||||
"62e910aaf957f2915e0a5e36",
|
||||
"64d0b40fbe2eed70e254e2d4"
|
||||
]
|
||||
},
|
||||
"customization_seller": false,
|
||||
"name": "Scorpion",
|
||||
"surname": "",
|
||||
"nickname": "Scorpion",
|
||||
"location": "Federal State Reserve Agency Base",
|
||||
"avatar": "/files/trader/avatar/6688d464bc40c867f60e7d7e.jpg",
|
||||
"balance_rub": 5000000,
|
||||
"balance_dol": 23000,
|
||||
"balance_eur": 20000,
|
||||
"unlockedByDefault": true,
|
||||
"discount": 0,
|
||||
"discount_end": 0,
|
||||
"buyer_up": true,
|
||||
"currency": "RUB",
|
||||
"nextResupply": 1615141448,
|
||||
"repair": {
|
||||
"availability": true,
|
||||
"quality": "0.35",
|
||||
"excluded_id_list": [],
|
||||
"excluded_category": [
|
||||
"5448e54d4bdc2dcc718b4568",
|
||||
"57bef4c42459772e8d35a53b",
|
||||
"543be5f84bdc2dd4348b456a"
|
||||
],
|
||||
"currency": "5449016a4bdc2d6f028b456f",
|
||||
"currency_coefficient": 1,
|
||||
"price_rate": 0
|
||||
},
|
||||
"insurance": {
|
||||
"availability": false,
|
||||
"min_payment": 0,
|
||||
"min_return_hour": 0,
|
||||
"max_return_hour": 0,
|
||||
"max_storage_time": 99,
|
||||
"excluded_category": []
|
||||
},
|
||||
"gridHeight": 150,
|
||||
"loyaltyLevels": [{
|
||||
"minLevel": 1,
|
||||
"minSalesSum": 0,
|
||||
"minStanding": 0.00,
|
||||
"buy_price_coef": 54,
|
||||
"repair_price_coef": 500,
|
||||
"insurance_price_coef": 1,
|
||||
"exchange_price_coef": 0,
|
||||
"heal_price_coef": 0
|
||||
},
|
||||
{
|
||||
"minLevel": 15,
|
||||
"minSalesSum": 500000,
|
||||
"minStanding": 0.30,
|
||||
"buy_price_coef": 50,
|
||||
"repair_price_coef": 480,
|
||||
"insurance_price_coef": 1,
|
||||
"exchange_price_coef": 0,
|
||||
"heal_price_coef": 0
|
||||
},
|
||||
{
|
||||
"minLevel": 27,
|
||||
"minSalesSum": 1500000,
|
||||
"minStanding": 0.50,
|
||||
"buy_price_coef": 45,
|
||||
"repair_price_coef": 430,
|
||||
"insurance_price_coef": 1,
|
||||
"exchange_price_coef": 0,
|
||||
"heal_price_coef": 0
|
||||
},
|
||||
{
|
||||
"minLevel": 40,
|
||||
"minSalesSum": 3000000,
|
||||
"minStanding": 0.90,
|
||||
"buy_price_coef": 39,
|
||||
"repair_price_coef": 400,
|
||||
"insurance_price_coef": 1,
|
||||
"exchange_price_coef": 0,
|
||||
"heal_price_coef": 0
|
||||
}
|
||||
],
|
||||
"sell_category": [
|
||||
"82e7fac0b7495d72d4083356",
|
||||
"ac705d3440c1407645e33579",
|
||||
"dc97aee367144dc03389405d",
|
||||
"7ffcc96aa06c7e90940330c5",
|
||||
"e8f46e3ad74b9d862121f9dc",
|
||||
"5b47574386f77428ca22b33e",
|
||||
"5b47574386f77428ca22b33f",
|
||||
"5b5f78dc86f77409407a7f8e",
|
||||
"5b47574386f77428ca22b346",
|
||||
"5b47574386f77428ca22b340",
|
||||
"5b47574386f77428ca22b344",
|
||||
"5b47574386f77428ca22b342",
|
||||
"5b47574386f77428ca22b341",
|
||||
"5b47574386f77428ca22b345",
|
||||
"5b47574386f77428ca22b343",
|
||||
"5b5f71b386f774093f2ecf11",
|
||||
"5b5f71c186f77409407a7ec0",
|
||||
"5b5f71de86f774093f2ecf13",
|
||||
"5b5f724186f77447ed5636ad",
|
||||
"5b5f736886f774094242f193",
|
||||
"5b5f73ec86f774093e6cb4fd",
|
||||
"5b5f74cc86f77447ec5d770a",
|
||||
"5b5f750686f774093e6cb503",
|
||||
"5b5f751486f77447ec5d770c",
|
||||
"5b5f752e86f774093e6cb505",
|
||||
"5b5f754a86f774094242f19b",
|
||||
"5b5f755f86f77447ec5d770e",
|
||||
"5b5f757486f774093e6cb507",
|
||||
"5b5f75b986f77447ec5d7710",
|
||||
"5b5f75c686f774094242f19f",
|
||||
"5b5f75e486f77447ec5d7712",
|
||||
"5b5f760586f774093e6cb509",
|
||||
"5b5f761f86f774094242f1a1",
|
||||
"5b5f764186f77447ec5d7714"
|
||||
]
|
||||
}
|
||||
85
user/mods/acidphantasm-scorpion/db/production.json
Normal file
85
user/mods/acidphantasm-scorpion/db/production.json
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
[
|
||||
{
|
||||
"_id": "6635a60afe3ddd40c508c666",
|
||||
"areaType": 11,
|
||||
"requirements": [
|
||||
{
|
||||
"areaType": 11,
|
||||
"requiredLevel": 1,
|
||||
"type": "Area"
|
||||
},
|
||||
{
|
||||
"templateId": "590c392f86f77444754deb29",
|
||||
"count": 3,
|
||||
"isFunctional": false,
|
||||
"isEncoded": false,
|
||||
"type": "Item"
|
||||
},
|
||||
{
|
||||
"templateId": "590a3b0486f7743954552bdb",
|
||||
"count": 10,
|
||||
"isFunctional": false,
|
||||
"isEncoded": false,
|
||||
"type": "Item"
|
||||
},
|
||||
{
|
||||
"questId": "66885a3d0221bbe5f306b6df",
|
||||
"type": "QuestComplete"
|
||||
}
|
||||
],
|
||||
"productionTime": 86400,
|
||||
"needFuelForAllProductionTime": false,
|
||||
"locked": true,
|
||||
"endProduct": "5c05300686f7746dce784e5d",
|
||||
"continuous": false,
|
||||
"count": 1,
|
||||
"productionLimitCount": 0,
|
||||
"isEncoded": false,
|
||||
"isCodeProduction": false
|
||||
},
|
||||
{
|
||||
"_id": "6635a60afe3ddd40c508c667",
|
||||
"areaType": 11,
|
||||
"requirements": [
|
||||
{
|
||||
"areaType": 11,
|
||||
"requiredLevel": 1,
|
||||
"type": "Area"
|
||||
},
|
||||
{
|
||||
"templateId": "590a3b0486f7743954552bdb",
|
||||
"count": 10,
|
||||
"isFunctional": false,
|
||||
"isEncoded": false,
|
||||
"type": "Item"
|
||||
},
|
||||
{
|
||||
"templateId": "5c05308086f7746b2101e90b",
|
||||
"count": 3,
|
||||
"isFunctional": false,
|
||||
"isEncoded": false,
|
||||
"type": "Item"
|
||||
},
|
||||
{
|
||||
"templateId": "61bf7c024770ee6f9c6b8b53",
|
||||
"count": 3,
|
||||
"isFunctional": false,
|
||||
"isEncoded": false,
|
||||
"type": "Item"
|
||||
},
|
||||
{
|
||||
"questId": "66885a3d0221bbe5f306b6e9",
|
||||
"type": "QuestComplete"
|
||||
}
|
||||
],
|
||||
"productionTime": 86400,
|
||||
"needFuelForAllProductionTime": false,
|
||||
"locked": true,
|
||||
"endProduct": "5c0530ee86f774697952d952",
|
||||
"continuous": false,
|
||||
"count": 1,
|
||||
"productionLimitCount": 0,
|
||||
"isEncoded": false,
|
||||
"isCodeProduction": false
|
||||
}
|
||||
]
|
||||
52
user/mods/acidphantasm-scorpion/db/questassort.json
Normal file
52
user/mods/acidphantasm-scorpion/db/questassort.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"started": {
|
||||
"661f33776b5151c024011d07": "668858720221bbe5f306b331"
|
||||
},
|
||||
"success": {
|
||||
"661f33776b5151c024011d02": "668858720221bbe5f306b2ea",
|
||||
"661f33776b5151c024011d03": "668858720221bbe5f306b2f7",
|
||||
"661f33776b5151c024011d06": "668858720221bbe5f306b30c",
|
||||
"6635a60afe3ddd40c508c545": "668858720221bbe5f306b331",
|
||||
"662344f0ca75908fcd0f97d2": "668858720221bbe5f306b352",
|
||||
"662344feca75908fcd0f981c": "668858720221bbe5f306b35f",
|
||||
"662344feca75908fcd0f982a": "668858720221bbe5f306b35f",
|
||||
"6635a60afe3ddd40c508c544": "668858720221bbe5f306b35f",
|
||||
"6635a60afe3ddd40c508c548": "668858720221bbe5f306b388",
|
||||
"6635a60afe3ddd40c508c549": "668858720221bbe5f306b393",
|
||||
"6635a60afe3ddd40c508c567": "668858720221bbe5f306b393",
|
||||
"661f33776b5151c024011d04": "668858720221bbe5f306b3a4",
|
||||
"661f33776b5151c024011d08": "668858720221bbe5f306b3d1",
|
||||
"661f33776b5151c024011d09": "668858720221bbe5f306b3e4",
|
||||
"662344f0ca75908fcd0f97ce": "668858720221bbe5f306b403",
|
||||
"662344f0ca75908fcd0f97cf": "668858720221bbe5f306b414",
|
||||
"662344f0ca75908fcd0f97d0": "668858720221bbe5f306b414",
|
||||
"662344feca75908fcd0f981a": "668858720221bbe5f306b43c",
|
||||
"6635a60afe3ddd40c508c572": "668858720221bbe5f306b534",
|
||||
"6635a60afe3ddd40c508c586": "668858720221bbe5f306b598",
|
||||
"6635a60afe3ddd40c508c587": "668858720221bbe5f306b5a0",
|
||||
"6635a60afe3ddd40c508c588": "668858720221bbe5f306b5ac",
|
||||
"6635a60afe3ddd40c508c589": "668858720221bbe5f306b5b9",
|
||||
"6635a60afe3ddd40c508c5b8": "668858720221bbe5f306b607",
|
||||
"6635a60afe3ddd40c508c5d3": "66885a3d0221bbe5f306b664",
|
||||
"662344feca75908fcd0f985c": "66885a3d0221bbe5f306b711",
|
||||
"6635a60afe3ddd40c508c58c": "66885a3d0221bbe5f306b732",
|
||||
"6635a60afe3ddd40c508c53b": "66885a3d0221bbe5f306b767",
|
||||
"6635a60afe3ddd40c508c58d": "66885a3d0221bbe5f306b788",
|
||||
"6635a60afe3ddd40c508c4d0": "66885a3d0221bbe5f306b7be",
|
||||
"6635a60afe3ddd40c508c58e": "66885a3d0221bbe5f306b7df",
|
||||
"6635a60afe3ddd40c508c4f2": "66885a3d0221bbe5f306b814",
|
||||
"6635a60afe3ddd40c508c58f": "66885a3d0221bbe5f306b835",
|
||||
"6635a60afe3ddd40c508c53c": "66885a3d0221bbe5f306b86b",
|
||||
"6635a60afe3ddd40c508c590": "66885a3d0221bbe5f306b88c",
|
||||
"6635a60afe3ddd40c508c53d": "66885a3d0221bbe5f306b8c1",
|
||||
"6635a60afe3ddd40c508c591": "66885a3d0221bbe5f306b8e2",
|
||||
"6635a60afe3ddd40c508c53e": "66885a3d0221bbe5f306b916",
|
||||
"6635a60afe3ddd40c508c592": "66885a3d0221bbe5f306b937",
|
||||
"6635a60afe3ddd40c508c53f": "66885a3d0221bbe5f306b96c",
|
||||
"6635a60afe3ddd40c508c593": "66885a3d0221bbe5f306b98d",
|
||||
"6635a60afe3ddd40c508c5f7": "66885a3d0221bbe5f306b9cf",
|
||||
"6635a60afe3ddd40c508c5f8": "66885a3d0221bbe5f306b9cf",
|
||||
"6635a60afe3ddd40c508c5f9": "66885a3d0221bbe5f306b9cf"
|
||||
},
|
||||
"fail": {}
|
||||
}
|
||||
34
user/mods/acidphantasm-scorpion/package.json
Normal file
34
user/mods/acidphantasm-scorpion/package.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "Scorpion",
|
||||
"version": "0.13.1",
|
||||
"main": "src/mod.js",
|
||||
"license": "MIT",
|
||||
"author": "acidphantasm",
|
||||
"sptVersion": "~3.11",
|
||||
"loadBefore": [
|
||||
"zzDrakiaXYZ-LiveFleaPrices"
|
||||
],
|
||||
"loadAfter": [
|
||||
"Virtual's Custom Quest Loader"
|
||||
],
|
||||
"incompatibilities": [],
|
||||
"contributors": [],
|
||||
"scripts": {
|
||||
"setup": "npm i",
|
||||
"build": "node ./build.mjs",
|
||||
"buildinfo": "node ./build.mjs --verbose"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.10.5",
|
||||
"@typescript-eslint/eslint-plugin": "7.2",
|
||||
"@typescript-eslint/parser": "7.2",
|
||||
"archiver": "^6.0",
|
||||
"eslint": "8.57",
|
||||
"fs-extra": "11.2",
|
||||
"ignore": "^5.2",
|
||||
"tsyringe": "4.8.0",
|
||||
"typescript": "5.7.3",
|
||||
"winston": "3.17.0",
|
||||
"jsonc": "2.0.0"
|
||||
}
|
||||
}
|
||||
BIN
user/mods/acidphantasm-scorpion/res/6688d464bc40c867f60e7d7e.jpg
Normal file
BIN
user/mods/acidphantasm-scorpion/res/6688d464bc40c867f60e7d7e.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
609
user/mods/acidphantasm-scorpion/src/mod.ts
Normal file
609
user/mods/acidphantasm-scorpion/src/mod.ts
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable @typescript-eslint/brace-style */
|
||||
|
||||
/*
|
||||
* If you are reading this, I hope you are enjoying Scorpion
|
||||
*
|
||||
*
|
||||
* I have worked on this mod for several months and have tried my best to make it as easy to read and clean as possible
|
||||
* I may not always do things in the best way, but I do try!
|
||||
* If you have any questions please reach out to me in the SPT Discord - do not DM me
|
||||
*
|
||||
*/
|
||||
import { DependencyContainer, container } from "tsyringe";
|
||||
|
||||
// SPT types
|
||||
import { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod";
|
||||
import { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
|
||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { ImageRouter } from "@spt/routers/ImageRouter";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { ITraderConfig } from "@spt/models/spt/config/ITraderConfig";
|
||||
import { IRagfairConfig } from "@spt/models/spt/config/IRagfairConfig";
|
||||
import type {DynamicRouterModService} from "@spt/services/mod/dynamicRouter/DynamicRouterModService";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
|
||||
// JSON Imports
|
||||
import { JsonUtil } from "@spt/utils/JsonUtil";
|
||||
import { FileSystemSync } from "@spt/utils/FileSystemSync";
|
||||
import { jsonc } from "jsonc";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
|
||||
// Custom Imports
|
||||
import { TraderHelper } from "./traderHelpers";
|
||||
import { Traders } from "@spt/models/enums/Traders";
|
||||
import baseJson = require("../db/base.json");
|
||||
import questJson = require("../db/questassort.json");
|
||||
import assortJson = require("../db/assort.json");
|
||||
import productionJson = require("../db/production.json");
|
||||
import weaponCompatibility = require("../config/ModdedWeaponCompatibility.json");
|
||||
import scorpionQuests = require("../../Virtual's Custom Quest Loader/database/quests/Scorpion_quests.json");
|
||||
import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator";
|
||||
|
||||
let realismDetected: boolean;
|
||||
const loadMessage = {
|
||||
0: "Scorpion has brought his crew into Tarkov",
|
||||
1: "One of us..one of us..one of us",
|
||||
2: "Welcome to the team, you're one of us meow ♡",
|
||||
3: "Call Kenny Loggins because you're in the danger zone",
|
||||
4: "Can I offer you a nice egg in this trying time?",
|
||||
5: "Good news everyone! We have over 100 quests!",
|
||||
6: "Never half-ass two things. Whole-ass one thing.",
|
||||
7: "Thanks for signing up for Cat Facts! You will now receive fun daily facts about CATS!",
|
||||
8: "Thanks for signing up for Dog Facts! You will now receive fun daily facts about DOGS!",
|
||||
9: "A big ball of wibbly wobbly, timey wimey stuff",
|
||||
10: "(╯°□°)╯︵ ┻━┻ ",
|
||||
11: "┬─┬ノ( º _ ºノ)",
|
||||
12: "Treat others how you want to be treated",
|
||||
13: "No act of kindness, no matter how small, is ever wasted",
|
||||
14: "Reticulating Splines...",
|
||||
15: "Unfolding Foldy Chairs...",
|
||||
16: "Pressurizing Fruit Punch Barrel Hydraulics...",
|
||||
17: "Fabricating Imaginary Infrastructure...",
|
||||
18: "We apologize again for the fault in the subtitles. Those responsible for sacking the people who have just been sacked, have been sacked.",
|
||||
19: "Are you suggesting coconuts migrate?",
|
||||
20: "We are now the knights who say ekki-ekki-ekki-pitang-zoom-boing!",
|
||||
21: "Knight jumps queen! Bishop jumps queen! Pawns jump queen!",
|
||||
22: "Hello. My name is Inigo Montoya. You killed my father. Prepare to die.",
|
||||
23: "I spent the last few years building up an immunity to iocane powder.",
|
||||
24: "Rodents Of Unusual Size? I don't think they exist.",
|
||||
25: "Always try to be nice, but never fail to be kind",
|
||||
26: "Never be cruel, never be cowardly",
|
||||
27: "Who do I need to ban? (◣_◢)",
|
||||
28: "This loading message is sponsored by Raid: Shadow Legends"
|
||||
}
|
||||
|
||||
class Scorpion implements IPreSptLoadMod, IPostDBLoadMod
|
||||
{
|
||||
private mod: string
|
||||
private logger: ILogger
|
||||
private traderHelper: TraderHelper
|
||||
|
||||
private static fileSystemSync = container.resolve<FileSystemSync>("FileSystemSync");
|
||||
private static config: Config = jsonc.parse(Scorpion.fileSystemSync.read(path.resolve(__dirname, "../config/config.jsonc")));
|
||||
|
||||
// Set the name of mod for logging purposes
|
||||
constructor()
|
||||
{
|
||||
this.mod = "acidphantasm-scorpion";
|
||||
}
|
||||
|
||||
/*
|
||||
* Some work needs to be done prior to SPT code being loaded
|
||||
*
|
||||
* TLDR:
|
||||
* Resolve SPT Types
|
||||
* Set trader refresh, config, image, flea settings
|
||||
* Register Dynamic Router for Randomization Config
|
||||
*
|
||||
*/
|
||||
public preSptLoad(container: DependencyContainer): void
|
||||
{
|
||||
// Get a logger
|
||||
this.logger = container.resolve<ILogger>("WinstonLogger");
|
||||
|
||||
// Get SPT code/data we need later
|
||||
const dynamicRouterModService = container.resolve<DynamicRouterModService>("DynamicRouterModService");
|
||||
const preSptModLoader: PreSptModLoader = container.resolve<PreSptModLoader>("PreSptModLoader");
|
||||
const databaseService: DatabaseService = container.resolve<DatabaseService>("DatabaseService");
|
||||
const ragfairOfferGenerator = container.resolve<RagfairOfferGenerator>("RagfairOfferGenerator");
|
||||
const imageRouter: ImageRouter = container.resolve<ImageRouter>("ImageRouter");
|
||||
const configServer = container.resolve<ConfigServer>("ConfigServer");
|
||||
const traderConfig: ITraderConfig = configServer.getConfig<ITraderConfig>(ConfigTypes.TRADER);
|
||||
const ragfairConfig = configServer.getConfig<IRagfairConfig>(ConfigTypes.RAGFAIR);
|
||||
|
||||
// Set config values to local variables for validation & use
|
||||
let minRefresh = Scorpion.config.traderRefreshMin;
|
||||
let maxRefresh = Scorpion.config.traderRefreshMax;
|
||||
const addToFlea = Scorpion.config.addTraderToFlea;
|
||||
if (minRefresh >= maxRefresh || maxRefresh <= 2)
|
||||
{
|
||||
minRefresh = 1800;
|
||||
maxRefresh = 3600;
|
||||
this.logger.error(`[${this.mod}] [Config Issue] Refresh timers have been reset to default.`);
|
||||
}
|
||||
|
||||
// Create helper class and use it to register our traders image/icon + set its stock refresh time
|
||||
this.traderHelper = new TraderHelper();
|
||||
const currentDate = new Date();
|
||||
const month = currentDate.getMonth();
|
||||
const day = currentDate.getDate();
|
||||
if (month == 3 && day == 1)
|
||||
{
|
||||
baseJson.nickname = "ScorpionXYZ"
|
||||
baseJson.name = "ScorpionXYZ"
|
||||
baseJson.avatar = "/files/trader/avatar/6688d464bc40c867f60e7d7e_aprilfools.jpg"
|
||||
this.traderHelper.registerProfileImage(baseJson, this.mod, preSptModLoader, imageRouter, "6688d464bc40c867f60e7d7e_aprilfools.jpg");
|
||||
}
|
||||
else this.traderHelper.registerProfileImage(baseJson, this.mod, preSptModLoader, imageRouter, "6688d464bc40c867f60e7d7e.jpg");
|
||||
|
||||
this.traderHelper.setTraderUpdateTime(traderConfig, baseJson, minRefresh, maxRefresh);
|
||||
|
||||
// Add trader to trader enum
|
||||
Traders[baseJson._id] = baseJson._id;
|
||||
|
||||
// Add trader to flea market
|
||||
if (addToFlea)
|
||||
{
|
||||
ragfairConfig.traders[baseJson._id] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ragfairConfig.traders[baseJson._id] = false;
|
||||
}
|
||||
|
||||
dynamicRouterModService.registerDynamicRouter(
|
||||
"ScorpionRefreshStock",
|
||||
[
|
||||
{
|
||||
url: "/client/items/prices/6688d464bc40c867f60e7d7e",
|
||||
action: async (url, info, sessionId, output) =>
|
||||
{
|
||||
const trader = databaseService.getTables().traders["6688d464bc40c867f60e7d7e"];
|
||||
const assortItems = trader.assort.items;
|
||||
let updateFleaOffers = false;
|
||||
if (!realismDetected)
|
||||
{
|
||||
if (Scorpion.config.randomizeBuyRestriction)
|
||||
{
|
||||
if (Scorpion.config.debugLogging) {this.logger.info(`[${this.mod}] Refreshing Scorpion Stock with Randomized Buy Restrictions.`);}
|
||||
updateFleaOffers = true;
|
||||
this.randomizeBuyRestriction(assortItems);
|
||||
}
|
||||
if (Scorpion.config.randomizeStockAvailable)
|
||||
{
|
||||
if (Scorpion.config.debugLogging) {this.logger.info(`[${this.mod}] Refreshing Scorpion Stock with Randomized Stock Availability.`);}
|
||||
updateFleaOffers = true;
|
||||
this.randomizeStockAvailable(assortItems);
|
||||
}
|
||||
|
||||
if (updateFleaOffers) ragfairOfferGenerator.generateFleaOffersForTrader("6688d464bc40c867f60e7d7e");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
],
|
||||
"spt"
|
||||
);
|
||||
}
|
||||
/*
|
||||
* Some work needs to be done after loading SPT code
|
||||
*
|
||||
* TLDR:
|
||||
* Resolve SPT Types
|
||||
* Add Modded Weapons to Quests
|
||||
* Mod Detection to enable/disable Assort Configuration options
|
||||
* Apply Assort Configurations
|
||||
* Add trader to dictionary, locales, and assort
|
||||
*
|
||||
*/
|
||||
public postDBLoad(container: DependencyContainer): void
|
||||
{
|
||||
const start = performance.now();
|
||||
|
||||
// Resolve SPT classes we'll use
|
||||
const databaseService: DatabaseService = container.resolve<DatabaseService>("DatabaseService");
|
||||
const jsonUtil: JsonUtil = container.resolve<JsonUtil>("JsonUtil");
|
||||
const logger = container.resolve<ILogger>("WinstonLogger");
|
||||
const quests = databaseService.getTables().templates.quests;
|
||||
|
||||
|
||||
//Set local variables for assortJson
|
||||
const assortPriceTable = assortJson.barter_scheme;
|
||||
const assortItemTable = assortJson.items;
|
||||
const assortLoyaltyTable = assortJson.loyal_level_items;
|
||||
|
||||
//Run Modded Weapon Compatibility
|
||||
this.moddedWeaponCompatibility();
|
||||
|
||||
//Enable event quests
|
||||
if (Scorpion.config.eventQuestsAlwaysActive) { this.eventQuestsAlwaysActive(quests,scorpionQuests);}
|
||||
|
||||
//Check Mod Compatibility
|
||||
this.modDetection();
|
||||
|
||||
//Push Production Schemes
|
||||
this.pushProductionUnlocks();
|
||||
|
||||
//Update Assort
|
||||
if (Scorpion.config.priceMultiplier !== 1) {this.setPriceMultiplier(assortPriceTable);}
|
||||
if (Scorpion.config.randomizeBuyRestriction) {this.randomizeBuyRestriction(assortItemTable);}
|
||||
if (Scorpion.config.randomizeStockAvailable) {this.randomizeStockAvailable(assortItemTable);}
|
||||
if (Scorpion.config.unlimitedStock) {this.setUnlimitedStock(assortItemTable);}
|
||||
if (Scorpion.config.unlimitedBuyRestriction) {this.setUnlimitedBuyRestriction(assortItemTable);}
|
||||
if (Scorpion.config.removeLoyaltyRestriction) {this.disableLoyaltyRestrictions(assortLoyaltyTable);}
|
||||
|
||||
// Set local variable for assort to pass to traderHelper regardless of priceMultiplier config
|
||||
const newAssort = assortJson
|
||||
|
||||
// Get a reference to the database tables
|
||||
const tables = databaseService.getTables();
|
||||
|
||||
// Add new trader to the trader dictionary in DatabaseServer
|
||||
// Add quest assort
|
||||
// Add trader to locale file, ensures trader text shows properly on screen
|
||||
this.traderHelper.addTraderToDb(baseJson, tables, jsonUtil, newAssort);
|
||||
tables.traders[baseJson._id].questassort = questJson;
|
||||
this.traderHelper.addTraderToLocales(baseJson, tables, baseJson.name, baseJson._id, baseJson.nickname, baseJson.location, "I'm sellin', what are you buyin'?");
|
||||
|
||||
this.logger.debug(`[${this.mod}] loaded... `);
|
||||
|
||||
const timeTaken = performance.now() - start;
|
||||
if (Scorpion.config.debugLogging) {logger.log(`[${this.mod}] Trader load took ${timeTaken.toFixed(3)}ms.`, "cyan");}
|
||||
|
||||
logger.log(`[${this.mod}] ${this.getRandomLoadMessage()}`, "cyan");
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* All functions are below this comment
|
||||
*
|
||||
* Most of these functions should be self explanatory
|
||||
*
|
||||
*/
|
||||
private setRealismDetection(i: boolean) // Except this one. This is dumb. I'll fix it eventually.
|
||||
{
|
||||
realismDetected = i;
|
||||
if (realismDetected && Scorpion.config.randomizeBuyRestriction || realismDetected && Scorpion.config.randomizeStockAvailable)
|
||||
{
|
||||
this.logger.log(`[${this.mod}] SPT-Realism detected, disabling randomizeBuyRestriction and/or randomizeStockAvailable:`, "cyan");
|
||||
}
|
||||
}
|
||||
|
||||
private setPriceMultiplier (assortPriceTable)
|
||||
{
|
||||
let priceMultiplier = Scorpion.config.priceMultiplier;
|
||||
if (priceMultiplier <= 0)
|
||||
{
|
||||
priceMultiplier = 1;
|
||||
this.logger.error(`[${this.mod}] priceMultiplier cannot be set to zero.`)
|
||||
}
|
||||
for (const itemID in assortPriceTable)
|
||||
{
|
||||
for (const item of assortPriceTable[itemID])
|
||||
{
|
||||
if (item[0].count <= 15)
|
||||
{
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] itemID: [${itemID}] No price change, it's a barter trade.`, "cyan");}
|
||||
continue;
|
||||
}
|
||||
const count = item[0].count;
|
||||
const newPrice = Math.round(count * priceMultiplier);
|
||||
item[0].count = newPrice
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] itemID: [${itemID}] Price Changed to: [${newPrice}]`, "cyan");}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private randomizeBuyRestriction(assortItemTable)
|
||||
{
|
||||
const randomUtil: RandomUtil = container.resolve<RandomUtil>("RandomUtil");
|
||||
if (!realismDetected) // If realism is not detected, continue, else do nothing
|
||||
{
|
||||
for (const item in assortItemTable)
|
||||
{
|
||||
if (assortItemTable[item].parentId !== "hideout" || assortItemTable[item].upd.BuyRestrictionMax <= 3)
|
||||
{
|
||||
continue // Skip setting count, it's a weapon attachment or armour plate
|
||||
}
|
||||
const itemID = assortItemTable[item]._id;
|
||||
const oldRestriction = assortItemTable[item].upd.BuyRestrictionMax;
|
||||
const newRestriction = randomUtil.randInt(4, oldRestriction + 40);
|
||||
|
||||
assortItemTable[item].upd.BuyRestrictionMax = newRestriction;
|
||||
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] Item: [${itemID}] Buy Restriction Changed to: [${newRestriction}]`, "cyan");}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private randomizeStockAvailable(assortItemTable)
|
||||
{
|
||||
const randomUtil: RandomUtil = container.resolve<RandomUtil>("RandomUtil");
|
||||
if (!realismDetected) // If realism is not detected, continue, else do nothing
|
||||
{
|
||||
for (const item in assortItemTable)
|
||||
{
|
||||
if (assortItemTable[item].parentId !== "hideout")
|
||||
{
|
||||
continue // Skip setting count, it's a weapon attachment or armour plate
|
||||
}
|
||||
const outOfStockRoll = randomUtil.getChance100(Scorpion.config.outOfStockChance);
|
||||
|
||||
if (outOfStockRoll)
|
||||
{
|
||||
const itemID = assortItemTable[item]._id;
|
||||
assortItemTable[item].upd.StackObjectsCount = 0;
|
||||
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] Item: [${itemID}] Marked out of stock`, "cyan");}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (assortItemTable[item].upd.StackObjectsCount <= 10) assortItemTable[item].upd.StackObjectsCount = 250
|
||||
|
||||
const itemID = assortItemTable[item]._id;
|
||||
const originalStock = assortItemTable[item].upd.StackObjectsCount;
|
||||
const newStock = randomUtil.randInt(3, Math.round(originalStock*0.75));
|
||||
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] Item: [${itemID}] Original Count: ${originalStock} | Stock Count changed to: [${newStock}]`, "cyan");}
|
||||
|
||||
assortItemTable[item].upd.StackObjectsCount = newStock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setUnlimitedStock(assortItemTable)
|
||||
{
|
||||
for (const item in assortItemTable)
|
||||
{
|
||||
if (assortItemTable[item].parentId !== "hideout")
|
||||
{
|
||||
continue // Skip setting count, it's a weapon attachment or armour plate
|
||||
}
|
||||
assortItemTable[item].upd.StackObjectsCount = 9999999;
|
||||
assortItemTable[item].upd.UnlimitedCount = true;
|
||||
}
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] Item stock counts are now unlimited`, "cyan");}
|
||||
}
|
||||
|
||||
private setUnlimitedBuyRestriction(assortItemTable)
|
||||
{
|
||||
for (const item in assortItemTable)
|
||||
{
|
||||
if (assortItemTable[item].parentId !== "hideout")
|
||||
{
|
||||
continue // Skip setting count, it's a weapon attachment or armour plate
|
||||
}
|
||||
delete assortItemTable[item].upd.BuyRestrictionMax;
|
||||
delete assortItemTable[item].upd.BuyRestrictionCurrent;
|
||||
}
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] Item buy restrictions are now unlimited`, "cyan");}
|
||||
}
|
||||
|
||||
private disableLoyaltyRestrictions(assortLoyaltyTable)
|
||||
{
|
||||
for (const item in assortLoyaltyTable)
|
||||
{
|
||||
delete assortLoyaltyTable[item];
|
||||
}
|
||||
if (Scorpion.config.debugLogging) {this.logger.log(`[${this.mod}] All Loyalty Level requirements are removed`, "cyan");}
|
||||
}
|
||||
|
||||
private modDetection()
|
||||
{
|
||||
const preSptModLoader: PreSptModLoader = container.resolve<PreSptModLoader>("PreSptModLoader");
|
||||
const vcqlCheck = preSptModLoader.getImportedModsNames().includes("Virtual's Custom Quest Loader");
|
||||
const realismCheck = preSptModLoader.getImportedModsNames().includes("SPT-Realism");
|
||||
const vcqlDllPath = path.resolve(__dirname, "../../../../BepInEx/plugins/VCQLQuestZones.dll");
|
||||
const heliCrashSamSWAT = path.resolve(__dirname, "../../../../BepInEx/plugins/SamSWAT.HeliCrash/SamSWAT.HeliCrash.dll");
|
||||
const heliCrashTyrian = path.resolve(__dirname, "../../../../BepInEx/plugins/SamSWAT.HeliCrash.TyrianReboot/SamSWAT.HeliCrash.TyrianReboot.dll");
|
||||
const heliCrashArys = path.resolve(__dirname, "../../../../BepInEx/plugins/SamSWAT.HeliCrash.ArysReloaded/SamSWAT.HeliCrash.ArysReloaded.dll");
|
||||
|
||||
// VCQL Zones DLL is missing
|
||||
if (!fs.existsSync(vcqlDllPath))
|
||||
{
|
||||
this.logger.error(`[${this.mod}] VCQL Zones DLL missing. Custom Trader quests may not work properly.`);
|
||||
}
|
||||
|
||||
// Outdated HeliCrash is installed
|
||||
if (fs.existsSync(heliCrashSamSWAT) || fs.existsSync(heliCrashTyrian))
|
||||
{
|
||||
this.logger.error(`[${this.mod}] Outdated HeliCrash Mod Detected. You will experience issues with Custom Trader quest zones.`);
|
||||
}
|
||||
|
||||
// Arys HeliCrash is installed
|
||||
if (fs.existsSync(heliCrashArys))
|
||||
{
|
||||
this.logger.warning(`[${this.mod}] HeliCrash Mod Detected. You may experience issues with Custom Trader quest zones.`);
|
||||
}
|
||||
|
||||
// VCQL package.json is missing
|
||||
if (!vcqlCheck)
|
||||
{
|
||||
this.logger.error(`[${this.mod}] VCQL not detected. Install VCQL and re-install ${this.mod}.`);
|
||||
}
|
||||
|
||||
// This is completely unneccessary and I'll fix it, eventually - probably
|
||||
if (Scorpion.config.randomizeBuyRestriction || Scorpion.config.randomizeStockAvailable)
|
||||
{
|
||||
this.setRealismDetection(realismCheck);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setRealismDetection(realismCheck);
|
||||
}
|
||||
}
|
||||
|
||||
private moddedWeaponCompatibility()
|
||||
{
|
||||
const databaseService: DatabaseService = container.resolve<DatabaseService>("DatabaseService");
|
||||
const questTable = databaseService.getTables().templates.quests;
|
||||
const quests = Object.values(questTable);
|
||||
|
||||
let questType;
|
||||
let weaponType;
|
||||
let wasAdded:boolean;
|
||||
|
||||
if (weaponCompatibility.AssaultRifles.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.AssaultRifles;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - ARs"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.SubmachineGuns.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.SubmachineGuns;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - SMGs"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Snipers.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Snipers;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Snipers"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Marksman.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Marksman;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Marksman"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Shotguns.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Shotguns;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Shotguns"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Pistols.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Pistols;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Pistols"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.LargeMachineGuns.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.LargeMachineGuns;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - LMGs"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Carbines.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Carbines;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Carbines"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Melee.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Melee;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Melee"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (weaponCompatibility.Explosives.length >= 1)
|
||||
{
|
||||
weaponType = weaponCompatibility.Explosives;
|
||||
questType = quests.filter(x => x.QuestName.includes("Weapon Proficiency - Explosives"));
|
||||
wasAdded = true;
|
||||
this.moddedWeaponPushToArray(questType, weaponType);
|
||||
}
|
||||
if (wasAdded) { this.logger.log(`[${this.mod}] Custom Weapons added to proficiency quests. Enjoy!`, "cyan"); }
|
||||
}
|
||||
|
||||
private moddedWeaponPushToArray(questTable, weaponType)
|
||||
{
|
||||
for (const quest in questTable)
|
||||
{
|
||||
for (const condition in questTable[quest].conditions.AvailableForFinish)
|
||||
{
|
||||
for (const item in questTable[quest].conditions.AvailableForFinish[condition].counter.conditions)
|
||||
{
|
||||
for (const id of weaponType)
|
||||
{
|
||||
questTable[quest].conditions.AvailableForFinish[condition].counter.conditions[item].weapon.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Scorpion.config.debugLogging) { this.logger.log(`[${this.mod}] ${questTable[quest].QuestName} --- Added ${weaponType}`, "cyan"); }
|
||||
}
|
||||
}
|
||||
|
||||
private eventQuestsAlwaysActive(questTable, quests)
|
||||
{
|
||||
let eventCount = 0;
|
||||
for (const quest in quests)
|
||||
{
|
||||
if (quests[quest]?.startMonth)
|
||||
{
|
||||
const currentDate = new Date();
|
||||
const questStartDate = new Date(currentDate.getFullYear(), quests[quest].startMonth - 1, quests[quest].startDay)
|
||||
const questEndDate = new Date(currentDate.getFullYear(), quests[quest].endMonth - 1, quests[quest].endDay)
|
||||
|
||||
if (currentDate < questStartDate || currentDate > questEndDate)
|
||||
{
|
||||
delete quests[quest].startMonth;
|
||||
delete quests[quest].endMonth;
|
||||
delete quests[quest].startDay;
|
||||
delete quests[quest].endDay
|
||||
|
||||
questTable[quest] = quests[quest];
|
||||
eventCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.logger.log(`[${this.mod}] Reactivated ${eventCount} Event Quests from Scorpion - Enjoy!`, "cyan");
|
||||
this.logger.log(`[${this.mod}] !!! Remember to fix your config.jsonc when you update this mod to keep event quest progress !!!`, "cyan");
|
||||
}
|
||||
|
||||
private pushProductionUnlocks() {
|
||||
const databaseService: DatabaseService = container.resolve<DatabaseService>("DatabaseService");
|
||||
const recipesTable = databaseService.getTables().hideout.production.recipes;
|
||||
|
||||
for (const item of productionJson)
|
||||
{
|
||||
recipesTable.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
private getRandomLoadMessage()
|
||||
{
|
||||
const value = loadMessage[Math.floor(Math.random() * Object.keys(loadMessage).length)];
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* This is the interface for the config to validate the values
|
||||
*
|
||||
*/
|
||||
interface Config
|
||||
{
|
||||
randomizeStockAvailable: boolean,
|
||||
outOfStockChance: number,
|
||||
randomizeBuyRestriction: boolean,
|
||||
priceMultiplier: number,
|
||||
unlimitedStock: boolean,
|
||||
unlimitedBuyRestriction: boolean,
|
||||
removeLoyaltyRestriction: boolean,
|
||||
traderRefreshMin: number,
|
||||
traderRefreshMax: number,
|
||||
addTraderToFlea: boolean,
|
||||
eventQuestsAlwaysActive: boolean,
|
||||
debugLogging: boolean,
|
||||
}
|
||||
|
||||
module.exports = { mod: new Scorpion() }
|
||||
106
user/mods/acidphantasm-scorpion/src/traderHelpers.ts
Normal file
106
user/mods/acidphantasm-scorpion/src/traderHelpers.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
|
||||
import { ITraderBase, ITraderAssort } from "@spt/models/eft/common/tables/ITrader";
|
||||
import { ITraderConfig, UpdateTime } from "@spt/models/spt/config/ITraderConfig";
|
||||
import { IDatabaseTables } from "@spt/models/spt/server/IDatabaseTables";
|
||||
import { ImageRouter } from "@spt/routers/ImageRouter";
|
||||
import { JsonUtil } from "@spt/utils/JsonUtil";
|
||||
import { ITraderUnlockRequirement } from "@spt/models/eft/hideout/IQteData";
|
||||
import * as questAssort from "../db/questassort.json";
|
||||
|
||||
export class TraderHelper
|
||||
{
|
||||
/**
|
||||
* Add profile picture to our trader
|
||||
* @param baseJson json file for trader (db/base.json)
|
||||
* @param preSptModLoader mod loader class - used to get the mods file path
|
||||
* @param imageRouter image router class - used to register the trader image path so we see their image on trader page
|
||||
* @param traderImageName Filename of the trader icon to use
|
||||
*/
|
||||
public registerProfileImage(baseJson: any, modName: string, preSptModLoader: PreSptModLoader, imageRouter: ImageRouter, traderImageName: string): void
|
||||
{
|
||||
// Reference the mod "res" folder
|
||||
const imageFilepath = `./${preSptModLoader.getModPath(modName)}res`;
|
||||
|
||||
// Register a route to point to the profile picture - remember to remove the .jpg from it
|
||||
imageRouter.addRoute(baseJson.avatar.replace(".jpg", ""), `${imageFilepath}/${traderImageName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add record to trader config to set the refresh time of trader in seconds (default is 60 minutes)
|
||||
* @param traderConfig trader config to add our trader to
|
||||
* @param baseJson json file for trader (db/base.json)
|
||||
* @param refreshTimeSecondsMin How many seconds between trader stock refresh min time
|
||||
* @param refreshTimeSecondsMax How many seconds between trader stock refresh max time
|
||||
*/
|
||||
public setTraderUpdateTime(traderConfig: ITraderConfig, baseJson: any, refreshTimeSecondsMin: number, refreshTimeSecondsMax: number): void
|
||||
{
|
||||
// Add refresh time in seconds to config
|
||||
const traderRefreshRecord: UpdateTime = {
|
||||
traderId: baseJson._id,
|
||||
seconds: {
|
||||
min: refreshTimeSecondsMin,
|
||||
max: refreshTimeSecondsMax
|
||||
} };
|
||||
|
||||
traderConfig.updateTime.push(traderRefreshRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add our new trader to the database
|
||||
* @param traderDetailsToAdd trader details
|
||||
* @param tables database
|
||||
* @param jsonUtil json utility class
|
||||
*/
|
||||
// rome-ignore lint/suspicious/noExplicitAny: traderDetailsToAdd comes from base.json, so no type
|
||||
public addTraderToDb(traderDetailsToAdd: any, tables: IDatabaseTables, jsonUtil: JsonUtil, newAssort: any): void
|
||||
{
|
||||
// Add trader to trader table, key is the traders id
|
||||
tables.traders[traderDetailsToAdd._id] = {
|
||||
assort: jsonUtil.deserialize(jsonUtil.serialize(newAssort)) as ITraderAssort, // assorts are the 'offers' trader sells, can be a single item (e.g. carton of milk) or multiple items as a collection (e.g. a gun)
|
||||
base: jsonUtil.deserialize(jsonUtil.serialize(traderDetailsToAdd)) as ITraderBase, // Deserialise/serialise creates a copy of the json and allows us to cast it as an ITraderBase
|
||||
questassort: jsonUtil.deserialize(jsonUtil.serialize(questAssort)) // questassort is empty as trader has no assorts unlocked by quests
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create basic data for trader + add empty assorts table for trader
|
||||
* @param tables SPT db
|
||||
* @param jsonUtil SPT JSON utility class
|
||||
* @returns ITraderAssort
|
||||
*/
|
||||
private createAssortTable(): ITraderAssort
|
||||
{
|
||||
// Create a blank assort object, ready to have items added
|
||||
const assortTable: ITraderAssort = {
|
||||
nextResupply: 0,
|
||||
items: [],
|
||||
barter_scheme: {},
|
||||
loyal_level_items: {}
|
||||
}
|
||||
|
||||
return assortTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add traders name/location/description to the locale table
|
||||
* @param baseJson json file for trader (db/base.json)
|
||||
* @param tables database tables
|
||||
* @param fullName Complete name of trader
|
||||
* @param firstName First name of trader
|
||||
* @param nickName Nickname of trader
|
||||
* @param location Location of trader (e.g. "Here in the cat shop")
|
||||
* @param description Description of trader
|
||||
*/
|
||||
public addTraderToLocales(baseJson: any, tables: IDatabaseTables, fullName: string, firstName: string, nickName: string, location: string, description: string)
|
||||
{
|
||||
// For each language, add locale for the new trader
|
||||
const locales = Object.values(tables.locales.global) as Record<string, string>[];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue