From cfdea7d325cf3ebb08f3c9a9b563d27e4f127763 Mon Sep 17 00:00:00 2001 From: Refringe Date: Wed, 24 Apr 2024 23:31:39 -0400 Subject: [PATCH 01/21] Workflow: Tests the checkout action --- .gitea/workflows/run-test.yaml | 70 +++++++++++++++------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index e5f9adad..2fbd2354 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -9,50 +9,42 @@ on: jobs: vitest: runs-on: ubuntu-latest + if: > # Conditional to limit runs: it checks if it's NOT a push to a branch with an open PR + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name != github.repository container: image: refringe/spt-build-node:1.0.7 steps: - - name: Clone - run: | - rm -rf /workspace/SPT-AKI/Build/server - git clone https://dev.sp-tarkov.com/${GITHUB_REPOSITORY}.git --branch master /workspace/SPT-AKI/Build/server - cd /workspace/SPT-AKI/Build/server - git checkout ${GITHUB_SHA} - shell: bash + - name: Checkout Repository + uses: actions/checkout@v3 + with: + lfs: true + github-server-url: 'https://dev.sp-tarkov.com' - - name: Pull LFS Files - run: | - cd /workspace/SPT-AKI/Build/server - git lfs pull - git lfs ls-files - shell: bash + - name: Cache NPM Dependencies + id: cache-npm-dependencies + uses: actions/cache@v4 + with: + path: project/node_modules + key: npm-dependencies-${{ hashFiles('project/package.json') }} - - name: Cache NPM Dependencies - id: cache-npm-dependencies - uses: actions/cache@v4 - with: - path: /workspace/SPT-AKI/Build/server/project/node_modules - key: npm-dependencies-${{ hashFiles('/workspace/SPT-AKI/Build/server/project/package.json') }} + - name: Install NPM Dependencies + if: steps.cache-npm-dependencies.outputs.cache-hit != 'true' + run: | + rm -rf project/node_modules + npm install + shell: bash - - name: Install NPM Dependencies - if: steps.cache-npm-dependencies.outputs.cache-hit != 'true' - run: | - cd /workspace/SPT-AKI/Build/server/project - rm -rf node_modules - npm install - shell: bash + - name: Run Tests + id: run-tests + run: | + npm run test + shell: bash - - name: Run Tests - id: run-tests - run: | - cd /workspace/SPT-AKI/Build/server/project - npm run test - shell: bash - - - name: Fix Instructions - if: failure() && steps.run-tests.outcome == 'failure' - run: | - echo -e "Automated tests have failed. This could point to an issue with the commited code, or an updated test that has yet to be updated. Please look into resolving these test failures. The testing suite has a GUI to aid in writing tests. You can launch this by running the following command from within the 'project' directory.\n\nnpm run test:ui\n" - echo -e "A test written today is a bug prevented tomorrow.™" - shell: bash + - name: Fix Instructions + if: failure() && steps.run-tests.outcome == 'failure' + run: | + echo -e "Automated tests have failed. This could point to an issue with the committed code, or an updated test that has yet to be updated. Please look into resolving these test failures. The testing suite has a GUI to aid in writing tests. You can launch this by running the following command from within the 'project' directory.\n\nnpm run test:ui\n" + echo -e "A test written today is a bug prevented tomorrow.™" + shell: bash From d47f7e3b13c056aa933c6792e1af1c285e698973 Mon Sep 17 00:00:00 2001 From: Refringe Date: Wed, 24 Apr 2024 23:42:17 -0400 Subject: [PATCH 02/21] Workflow: Pull LFS files manually. Checkout doesn't like us. --- .gitea/workflows/run-test.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 2fbd2354..379f6a08 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -19,9 +19,16 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 with: - lfs: true + lfs: false github-server-url: 'https://dev.sp-tarkov.com' + - name: Pull LFS Files + run: | + ls -lah + git lfs pull + git lfs ls-files + shell: bash + - name: Cache NPM Dependencies id: cache-npm-dependencies uses: actions/cache@v4 From 391834081e70ff29585ee9858e3d06f5bd95136c Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:06:29 -0400 Subject: [PATCH 03/21] Workflow: Reverts to manual checkout, swaps to using GITHUB_REF --- .gitea/workflows/run-test.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 379f6a08..01362d09 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -9,22 +9,24 @@ on: jobs: vitest: runs-on: ubuntu-latest - if: > # Conditional to limit runs: it checks if it's NOT a push to a branch with an open PR + if: > # Conditional to limit runs: checks if it's NOT a push to a branch with an open PR github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository container: image: refringe/spt-build-node:1.0.7 steps: + - name: Clone + run: | + rm -rf /workspace/SPT-AKI/Server + git clone https://dev.sp-tarkov.com/${{ GITHUB_REPOSITORY }}.git --branch master /workspace/SPT-AKI/Server - - name: Checkout Repository - uses: actions/checkout@v3 - with: - lfs: false - github-server-url: 'https://dev.sp-tarkov.com' + cd /workspace/SPT-AKI/Server + git checkout ${{ GITHUB_REF }} + shell: bash - name: Pull LFS Files run: | - ls -lah + cd /workspace/SPT-AKI/Server git lfs pull git lfs ls-files shell: bash From ba4e26585b05e7566f49145ebe208e2d4b390d80 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:10:32 -0400 Subject: [PATCH 04/21] Workflow: Adds debugging information --- .gitea/workflows/run-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 01362d09..a87875be 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -20,13 +20,13 @@ jobs: rm -rf /workspace/SPT-AKI/Server git clone https://dev.sp-tarkov.com/${{ GITHUB_REPOSITORY }}.git --branch master /workspace/SPT-AKI/Server - cd /workspace/SPT-AKI/Server + cd /workspace/SPT-AKI/Server && ls -lah git checkout ${{ GITHUB_REF }} shell: bash - name: Pull LFS Files run: | - cd /workspace/SPT-AKI/Server + cd /workspace/SPT-AKI/Server && ls -lah git lfs pull git lfs ls-files shell: bash From 1495587b4399599efa00e2b352f74016d7e10014 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:18:27 -0400 Subject: [PATCH 05/21] Workflow: Attempts to access the environment variables differently --- .gitea/workflows/run-test.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index a87875be..77bde855 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -17,11 +17,14 @@ jobs: steps: - name: Clone run: | + echo "github.repository = ${{ github.repository }}" + echo "github.ref = ${{ github.ref }}" + rm -rf /workspace/SPT-AKI/Server - git clone https://dev.sp-tarkov.com/${{ GITHUB_REPOSITORY }}.git --branch master /workspace/SPT-AKI/Server + git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master /workspace/SPT-AKI/Server cd /workspace/SPT-AKI/Server && ls -lah - git checkout ${{ GITHUB_REF }} + git checkout ${{ github.ref }} shell: bash - name: Pull LFS Files From 3e2e2e47dfadbe43302cc6b03be8788b3b3455f4 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:27:18 -0400 Subject: [PATCH 06/21] Workflow: Use the workspace environment variable --- .gitea/workflows/run-test.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 77bde855..b6b811ee 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -19,19 +19,20 @@ jobs: run: | echo "github.repository = ${{ github.repository }}" echo "github.ref = ${{ github.ref }}" + echo "github.ref = ${{ github.ref }}" + echo "github.workspace = ${{ github.workspace }}" - rm -rf /workspace/SPT-AKI/Server - git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master /workspace/SPT-AKI/Server + rm -rf ${{ github.workspace }} + git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master ${{ github.workspace }} - cd /workspace/SPT-AKI/Server && ls -lah + cd ${{ github.workspace }} && ls -lah git checkout ${{ github.ref }} shell: bash - name: Pull LFS Files run: | - cd /workspace/SPT-AKI/Server && ls -lah - git lfs pull - git lfs ls-files + cd ${{ github.workspace }} && ls -lah + git lfs pull && git lfs ls-files shell: bash - name: Cache NPM Dependencies From bf7f5c943cb1b688c3a35a1f0c560e3a7ca7cdd1 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:34:02 -0400 Subject: [PATCH 07/21] Workflow: Start the clone step from the workspace directory --- .gitea/workflows/run-test.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index b6b811ee..7df8a1cc 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -17,11 +17,7 @@ jobs: steps: - name: Clone run: | - echo "github.repository = ${{ github.repository }}" - echo "github.ref = ${{ github.ref }}" - echo "github.ref = ${{ github.ref }}" - echo "github.workspace = ${{ github.workspace }}" - + cd /workspace rm -rf ${{ github.workspace }} git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master ${{ github.workspace }} From 900e0e6a1573b2f3d85f5eb99e5ea88a9a119bfa Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:39:24 -0400 Subject: [PATCH 08/21] Workflow: Running into walls. Work from a subdirectory. --- .gitea/workflows/run-test.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 7df8a1cc..e20660b2 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -17,17 +17,16 @@ jobs: steps: - name: Clone run: | - cd /workspace - rm -rf ${{ github.workspace }} - git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master ${{ github.workspace }} + rm -rf ${{ github.workspace }}/current + git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master ${{ github.workspace }}/current - cd ${{ github.workspace }} && ls -lah + cd ${{ github.workspace }}/current && ls -lah git checkout ${{ github.ref }} shell: bash - name: Pull LFS Files run: | - cd ${{ github.workspace }} && ls -lah + cd ${{ github.workspace }}/current && ls -lah git lfs pull && git lfs ls-files shell: bash @@ -35,13 +34,13 @@ jobs: id: cache-npm-dependencies uses: actions/cache@v4 with: - path: project/node_modules - key: npm-dependencies-${{ hashFiles('project/package.json') }} + path: current/project/node_modules + key: npm-dependencies-${{ hashFiles('current/project/package.json') }} - name: Install NPM Dependencies if: steps.cache-npm-dependencies.outputs.cache-hit != 'true' run: | - rm -rf project/node_modules + rm -rf current/project/node_modules npm install shell: bash From ea0b4bdeed3bac34477b14dbaa08e19e09f4e662 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 00:47:42 -0400 Subject: [PATCH 09/21] Workflow: Reference the current branch in the clone command. --- .gitea/workflows/run-test.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index e20660b2..24f5deb7 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -18,10 +18,7 @@ jobs: - name: Clone run: | rm -rf ${{ github.workspace }}/current - git clone --progress --verbose https://dev.sp-tarkov.com/${{ github.repository }}.git --branch master ${{ github.workspace }}/current - - cd ${{ github.workspace }}/current && ls -lah - git checkout ${{ github.ref }} + git clone --progress https://dev.sp-tarkov.com/${{ github.repository }}.git --branch ${{ github.ref }} ${{ github.workspace }}/current shell: bash - name: Pull LFS Files From 593121e5a74ab9f5642610d4811cac16c20fde17 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 01:27:52 -0400 Subject: [PATCH 10/21] Workflow: Try using the branch for pushes, and sha for PRs --- .gitea/workflows/run-test.yaml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 24f5deb7..c735e7e7 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -17,8 +17,23 @@ jobs: steps: - name: Clone run: | + # For pull request events, checkout using GITHUB_SHA + # For push events, checkout using GITHUB_REF_NAME + if [[ $GITHUB_EVENT_NAME == "pull_request" ]]; then + REF=${GITHUB_SHA} + else + REF=${GITHUB_REF_NAME} + fi + rm -rf ${{ github.workspace }}/current - git clone --progress https://dev.sp-tarkov.com/${{ github.repository }}.git --branch ${{ github.ref }} ${{ github.workspace }}/current + git clone https://dev.sp-tarkov.com/${{ github.repository }}.git ${{ github.workspace }}/current + + cd ${{ github.workspace }}/current + git checkout $REF + env: + GITHUB_SHA: ${{ github.sha }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_EVENT_NAME: ${{ github.event_name }} shell: bash - name: Pull LFS Files From 432ae73a6b9901ea262bd3755fdb571d1a84aaba Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 01:34:35 -0400 Subject: [PATCH 11/21] Workflow: Incorrect install npm working directory --- .gitea/workflows/run-test.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index c735e7e7..02732b72 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -25,10 +25,10 @@ jobs: REF=${GITHUB_REF_NAME} fi - rm -rf ${{ github.workspace }}/current - git clone https://dev.sp-tarkov.com/${{ github.repository }}.git ${{ github.workspace }}/current + rm -rf /workspace/SPT-AKI/Server/current + git clone https://dev.sp-tarkov.com/${{ github.repository }}.git /workspace/SPT-AKI/Server/current - cd ${{ github.workspace }}/current + cd /workspace/SPT-AKI/Server/current git checkout $REF env: GITHUB_SHA: ${{ github.sha }} @@ -38,7 +38,7 @@ jobs: - name: Pull LFS Files run: | - cd ${{ github.workspace }}/current && ls -lah + cd /workspace/SPT-AKI/Server/current && ls -lah git lfs pull && git lfs ls-files shell: bash @@ -46,19 +46,21 @@ jobs: id: cache-npm-dependencies uses: actions/cache@v4 with: - path: current/project/node_modules - key: npm-dependencies-${{ hashFiles('current/project/package.json') }} + path: /workspace/SPT-AKI/Server/current/project/node_modules + key: npm-dependencies-${{ hashFiles('/workspace/SPT-AKI/Server/current/project/package.json') }} - name: Install NPM Dependencies if: steps.cache-npm-dependencies.outputs.cache-hit != 'true' run: | - rm -rf current/project/node_modules + cd /workspace/SPT-AKI/Server/current/project + rm -rf /workspace/SPT-AKI/Server/current/project/node_modules npm install shell: bash - name: Run Tests id: run-tests run: | + cd /workspace/SPT-AKI/Server/current/project npm run test shell: bash From dce2ccbcfda992d389ad9722da7735986babe221 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 01:43:03 -0400 Subject: [PATCH 12/21] Workflow: Attempt to fetch before checking out the PR merge commit by hash --- .gitea/workflows/run-test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/run-test.yaml b/.gitea/workflows/run-test.yaml index 02732b72..196672a8 100644 --- a/.gitea/workflows/run-test.yaml +++ b/.gitea/workflows/run-test.yaml @@ -29,6 +29,7 @@ jobs: git clone https://dev.sp-tarkov.com/${{ github.repository }}.git /workspace/SPT-AKI/Server/current cd /workspace/SPT-AKI/Server/current + git fetch git checkout $REF env: GITHUB_SHA: ${{ github.sha }} From fbf1d7f7fbe68cf5442fd1e29eeaac6e9e353ee2 Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 25 Apr 2024 17:26:47 +0000 Subject: [PATCH 13/21] Updates Server README (!306) - Updates the Server project README to include additional information about installation, development, and contribuation guidelines. - Moves the feature list to a dedicated FEATURE.md file (linked from the README.md). - Reviewed the FEATURES.md file to clean it up a little bit. Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/306 Co-authored-by: Refringe Co-committed-by: Refringe --- FEATURES.md | 319 +++++++++++++++++++++++++ README.md | 412 +++++++------------------------- project/.vscode/extensions.json | 10 + project/Server.code-workspace | 8 - 4 files changed, 420 insertions(+), 329 deletions(-) create mode 100644 FEATURES.md create mode 100644 project/.vscode/extensions.json diff --git a/FEATURES.md b/FEATURES.md new file mode 100644 index 00000000..17ca7702 --- /dev/null +++ b/FEATURES.md @@ -0,0 +1,319 @@ +# Features + +## Table of Contents +- [Profiles](#Profiles) + - [Progression](#progression) + - [Starting Profile Types](#starting-profile-types) +- [Bots](#bots) + - [AI Types](#ai-types) + - [Generation](#generation) +- [Inventory](#inventory) +- [Traders](#traders) +- [Flea market](#flea-market) +- [Quests](#quests) +- [Hideout](#hideout) +- [Weapon Building](#weapon-building) +- [Raids](#raids) +- [Messages](#messages) +- [Events](#events) +- [Modding](#modding) + +## Profiles + +### Progression + +The player profile is stored as a JSON file, allowing for changes to persist across server restarts. The profile contains the following information for both your PMC and Scav player characters: + +- Task Conditions +- Account Bonuses +- Model Selection +- Health +- Energy, Hydration, & Temperature +- Hideout Build & Production Status +- Items (Inventory, Insured, Quest, Wishlist) +- Inventory +- Quest Progress +- Flea Market Rating & Current Offers +- Common and Mastering Skills +- Various Raid Stats +- Trader Status and Loyalty Levels +- Extract Counts +- Achievements + +### Starting Profile Types + +The following profile types are available to start with when creating an account in the Launcher: + +- Standard Profiles: + - Standard + - Left Behind + - Prepare To Escape + - Edge Of Darkness +- Custom profiles + - SPT Easy Start + - Lots of money, quality of life skills to level 20, and player to level 69. + - SPT Zero to Hero + - No money, skills, trader reputation, or items. Start with a knife. + - SPT Developer + - Developer testing profile, player to level 69, max skills, and max trader reputation. + - USEC will have all quests ready to start. + - BEAR will have all quests ready to hand in. + +## Bots + +### AI Types + +Bot data is emulated to mimic live bots as closely as possible. This includes the following bot types: + +- Scavs + - Regular Scav (*assault*) + - Sniper Scav (*marksman*) + - Tagged & Cursed (*cursedAssault*) +- Bosses + - Reshalla (*bossBully*) + - Guard (*followerBully*) + - Glukhar (*bossGluhar*) + - Assault Guard (*followerGluharAssault*) + - Scout Guard (*followerGluharScout*) + - Security Guard (*followerGluharSecurity*) + - Sniper Guard (*followerGluharSnipe*) + - Killa (*bossKilla*) + - Shturman (*bossKojainy*) + - Guard (*followerKojaniy*) + - Sanitar (*bossSanitar*) + - Guard (*followerSanitar*) + - Tagilla (*bossTagilla*) + - Knight (*bossKnight*) + - Big Pipe (*followerBigPipe*) + - Bird Eye (*followerBirdEye*) + - Zryachiy (*bossZryachiy*) + - Guard (*followerzryachiy*) + - Kaban (*bossBoar*) + - Sniper Guard (*bossBoarSniper*) + - Guard (*followerBoar*) +- Cultists + - Priest (*sectantPriest*) + - Warrior (*sectantWarrior*) +- Raiders (*pmcBot*) +- Rogues (*exUsec*) +- Santa (*gifter*) - *partially implemented* + +*PMCs are generated with a random type from a sub-set of the above list.* + +*Some bot types are only available on some maps.* + +### Generation + +Bots are generated with the following characteristics: + +- All Bots: + - Weapons - *Weighted, semi-randomly selected* + - Ammunition - *Weighted, semi-randomly selected* + - Gear - *Weighted, semi-randomly selected* + - Headgear Attachments - *Weighted, semi-randomly selected* +- PMC Bots + - AI Type - *Randomly chosen from sub-set of possible bot types* + - Dogtags - *Random level & name* + - Chance of name being the name of a contributor to the project + - Voices - *Randomly chosen Bear/USEC voices for each faction* + +Other bot generation systems/features include: + +- Loot item blacklist & whitelist +- Loot items can be configured to be limited to a certain number based on bot type +- Level-relative gear for PMCs from levels 1-15 and 15+ + - Level 1-15 bots have lower-tier items + - Level 15+ bots have access to almost anything +- Randomised gear and weapon durability based on bot type and level + +## Inventory + +The inventory system includes the following features: + +- Move, Split, and Delete Item Stacks +- Add, Modify, and Remove Item Tags +- Armor and Weapon Repair Kits +- Auto-sort Inventory +- Out-of-raid Healing, Eating, & Drinking +- Special Player Slots + +## Traders + +The trader system includes the following features: + +- Buy and sell items from each trader +- Listed items are refreshed on a timer based on the trader +- Purchase limits per refresh period +- Tracks currency spent through each trader +- Loyalty levels +- Reputation +- Item repair from Prapor, Skier, and Mechanic +- Unlock and purchase clothing from Ragman +- Insurance from Therapist and Prapor + - Chance for items to be returned, higher chance for more expensive trader + - Chance parts will be stripped from returned weapons based on value +- Post-raid Therapist Healing +- Fence Item Assortment + - Lists random items for sale + - Emulated system of 'churn' for items sold by Fence + +## Flea market + +The flea market system has been build to simulate the live flea market as closely as possible. It includes the following features: + +- Simulated Player Offers + - Generated with random names, ratings, and expiry times + - Variable offer prices based on live item prices (~20% above and below) + - Weapon presets as offers + - Barter offers + - Listed in multiple currencies (Rouble, Euro, and Dollar) + - Dynamically adjust flea prices that drift below trader price +- Buy Items +- Sell Items + - Generates listing fee + - Increase flea rating by selling items + - Decrease flea rating by failing to sell items + - Items purchased by simulated players + - Offer price effects chance that item will be purchased +- Filtering + - By specific item + - By link to item + - Text search by name + - By currency + - By price range + - By condition range + - By Traders, Players, or Both + - To include barter offers (or not) +- Sorting by + - Rating + - Name + - Price + - Expiry + +## Quests + +The quest system includes the following features: + +- Accurate Quest List - *roughly 90% implemented* +- Trader Quests - *Accept, Turn-in Items, and Complete* +- Daily Quests - *Accept, Replace, Turn-in Items, Complete* + - Simulates Daily and Weekly Quests + - Quest Replacement Fee +- Scav Quests +- Trader items unlock through completion of quests +- Receive messages from traders after interacting with a quest +- Item rewards passed through messages + +## Hideout + +The hideout has the following features implemented: + +- Areas + - Air Filter + - Filter Degradation + - Boosts Skill Levelling + - Bitcoin Farm + - Generation Speed Dependent on Number of Graphics Cards + - Booze Generator + - Crafts Moonshine + - Generator + - Fuel Degradation + - Heating + - Energy Regeneration + - Negative Effects Removal + - Illumination + - Intel Centre + - ~~Unlocks Fence's Scav Quests~~ *not implemented - workaround: unlocks at level 5* + - ~~Reduces Insurance Return Time~~ *not implemented* + - Quest Currency Reward Boost + - Lavatory + - Library + - Medstation + - Nutrition Unit + - Rest Space + - Scav Case + - Custom Reward System + - Security + - Shooting Range + - Solar Power + - Stash + - Upgrades grant larger stash sizes + - Vents + - Water Collector + - Workbench + - Unlocks the ability to repair items + - Christmas Tree +- Item Crafting + - Items are marked found-in-raid on completion + - Continues to track crafting progress even when server is not running + +## Weapon Building + +The weapon building system has been fully implemented: + +- Create Weapon Presets +- Saving Presets +- Load Presets + +## Raids + +The in-raid systems included are as follows: + +- Maps + - Customs + - Factory Day + - Factory Night + - Ground Zero + - Interchange + - Laboratory + - Lighthouse + - Reserve + - Shoreline + - Streets + - Woods +- Loot + - Loot spawning has been generated using over 100,000 EFT offline loot runs. + - Static Loot (in containers) + - Each container type can contain items appropriate to that container type found in offline EFT. + - Loose Loot (on map) + - Randomised loose items found on map in offline EFT. +- Airdrops + - Randomised Spawn Chance + - Request with Red Flare + - Crate Types: + - Weapons & Armour + - Food & Medical + - Barter Goods + - Mixed - *mixture of any of the above items* + - Supported Maps: + - Customs + - Interchange + - Lighthouse + - Reserve + - Shoreline + - Streets + - Woods +- Persisted Raid Damage - *extracting with injury will persist injury out of raid* +- Scav Raids - *raid time and items are reduced to simulate entering a raid late* + +## Messages + +A messaging system has been implemented to allow for the following functionality: + +- Receive messages (with item attachments) from traders or "system" +- Pin/unpin senders within the message list +- Receive all (or individual) attachments +- Send messages to "Commando" friend to execute server commands + +## Events + +The following events have been implemented and have a set time period for when they will be active: + +- Snow +- Halloween +- Christmas + +## Modding + +- The Server project has been built to allow for extensive modifications to nearly any aspect and system used. +- [Example mods](https://dev.sp-tarkov.com/chomp/ModExamples) are provided that cover the most common modding methods. diff --git a/README.md b/README.md index 42469df8..de26217d 100644 --- a/README.md +++ b/README.md @@ -1,352 +1,122 @@ -# Server +# Single Player Tarkov - Server Project -Modding framework for Escape From Tarkov +This is the Server project for the Single Player Tarkov mod for Escape From Tarkov. It can be run locally to replicate responses to the modified Escape From Tarkov client. -[![Build Status](https://drone.sp-tarkov.com/api/badges/SPT-AKI/Server/status.svg?ref=refs/heads/development)](https://drone.sp-tarkov.com/SPT-AKI/Server) -[![Quality Gate Status](https://sonar.sp-tarkov.com/api/project_badges/measure?project=AKI&metric=alert_status&token=d3b87ff5fac591c1f49a57d4a2883c92bfe6a77f)](https://sonar.sp-tarkov.com/dashboard?id=AKI) +# Table of Contents -## Privacy -SPT is an open source project. Your commit credentials as author of a commit will be visible by anyone. Please make sure you understand this before submitting a PR. -Feel free to use a "fake" username and email on your commits by using the following commands: -```bash -git config --local user.name "USERNAME" -git config --local user.email "USERNAME@SOMETHING.com" -``` +- [Features](#features) +- [Installation](#installation) + - [Requirements](#requirements) + - [Initial Setup](#initial-setup) +- [Development](#development) + - [Commands](#commands) + - [Debugging](#debugging) + - [Mod Debugging](#mod-debugging) +- [Contributing](#contributing) + - [Branches](#branchs) + - [Pull Request Guidelines](#pull-request-guidelines) + - [Tests](#tests) +- [License](#license) -## Requirements +## Features -- NodeJS (with npm) -- Visual Studio Code -- git [LFS](https://git-lfs.github.com/) +For a full list of features, please see [FEATURES.md](FEATURES.md). -## Observations +## Installation -- The server was tested to work with **NodeJS v20.11.1**, if you are using a different version and experiencing difficulties change it before looking for support -- If you are updating a branch you've had for some time, run `npm ci` before running any tasks. This will run the clean and install target from npm. -- You can debug your mods using the server, just copy your mod files into the `user/mods` folder and put breakpoints on the **JS** files. **DO NOT** contact the dev team for support on this. +### Requirements -## Pulling -- Run `git lfs fetch` and `git lfs pull` to acquire loot files +This project has been built in [Visual Studio Code](https://code.visualstudio.com/) (VSC) using [Node.js](https://nodejs.org/). We recommend using [NVM](https://github.com/coreybutler/nvm-windows) to manage installation and switching Node versions. If you do not wish to use NVM, you will need to install the version of Node.js listed within the `.nvmrc` file manually. -## Setup +There are a number of VSC extensions that we recommended for this project. VSC will prompt you to install these when you open the workspace file. If you do not see the prompt, you can install them manually: -1. Visual Studio Code > File > Open Workspace... > `project\Server.code-workspace` -2. Visual Studio Code > Terminal > Run Task... > npm > npm: Install +- [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) - Editor Settings Synchronization +- [Dprint Code Formatter](https://marketplace.visualstudio.com/items?itemName=dprint.dprint) - Formatting on Save +- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - Linting for Coding Issues & Naming Conventions +- [Biome](https://marketplace.visualstudio.com/items?itemName=biomejs.biome) - Linting for Coding Standards +- [Vitest](https://marketplace.visualstudio.com/items?itemName=vitest.explorer) - Debugging Tests +- [SPT ID Highlighter](https://marketplace.visualstudio.com/items?itemName=refringe.spt-id-highlighter) - Converts IDs to Names -## Build -This is for preparing for a release, not to run locally. +### Initial Setup -**Mode** | **Location** --------- | ----------------------------------------------------------------- -release | Visual Studio Code > Terminal > Run Build Task... > build:release -debug | Visual Studio Code > Terminal > Run Build Task... > build:debug +To prepare the project for development you will need to: -## Test / Run locally +1. Run `git clone https://dev.sp-tarkov.com/SPT-AKI/Server.git server` to clone the repository. +2. Run `git lfs pull` to download LFS files locally. +2. Open the `project/mod.code-workspace` file in Visual Studio Code (VSC). +3. Run `nvm use 20.11.1` in the VSC terminal. +4. Run `npm install` in the VSC terminal. -Visual Studio Code > Run > Start Debugging +## Development -# Features +### Commands -## Progression -Player profile is stored in SPT folder as a JSON file, allowing for changes to persist -- Scav: - - Stats increase by doing scav raids - - Skills increase by doing scav raids - - Scav reputation system (Karma) - - Scavs hostile below certain level - - Scav run cooldown adjustment - - Scav follow chance adjustment - - Scav case - - ~~Completion time adjustment~~ NOT IMPLEMENTED - - ~~Equipment chance adjustment~~ NOT IMPLEMENTED - - Bosses hostile below certain level - - ~~Exfil price adjustment~~ NOT IMPLEMENTED - - Improved gear with higher rep - - Increase rep by exiting through car extracts -- PMC: - - Stats increase by doing PMC raids - - Skills increase by doing PMC raids - - Hydration/food - - Increase out of raid - - Post-raid levels are persisted to profile - - Raid stat tracking - - Raid count - - Survived count - - KIA count - - MIA count - - AWOL count - - Kills count +The following commands are available after the initial setup. Run them with `npm run `. -## Bots +| Command | Description | +|--------------------------|----------------------------------------------------------------------| +| `check:circular` | Check for circular dependencies in the project. | +| `lint` | Lint the project for coding standards. | +| `lint:fix` | Attempt to automatically fix coding standard issues. | +| `style` | Check the project for style/formatting issues. | +| `style:fix` | Attempt to automatically fix style/formatting issues. | +| `test` | Run all tests. | +| `test:watch` | Run tests in watch mode. Tests will re-run when files are changed. | +| `test:coverage` | Run tests and generate a coverage report. | +| `test:ui` | Run tests in UI mode. This will open a browser window to view tests. | +| `build:release` | Build the project for release. | +| `build:debug` | Build the project for debugging. | +| `build:bleeding` | Build the project on the bleeding edge. | +| `build:bleedingmods` | Build the project on the bleeding edge with mods. | +| `run:build` | Run the project in build mode. | +| `run:debug` | Run the project in debug mode. | +| `run:profiler` | Run the project in profiler mode. | +| `gen:types` | Generate types for the project. | +| `gen:docs` | Generate documentation for the project. | - - Emulated bots: - - assault (scav) - - bossBully (Reshalla) - - bossGluhar - - bossKilla - - bossKnight - - bossKojainy (Shturman) - - bossSanitar - - bossTagilla - - bossZryachiy - - bossBoar (Kaban) - - bossBoarSniper - - curedAssault - - exUsec (Rogue) - - followerBigPipe - - Grenade launcher - - followerBirdEye - - followerBoar - - followerBully - - followerGluharAssault - - followerGluharScout - - followerGluharSecurity - - followerGluharSnipe - - followerKojaniy - - followerSanitar - - followerzryachiy - - gifter (Santa) - - Gives gifts (partially implemented) - - marksman - - pmcBot (Raider) - - sectantPriest (Cultist) - - sectantWarrior (Cultist) -- Gear - - Semi-randomised gear chosen with weighting system - - Randomised durability of gear -- Ammo - - Ammo weighting system to mimic live -- Loot - - Semi-randomised loot - - Item type spawn limit system -- Per-map AI types +### Debugging -## PMCs -- Simulated PMC players - - Custom weapons - - Semi-randomly generated with weighting system - - Semi-randomly chosen ammo with weighting system - - Custom gear - - Semi-randomly generated with weighting system - - Custom headgear - - Randomised attachments with percentage based chance to appear - - Face shields - - Flashlights - - Randomised AI brains - - Chooses random AI behaviour from pool of possible bot types (e.g. raider/rogue/killa) - - Dogtags - - Random level - - Random name - - Voices - - Bear/usec voices for each faction - - Loot item blacklist/whitelist - - Highly configurable in config - - Level-relative gear for PMCs from levels 1-15 and 15+ - - 1-15 bots have lower-tier items - - 15+ bots have access to anything +To debug the project in Visual Studio Code, you can select the `Run` tab and then select the `Start Debugging` option (or the `F5` shortcut). This will start the server in debug mode, attaching a debugger to code execution, allowing you to set breakpoints and step through the code as it runs. -## Inventory - - Move/split/delete stacks - - Tags (add/modify/remove) - - Armor/weapon kit item repair - - Auto-sort - - Out of raid healing - - Out of raid eating - - Special slots (compass etc) +### Mod Debugging -## Traders -- Buy/Sell -- Listed items are refreshed every hour -- purchase limits per refresh period -- Track sold rouble count -- Loyalty levels -- Build reputation -- Item repair - - Calculate randomised durability level based on item type/values -- Alternate clothing from Ragman - - Buy/unlock new clothing -- Insurance - - chance for items to be returned - higher chance for more expensive trader - - Chance parts will be stripped from returned weapons -- Fence - - Lists random items for sale - - Emulated system of 'churn' for items sold by fence - - every 4 minutes 20% of fences' items are replaced - - Configurable through config +To debug a server mod in Visual Studio Code, you can copy the mod files into the `user/mods` folder and then start the server in [debug mode](#debugging). You should now be able to set breakpoints in the mod's Typescript files and they will be hit when the server runs the mod files. -## Flea market -- Buy and sell items -- Prices pulled from live data -- Listing tax fee -- Offer filtering -- Offer search -- Filter by item -- Linked search -- Simulated player offers - - Generated with random names/ratings/expiry times - - Variable prices based on live price (20% above/below) - - Weapon presets as offers - - Bartering offers - - Listed currency - - Rouble - - Euro - - Dollar -- Rating - - Increase flea rating by selling items - - Decrease flea rating by failing to sell items - - Will be purchased by simulated players - - Greater chance listed item will be purchased the lower it is listed for -- Adjust flea prices that are massively below trader buy price -- Receive purchased item through mail from seller -- Sorting by - - Rating - - Price - - Name -- Configurable using config +## Contributing -## Quests -- ~~Accurate quest list~~ INCOMPLETE (85% complete) -- Trader quests - - Accept/Complete -- Daily Quests - - Simulated system of daily quests - - Replace daily quest - - Replace quest with new one - - Charged fee - - Scav daily quests - - Types - - Elimination - - Exit location - - Find -- Trader item unlocks through completion of quests -- Receive mail from traders after accepting/completing/failing a quest -- Item rewards given through mail +We're really excited that you're interested in contributing! Before submitting your contribution, please consider the following: -## Hideout -- Areas supported - - Air filter - - Air filter degradation speed calculation - - Skill levelling boost + 40% - - Bitcoin farm - - Coin generation speed calculation - - Booze generator - - Create moonshine - - Generator - - Fuel usage calculation - - Heating - - Energy regen rate - - Negative effects removal rate x2 - - Illumination - - Intel centre - - ~~Unlocks scav tasks from fence~~ NOT IMPLEMENTED - unlocks at level 5 - - ~~Reduces insurance return time by 20%~~ NOT IMPLEMENTED - - Quest money reward boost - - Lavatory - - Library - - Medstation - - Nutrition unit - - Rest space - - Scav case - - Custom reward system - - Configurable in config - - Security - - Shooting range - - Solar power - - Stash - - Gives bonus storage space - - Vents - - Water collector - - Workbench - - Christmas tree -- Item crafting - - Found in raid on completion - - Crafts when server not running +### Branchs -## Weapon building -- Create weapon presets -- Saving of presets +- __master__ + The default branch used for the latest stable release. This branch is protected and typically is only merges with release branches. +- __3.9.0-DEV__ + Development for the next minor release of SPT. Minor releases target the latest version of EFT. Late in the minor release cycle the EFT version is frozen for stability to prepare for release. Larger changes to the project structure may be included in minor releases. +- __3.8.1-DEV__ + Development for the next hotfix release of SPT. Hotfix releases include bug fixes and minor features that do not effect the coding structure of the project. Special care is taken to not break server mod stability. These always target the same version of EFT as the last minor release. -## Raids -- Supported maps - - Customs - - Factory day - - Factory night - - Reserve - - Woods - - Lighthouse - - Laboratory - - Shoreline - - Streets -- Loot - - Generated from over 30,000 loot runs on live, spawn chances calculated from all runs to give fairly accurate depiction of live loot. - - Static loot (containers) - - Each container type can contain items appropriate to that type - - Loose loot - - Randomised loose items found on map -- Airdrops - - Randomised chance of spawning - - Fire red flare to request an airdrop - - Drops 'themed' crates: - - Weapons / armor - - Only weapons and armor - - Food / medical - - Only food and medical items - - Barter goods - - Only barter goods - - Mixed - - A mixture of any of the above items - - Drops lootable crate in: - - Customs - - Reserve - - Woods - - Lighthouse - - Shoreline - - Streets - - Can be adjusted via config file -- Raid damage - - Exiting a raid with injury to player character will be persisted out of raid -- Post-raid therapist healing -- Scav Raids - - Adjusted time when running raids as scav - - Simulated loot being taken by other players the later into the raid player starts +### Pull Request Guidelines -## Messages -- Receive from traders -- Pin/unpin senders -- Accept all attachments -- Accept individual mail attachment +- __Keep Them Small__ + If you're fixing a bug, try to keep the changes to the bug fix only. If you're adding a feature, try to keep the changes to the feature only. This will make it easier to review and merge your changes. +- __Perform a Self-Review__ + Before submitting your changes, review your own code. This will help you catch any mistakes you may have made. +- __Remove Noise__ + Remove any unnecessary changes to white space, code style formatting, or some text change that has no impact related to the intention of the PR. +- __Create a Meaningful Title__ + When creating a PR, make sure the title is meaningful and describes the changes you've made. +- __Write Detailed Commit Messages__ + Bring out your table manners, speak the Queen's English and be on your best behaviour. -## Modding -- Extensive system that allows for the modification of nearly any aspect of SPT -- Example mods covering a good slice of modding capabilities +### Style Guide -## Misc -- Profiles - - Standard/Left Behind/Prepare To Escape/Edge Of Darkness - - Custom profiles - - SPT Easy start - - Lots of money / some QoL skills level 20 / level 69 - - SPT Zero to hero - - No money, skills, trader rep or items, only a knife - - SPT Developer - - Testing profile, level 69, most skills maxed, max trader rep - - USEC have all quests ready to start - - BEAR have all quests ready to hand in -- Note system - - Add - - Edit - - Delete -- Extensive config system - - Alter how SPT works -- Holiday themes in hideout on appropriate days - - Halloween - - Christmas +We use Dprint to enforce a consistent code style. Please run `npm run style` and/or `npm run style:fix` before submitting your changes. This is made easier by using the recommended VSC extensions to automatically format your code whenever you save a file. -## Code -- TypeScript - - Majority of EFT request/response classes passed from client to server have been mapped -- Unit Tests - - Supports tests via vitest -- Dependency injection -- Config files accessible from `Aki_Data\Server\configs` / `project\assets\configs` +### Tests + +We have a number of tests that are run automatically when you submit a pull request. You can run these tests locally by running `npm run test`. If you're adding a new feature or fixing a bug, please conceder adding tests to cover your changes so that we can ensure they don't break in the future. + +## License + +This project is licensed under the NCSA Open Source License. See the [LICENSE](LICENSE.md) file for details. diff --git a/project/.vscode/extensions.json b/project/.vscode/extensions.json new file mode 100644 index 00000000..49f98cc2 --- /dev/null +++ b/project/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "EditorConfig.EditorConfig", + "dprint.dprint", + "dbaeumer.vscode-eslint", + "biomejs.biome", + "vitest.explorer", + "refringe.spt-id-highlighter" + ] +} diff --git a/project/Server.code-workspace b/project/Server.code-workspace index 1758f080..5ab13e4e 100644 --- a/project/Server.code-workspace +++ b/project/Server.code-workspace @@ -4,14 +4,6 @@ "path": "." } ], - "extensions": { - "recommendations": [ - "EditorConfig.EditorConfig", - "dprint.dprint", - "dbaeumer.vscode-eslint", - "biomejs.biome" - ] - }, "settings": { "window.title": "SPT-AKI Server", "editor.formatOnSave": true, From ea38e50cca1b7778dd436de2f48bc2ba37b4ed69 Mon Sep 17 00:00:00 2001 From: DrakiaXYZ Date: Thu, 25 Apr 2024 17:46:27 +0000 Subject: [PATCH 14/21] If the trader updateTime is missing min/max use defaults (!307) This is just another catch for people trying to run outdated trader mods, will use default refresh time instead of returning null for the trader update seconds. Lets users know in the console right off the bat that there's a problem with the trader Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Co-authored-by: Refringe Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/307 Co-authored-by: DrakiaXYZ Co-committed-by: DrakiaXYZ --- project/src/helpers/TraderHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/src/helpers/TraderHelper.ts b/project/src/helpers/TraderHelper.ts index 075e57fc..64d2f17d 100644 --- a/project/src/helpers/TraderHelper.ts +++ b/project/src/helpers/TraderHelper.ts @@ -272,7 +272,7 @@ export class TraderHelper public getTraderUpdateSeconds(traderId: string): number { const traderDetails = this.traderConfig.updateTime.find((x) => x.traderId === traderId); - if (!traderDetails) + if (!traderDetails || traderDetails.seconds.min === undefined || traderDetails.seconds.max === undefined) { this.logger.warning( this.localisationService.getText("trader-missing_trader_details_using_default_refresh_time", { From 3bbef3fd0c64a655fa312f0c1842901ba2fcc258 Mon Sep 17 00:00:00 2001 From: Dev Date: Fri, 26 Apr 2024 10:20:33 +0100 Subject: [PATCH 15/21] Added `unheard` gift code --- project/assets/configs/gifts.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/project/assets/configs/gifts.json b/project/assets/configs/gifts.json index d5518189..d5f3629b 100644 --- a/project/assets/configs/gifts.json +++ b/project/assets/configs/gifts.json @@ -3663,6 +3663,23 @@ "associatedEvent": "Promo", "collectionTimeHours": 72, "messageText": "Thank you for purchasing of the first book in EFT series by A, Kontorovich. We are glad to give you this theme package with ingame items." + }, + "UNHEARD": { + "items": [ + { + "_id": "a89275c1b18254ef7432a6d9", + "_tpl": "5696686a4bdc2da3298b456a", + "slotId": "main", + "upd": { + "StackObjectsCount": 250 + }, + "parentId": "64b996c9d0de4697180359b6" + } + ], + "sender": "System", + "messageText": "Have a nice 20 minute adventure in the blatant plagiarist game. In and out", + "collectionTimeHours": 72, + "associatedEvent": "Promo" } } } \ No newline at end of file From dd6d7e192f4b127a138c19ab4c5e329fa6e53317 Mon Sep 17 00:00:00 2001 From: Dev Date: Fri, 26 Apr 2024 14:06:18 +0100 Subject: [PATCH 16/21] Fixed coop exit usage not updating scav profile fence rep value in a similar way to car extracts do --- project/src/controllers/MatchController.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/project/src/controllers/MatchController.ts b/project/src/controllers/MatchController.ts index 4869e4b8..60b69751 100644 --- a/project/src/controllers/MatchController.ts +++ b/project/src/controllers/MatchController.ts @@ -173,7 +173,7 @@ export class MatchController if (extractName && this.extractWasViaCoop(extractName) && this.traderConfig.fence.coopExtractGift.sendGift) { - this.handleCoopExtract(pmcData, extractName); + this.handleCoopExtract(sessionId, pmcData, extractName); this.sendCoopTakenFenceMessage(sessionId); } } @@ -225,10 +225,11 @@ export class MatchController /** * Handle when a player extracts using a coop extract - add rep to fence + * @param sessionId Session/player id * @param pmcData Profile * @param extractName Name of extract taken */ - protected handleCoopExtract(pmcData: IPmcData, extractName: string): void + protected handleCoopExtract(sessionId: string, pmcData: IPmcData, extractName: string): void { if (!pmcData.CoopExtractCounts) { @@ -256,6 +257,11 @@ export class MatchController // Check if new standing has leveled up trader this.traderHelper.lvlUp(fenceId, pmcData); pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1); + + // Copy updated fence rep values into scav profile to ensure consistency + const scavData: IPmcData = this.profileHelper.getScavProfile(sessionId); + scavData.TradersInfo[fenceId].standing = pmcData.TradersInfo[fenceId].standing; + scavData.TradersInfo[fenceId].loyaltyLevel = pmcData.TradersInfo[fenceId].loyaltyLevel; } /** @@ -313,6 +319,7 @@ export class MatchController this.logger.debug( `Car extract: ${extractName} used, total times taken: ${pmcData.CarExtractCounts[extractName]}`, ); + // Copy updated fence rep values into scav profile to ensure consistency const scavData: IPmcData = this.profileHelper.getScavProfile(sessionId); scavData.TradersInfo[fenceId].standing = pmcData.TradersInfo[fenceId].standing; From f566ea8620329751fef0dd9c7df0547f3a4d5df9 Mon Sep 17 00:00:00 2001 From: Dev Date: Fri, 26 Apr 2024 15:54:43 +0100 Subject: [PATCH 17/21] Fixed barter traders that required multiple of a stack removing the entire stack instead --- project/src/services/PaymentService.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/project/src/services/PaymentService.ts b/project/src/services/PaymentService.ts index c4658a74..300197a4 100644 --- a/project/src/services/PaymentService.ts +++ b/project/src/services/PaymentService.ts @@ -65,7 +65,13 @@ export class PaymentService if (!this.paymentHelper.isMoneyTpl(item._tpl)) { // If the item is not money, remove it from the inventory. - this.inventoryHelper.removeItem(pmcData, item._id, sessionID, output); + this.inventoryHelper.removeItemByCount( + pmcData, + item._id, + request.scheme_items[index].count, + sessionID, + output, + ); request.scheme_items[index].count = 0; } else From a2886f102b93207d2d82d01973439e51f29161be Mon Sep 17 00:00:00 2001 From: Dev Date: Fri, 26 Apr 2024 21:21:17 +0100 Subject: [PATCH 18/21] Adjusted scav raid time for streets --- project/assets/configs/location.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/project/assets/configs/location.json b/project/assets/configs/location.json index e518d8c7..640a7d50 100644 --- a/project/assets/configs/location.json +++ b/project/assets/configs/location.json @@ -1293,13 +1293,12 @@ "minStaticLootPercent": 60, "reducedChancePercent": 95, "reductionPercentWeights": { - "20": 2, + "20": 3, "30": 4, - "40": 4, + "40": 5, "50": 4, "60": 4, - "70": 1, - "80": 1 + "70": 1 }, "adjustWaves": true }, From d7a8e1558adfb2a707b53e588459b545633012e7 Mon Sep 17 00:00:00 2001 From: Dev Date: Sat, 27 Apr 2024 10:22:41 +0100 Subject: [PATCH 19/21] Fixed missing `mod_mount` data in PMCs, should reduce chance of seeing low level PMCs with helmet attachments Reduced chance of mount on equipment to 1 for level 1-14 PMCs --- project/assets/configs/bot.json | 3 ++- project/assets/database/bots/types/bear.json | 1 + project/assets/database/bots/types/usec.json | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/project/assets/configs/bot.json b/project/assets/configs/bot.json index 1e4efb5f..f9c916dc 100644 --- a/project/assets/configs/bot.json +++ b/project/assets/configs/bot.json @@ -1013,7 +1013,8 @@ "mod_equipment_000": 3, "mod_equipment_001": 3, "mod_equipment_002": 3, - "mod_nvg": 3 + "mod_nvg": 3, + "mod_mount": 1 }, "weaponMods": { "mod_barrel": 5, diff --git a/project/assets/database/bots/types/bear.json b/project/assets/database/bots/types/bear.json index d135d38e..8b42f5af 100644 --- a/project/assets/database/bots/types/bear.json +++ b/project/assets/database/bots/types/bear.json @@ -80,6 +80,7 @@ "mod_equipment_001": 25, "mod_equipment_002": 25, "mod_nvg": 40, + "mod_mount": 20, "right_side_plate": 75 }, "weaponMods": { diff --git a/project/assets/database/bots/types/usec.json b/project/assets/database/bots/types/usec.json index 983b3201..f14bd29c 100644 --- a/project/assets/database/bots/types/usec.json +++ b/project/assets/database/bots/types/usec.json @@ -77,6 +77,7 @@ "mod_equipment_001": 25, "mod_equipment_002": 25, "mod_nvg": 40, + "mod_mount": 20, "right_side_plate": 75 }, "weaponMods": { From a1e48ca245d7e0104cf23f965df6d09c82fac213 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 27 Apr 2024 16:56:28 +0000 Subject: [PATCH 20/21] Added profile and trader commands to commando (!309) Added new spt commands for commando to alter profile levels and skill, as well as trader rep and money spent. Co-authored-by: clodan Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/309 Co-authored-by: Alex Co-committed-by: Alex --- project/src/di/Container.ts | 12 +- .../Dialogue/AbstractDialogueChatBot.ts | 34 +++- .../{ => GiveCommand}/GiveSptCommand.ts | 11 +- .../{ => GiveCommand}/SavedCommand.ts | 0 .../ProfileCommand/ProfileSptCommand.ts | 163 ++++++++++++++++++ .../TraderCommand/TraderSptCommand.ts | 114 ++++++++++++ .../models/spt/dialog/ISendMessageDetails.ts | 12 +- project/src/services/MailSendService.ts | 16 +- 8 files changed, 348 insertions(+), 14 deletions(-) rename project/src/helpers/Dialogue/Commando/SptCommands/{ => GiveCommand}/GiveSptCommand.ts (96%) rename project/src/helpers/Dialogue/Commando/SptCommands/{ => GiveCommand}/SavedCommand.ts (100%) create mode 100644 project/src/helpers/Dialogue/Commando/SptCommands/ProfileCommand/ProfileSptCommand.ts create mode 100644 project/src/helpers/Dialogue/Commando/SptCommands/TraderCommand/TraderSptCommand.ts diff --git a/project/src/di/Container.ts b/project/src/di/Container.ts index 2fdfde9d..ba2c1223 100644 --- a/project/src/di/Container.ts +++ b/project/src/di/Container.ts @@ -90,7 +90,9 @@ import { BotHelper } from "@spt-aki/helpers/BotHelper"; import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper"; import { ContainerHelper } from "@spt-aki/helpers/ContainerHelper"; import { SptCommandoCommands } from "@spt-aki/helpers/Dialogue/Commando/SptCommandoCommands"; -import { GiveSptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/GiveSptCommand"; +import { GiveSptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand"; +import { ProfileSptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/ProfileCommand/ProfileSptCommand"; +import { TraderSptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/TraderCommand/TraderSptCommand"; import { CommandoDialogueChatBot } from "@spt-aki/helpers/Dialogue/CommandoDialogueChatBot"; import { SptDialogueChatBot } from "@spt-aki/helpers/Dialogue/SptDialogueChatBot"; import { DialogueHelper } from "@spt-aki/helpers/DialogueHelper"; @@ -375,6 +377,8 @@ export class Container // SptCommando Commands depContainer.registerType("SptCommand", "GiveSptCommand"); + depContainer.registerType("SptCommand", "TraderSptCommand"); + depContainer.registerType("SptCommand", "ProfileSptCommand"); } private static registerUtils(depContainer: DependencyContainer): void @@ -598,6 +602,12 @@ export class Container }); // SptCommands depContainer.register("GiveSptCommand", GiveSptCommand, { lifecycle: Lifecycle.Singleton }); + depContainer.register("TraderSptCommand", TraderSptCommand, { + lifecycle: Lifecycle.Singleton, + }); + depContainer.register("ProfileSptCommand", ProfileSptCommand, { + lifecycle: Lifecycle.Singleton, + }); } private static registerLoaders(depContainer: DependencyContainer): void diff --git a/project/src/helpers/Dialogue/AbstractDialogueChatBot.ts b/project/src/helpers/Dialogue/AbstractDialogueChatBot.ts index ed387b97..c5f9b5c1 100644 --- a/project/src/helpers/Dialogue/AbstractDialogueChatBot.ts +++ b/project/src/helpers/Dialogue/AbstractDialogueChatBot.ts @@ -57,12 +57,34 @@ export abstract class AbstractDialogueChatBot implements IDialogueChatBot if (splitCommand[0].toLowerCase() === "help") { - const helpMessage = this.chatCommands.map((c) => - `Available commands:\n\n${c.getCommandPrefix()}:\n\n${ - Array.from(c.getCommands()).map((command) => c.getCommandHelp(command)).join("\n") - }` - ).join("\n"); - this.mailSendService.sendUserMessageToPlayer(sessionId, this.getChatBot(), helpMessage); + this.mailSendService.sendUserMessageToPlayer( + sessionId, + this.getChatBot(), + "The available commands will be listed below:", + ); + // due to BSG being dumb with messages we need a mandatory timeout between messages so they get out on the right order + setTimeout(() => + { + for (const chatCommand of this.chatCommands) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + this.getChatBot(), + `Commands available for "${chatCommand.getCommandPrefix()}" prefix:`, + ); + setTimeout(() => + { + for (const subCommand of chatCommand.getCommands()) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + this.getChatBot(), + `Subcommand ${subCommand}:\n${chatCommand.getCommandHelp(subCommand)}`, + ); + } + }, 1000); + } + }, 1000); return request.dialogId; } diff --git a/project/src/helpers/Dialogue/Commando/SptCommands/GiveSptCommand.ts b/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts similarity index 96% rename from project/src/helpers/Dialogue/Commando/SptCommands/GiveSptCommand.ts rename to project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts index ccab75e2..9bfb7004 100644 --- a/project/src/helpers/Dialogue/Commando/SptCommands/GiveSptCommand.ts +++ b/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts @@ -1,5 +1,5 @@ +import { SavedCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/GiveCommand/SavedCommand"; import { ISptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/ISptCommand"; -import { SavedCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/SavedCommand"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; import { PresetHelper } from "@spt-aki/helpers/PresetHelper"; import { Item } from "@spt-aki/models/eft/common/tables/IItem"; @@ -107,6 +107,15 @@ export class GiveSptCommand implements ISptCommand isItemName = result[5] !== undefined; item = result[5] ? result[5] : result[2]; quantity = +result[6]; + if (quantity <= 0) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + `Invalid quantity! Must be 1 or higher. Use \"help\" for more information.`, + ); + return request.dialogId; + } if (isItemName) { diff --git a/project/src/helpers/Dialogue/Commando/SptCommands/SavedCommand.ts b/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/SavedCommand.ts similarity index 100% rename from project/src/helpers/Dialogue/Commando/SptCommands/SavedCommand.ts rename to project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/SavedCommand.ts diff --git a/project/src/helpers/Dialogue/Commando/SptCommands/ProfileCommand/ProfileSptCommand.ts b/project/src/helpers/Dialogue/Commando/SptCommands/ProfileCommand/ProfileSptCommand.ts new file mode 100644 index 00000000..025a205c --- /dev/null +++ b/project/src/helpers/Dialogue/Commando/SptCommands/ProfileCommand/ProfileSptCommand.ts @@ -0,0 +1,163 @@ +import { SavedCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/GiveCommand/SavedCommand"; +import { ISptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/ISptCommand"; +import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; +import { PresetHelper } from "@spt-aki/helpers/PresetHelper"; +import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper"; +import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest"; +import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile"; +import { SkillTypes } from "@spt-aki/models/enums/SkillTypes"; +import { IProfileChangeEvent, ProfileChangeEventType } from "@spt-aki/models/spt/dialog/ISendMessageDetails"; +import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; +import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; +import { LocaleService } from "@spt-aki/services/LocaleService"; +import { MailSendService } from "@spt-aki/services/MailSendService"; +import { HashUtil } from "@spt-aki/utils/HashUtil"; +import { JsonUtil } from "@spt-aki/utils/JsonUtil"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class ProfileSptCommand implements ISptCommand +{ + /** + * Regex to account for all these cases: + * spt profile level 20 + * spt profile skill metabolism 10 + */ + private static commandRegex = + /^spt profile (?level|skill)((?<=.*skill) (?[\w]+)){0,1} (?(?!0+)[0-9]+)$/; + + protected savedCommand: SavedCommand; + + public constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("ItemHelper") protected itemHelper: ItemHelper, + @inject("HashUtil") protected hashUtil: HashUtil, + @inject("JsonUtil") protected jsonUtil: JsonUtil, + @inject("PresetHelper") protected presetHelper: PresetHelper, + @inject("MailSendService") protected mailSendService: MailSendService, + @inject("LocaleService") protected localeService: LocaleService, + @inject("DatabaseServer") protected databaseServer: DatabaseServer, + @inject("ProfileHelper") protected profileHelper: ProfileHelper, + ) + { + } + + public getCommand(): string + { + return "profile"; + } + + public getCommandHelp(): string + { + return "spt profile\n========\nSets the profile level or skill to the desired level through the message system.\n\n\tspt profile level [desired level]\n\t\tEx: spt profile level 20\n\n\tspt profile skill [skill name] [quantity]\n\t\tEx: spt profile skill metabolism 51"; + } + + public performAction(commandHandler: IUserDialogInfo, sessionId: string, request: ISendMessageRequest): string + { + if (!ProfileSptCommand.commandRegex.test(request.text)) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of trader command. Use \"help\" for more information.", + ); + return request.dialogId; + } + + const result = ProfileSptCommand.commandRegex.exec(request.text); + + const command: string = result.groups.command; + const skill: string = result.groups.skill; + const quantity: number = +result.groups.quantity; + + let event: IProfileChangeEvent; + switch (command) + { + case "level": + if (quantity < 1 || quantity > this.profileHelper.getMaxLevel()) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of profile command, the level was outside bounds: 1 to 70. Use \"help\" for more information.", + ); + return request.dialogId; + } + event = this.handleLevelCommand(quantity); + break; + case "skill": + { + const enumSkill = Object.values(SkillTypes).find((t) => + t.toLocaleLowerCase() === skill.toLocaleLowerCase() + ); + + if (enumSkill === undefined) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of profile command, the skill was not found. Use \"help\" for more information.", + ); + return request.dialogId; + } + + if (quantity < 0 || quantity > 51) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of profile command, the skill level was outside bounds: 1 to 51. Use \"help\" for more information.", + ); + return request.dialogId; + } + event = this.handleSkillCommand(enumSkill, quantity); + break; + } + default: + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + `If you are reading this, this is bad. Please report this to SPT staff with a screenshot. Command ${command}.`, + ); + return request.dialogId; + } + + this.mailSendService.sendSystemMessageToPlayer( + sessionId, + "A single ruble is being attached, required by BSG logic.", + [{ + _id: this.hashUtil.generate(), + _tpl: "5449016a4bdc2d6f028b456f", + upd: { StackObjectsCount: 1 }, + parentId: this.hashUtil.generate(), + slotId: "main", + }], + undefined, + [event], + ); + return request.dialogId; + } + + protected handleSkillCommand(skill: string, level: number): IProfileChangeEvent + { + const event: IProfileChangeEvent = { + _id: this.hashUtil.generate(), + Type: ProfileChangeEventType.SKILL_POINTS, + value: level * 100, + entity: skill, + }; + return event; + } + + protected handleLevelCommand(level: number): IProfileChangeEvent + { + const exp = this.profileHelper.getExperience(level); + const event: IProfileChangeEvent = { + _id: this.hashUtil.generate(), + Type: ProfileChangeEventType.PROFILE_LEVEL, + value: exp, + entity: null, + }; + return event; + } +} diff --git a/project/src/helpers/Dialogue/Commando/SptCommands/TraderCommand/TraderSptCommand.ts b/project/src/helpers/Dialogue/Commando/SptCommands/TraderCommand/TraderSptCommand.ts new file mode 100644 index 00000000..feb3c0b2 --- /dev/null +++ b/project/src/helpers/Dialogue/Commando/SptCommands/TraderCommand/TraderSptCommand.ts @@ -0,0 +1,114 @@ +import { SavedCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/GiveCommand/SavedCommand"; +import { ISptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/ISptCommand"; +import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; +import { PresetHelper } from "@spt-aki/helpers/PresetHelper"; +import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest"; +import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile"; +import { IProfileChangeEvent, ProfileChangeEventType } from "@spt-aki/models/spt/dialog/ISendMessageDetails"; +import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; +import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; +import { LocaleService } from "@spt-aki/services/LocaleService"; +import { MailSendService } from "@spt-aki/services/MailSendService"; +import { HashUtil } from "@spt-aki/utils/HashUtil"; +import { JsonUtil } from "@spt-aki/utils/JsonUtil"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class TraderSptCommand implements ISptCommand +{ + /** + * Regex to account for all these cases: + * spt trader prapor rep 100 + * spt trader mechanic spend 1000000 + */ + private static commandRegex = /^spt trader (?[\w]+) (?rep|spend) (?(?!0+)[0-9]+)$/; + + protected savedCommand: SavedCommand; + + public constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("ItemHelper") protected itemHelper: ItemHelper, + @inject("HashUtil") protected hashUtil: HashUtil, + @inject("JsonUtil") protected jsonUtil: JsonUtil, + @inject("PresetHelper") protected presetHelper: PresetHelper, + @inject("MailSendService") protected mailSendService: MailSendService, + @inject("LocaleService") protected localeService: LocaleService, + @inject("DatabaseServer") protected databaseServer: DatabaseServer, + ) + { + } + + public getCommand(): string + { + return "trader"; + } + + public getCommandHelp(): string + { + return "spt trader\n========\nSets the reputation or money spent to the input quantity through the message system.\n\n\tspt trader [trader] rep [quantity]\n\t\tEx: spt trader prapor rep 2\n\n\tspt trader [trader] spend [quantity]\n\t\tEx: spt trader therapist spend 1000000"; + } + + public performAction(commandHandler: IUserDialogInfo, sessionId: string, request: ISendMessageRequest): string + { + if (!TraderSptCommand.commandRegex.test(request.text)) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of trader command. Use \"help\" for more information.", + ); + return request.dialogId; + } + + const result = TraderSptCommand.commandRegex.exec(request.text); + + const trader: string = result.groups.trader; + const command: string = result.groups.command; + const quantity: number = +result.groups.quantity; + + const dbTrader = Object.values(this.databaseServer.getTables().traders).find((t) => + t.base.nickname.toLocaleLowerCase() === trader.toLocaleLowerCase() + ); + if (dbTrader === undefined) + { + this.mailSendService.sendUserMessageToPlayer( + sessionId, + commandHandler, + "Invalid use of trader command, the trader was not found. Use \"help\" for more information.", + ); + return request.dialogId; + } + let profileChangeEventType: ProfileChangeEventType; + switch (command) + { + case "rep": + profileChangeEventType = ProfileChangeEventType.TRADER_STANDING; + break; + case "spend": + profileChangeEventType = ProfileChangeEventType.TRADER_SALES_SUM; + break; + } + + const event: IProfileChangeEvent = { + _id: this.hashUtil.generate(), + Type: profileChangeEventType, + value: quantity, + entity: dbTrader.base._id, + }; + + this.mailSendService.sendSystemMessageToPlayer( + sessionId, + "A single ruble is being attached, required by BSG logic.", + [{ + _id: this.hashUtil.generate(), + _tpl: "5449016a4bdc2d6f028b456f", + upd: { StackObjectsCount: 1 }, + parentId: this.hashUtil.generate(), + slotId: "main", + }], + undefined, + [event], + ); + return request.dialogId; + } +} diff --git a/project/src/models/spt/dialog/ISendMessageDetails.ts b/project/src/models/spt/dialog/ISendMessageDetails.ts index 1f9f0d05..13d8957e 100644 --- a/project/src/models/spt/dialog/ISendMessageDetails.ts +++ b/project/src/models/spt/dialog/ISendMessageDetails.ts @@ -34,7 +34,17 @@ export interface ISendMessageDetails export interface IProfileChangeEvent { _id: string; - Type: "TraderSalesSum" | "TraderStanding" | "ProfileLevel" | "SkillPoints" | "ExamineAllItems" | "UnlockTrader"; + Type: ProfileChangeEventType; value: number; entity?: string; } + +export enum ProfileChangeEventType +{ + TRADER_SALES_SUM = "TraderSalesSum", + TRADER_STANDING = "TraderStanding", + PROFILE_LEVEL = "ProfileLevel", + SKILL_POINTS = "SkillPoints", + EXAMINE_ALL_ITEMS = "ExamineAllItems", + UNLOCK_TRADER = "UnlockTrader", +} diff --git a/project/src/services/MailSendService.ts b/project/src/services/MailSendService.ts index 2660d9c7..4fbc536a 100644 --- a/project/src/services/MailSendService.ts +++ b/project/src/services/MailSendService.ts @@ -10,7 +10,7 @@ import { Dialogue, IUserDialogInfo, Message, MessageItems } from "@spt-aki/model import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { MessageType } from "@spt-aki/models/enums/MessageType"; import { Traders } from "@spt-aki/models/enums/Traders"; -import { ISendMessageDetails } from "@spt-aki/models/spt/dialog/ISendMessageDetails"; +import { IProfileChangeEvent, ISendMessageDetails } from "@spt-aki/models/spt/dialog/ISendMessageDetails"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { SaveServer } from "@spt-aki/servers/SaveServer"; @@ -169,7 +169,8 @@ export class MailSendService sessionId: string, message: string, items: Item[] = [], - maxStorageTimeSeconds = null, + maxStorageTimeSeconds?: number, + profileChangeEvents?: IProfileChangeEvent[], ): void { const details: ISendMessageDetails = { @@ -185,6 +186,11 @@ export class MailSendService details.itemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds ?? 172800; // 48 hours if no value supplied } + if ((profileChangeEvents?.length ?? 0) > 0) + { + details.profileChangeEvents = profileChangeEvents; + } + this.sendMessageToPlayer(details); } @@ -199,8 +205,8 @@ export class MailSendService sessionId: string, messageLocaleId: string, items: Item[] = [], - profileChangeEvents = [], - maxStorageTimeSeconds = null, + profileChangeEvents?: IProfileChangeEvent[], + maxStorageTimeSeconds?: number, ): void { const details: ISendMessageDetails = { @@ -216,7 +222,7 @@ export class MailSendService details.itemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds ?? 172800; // 48 hours if no value supplied } - if (profileChangeEvents.length > 0) + if ((profileChangeEvents?.length ?? 0) > 0) { details.profileChangeEvents = profileChangeEvents; } From 30ee6de4a27ed14ce74a12787692ebbed94218ae Mon Sep 17 00:00:00 2001 From: Dev Date: Sat, 27 Apr 2024 22:02:06 +0100 Subject: [PATCH 21/21] Added 2 additional PMC responses --- project/assets/database/locales/server/en.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/assets/database/locales/server/en.json b/project/assets/database/locales/server/en.json index bd4c2091..2346aadf 100644 --- a/project/assets/database/locales/server/en.json +++ b/project/assets/database/locales/server/en.json @@ -431,6 +431,7 @@ "pmcresponse-victim_negative_99": "Your computer so bad you get 20fps on streets", "pmcresponse-victim_negative_100": "I bet you installed SAIN and had to remove it cuz you kept getting killed too much", "pmcresponse-victim_negative_101": "What the HECK did you just HECKING say about me, you little Scav? I’ll have you know I graduated top of my class in the USEC corps, and I’ve been involved in numerous secret raids on the {{playerSide}}s, and I have over 300 confirmed kills. I am trained in gorilla warfare and I’m the top sniper in the entire USEC armed forces. You are nothing to me but just another target. I will wipe you the HECK out with precision the likes of which has never been seen before in this raid, mark my HECKING words. You think you can get away with saying that shit to me over the messaging window? Think again, HECKER. As we speak I am contacting my secret network of spies across the Customs location and your stash is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You’re HECKING dead, Scav. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that’s just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the USEC Corps and I will use it to its full extent to wipe your miserable butt off the face of the map, you little poop. If only you could have known what unholy retribution your little “clever” kill was about to bring down upon you, maybe you would have held your HECKING tongue. But you couldn’t, you didn’t, and now you’re paying the price, you HECKING idiot. I will POOP fury all over you and you will drown in it. You’re HECKING dead, Scav.", + "pmcresponse-victim_negative_102": "I bet you bought that new edition just for the bigger pockets", "pmcresponse-victim_plead_1": "I was questing", "pmcresponse-victim_plead_2": "I just wanted to finish a quest, whyd you kill me", "pmcresponse-victim_plead_3": "Hope ur happy i can't even afford a new kit", @@ -529,6 +530,7 @@ "pmcresponse-killer_negative_27": "Easiest loot of today", "pmcresponse-killer_negative_28": "Not to worry, i stashed your gear at your moms house", "pmcresponse-killer_negative_29": "Were you even trying", + "pmcresponse-killer_negative_30": "I bet you actually paid 250 big ones for that new edition", "pmcresponse-killer_plead_1": "I was trying to extract a quest item and you were in my path", "pmcresponse-killer_plead_2": "I was looting barrel caches and you were in the way, sorry", "pmcresponse-killer_plead_3": "I need PMC kills, Im sure you understand",