forked from SpaceStation14-Shenanigans/Monolith
Compare commits
419 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eebcb303f0 | |||
| 2ccf6d95b6 | |||
| 1b953cf4cb | |||
| 9ddc145cc1 | |||
| 8e59b16718 | |||
| bbaeab61b5 | |||
| 97e11f66d1 | |||
| 80ae6a33a5 | |||
| 4a3178b4e2 | |||
| 36faba3835 | |||
| 2c6fe74bca | |||
| 3ce24a6dea | |||
| ff40a7c9ce | |||
| f66007ed82 | |||
| 116b6b5201 | |||
| eceaa24095 | |||
| 51e43c0218 | |||
| d11a891bed | |||
| 53222c7db0 | |||
| f875a93d99 | |||
| 58e585fafb | |||
| 4b371b6f08 | |||
| 61bbc0b04d | |||
| 17376acb2b | |||
| 2a91d09f34 | |||
| 425732c52b | |||
| f2d130f925 | |||
| 5423c7111e | |||
| dce3a56655 | |||
| b4fe165b53 | |||
| 5c883e42d7 | |||
| f99ef195df | |||
| a5d51193cb | |||
| b8cfff831f | |||
| 84b077432d | |||
| 901f1dad81 | |||
| 9b0c66171a | |||
| 8f354a91f8 | |||
| f9dad2c68a | |||
| 730f0758b9 | |||
| 8956b8693a | |||
| 8ec089e18a | |||
| 1415ce6c0c | |||
| 94fcc2457a | |||
| cee68ab5f9 | |||
| 4c6401cf0f | |||
| b1580d2dc0 | |||
| 9cb45515cf | |||
| 6835481c97 | |||
| 236fcf75c5 | |||
| 04a7bb42b5 | |||
| fd6c0569d1 | |||
| 4028405a5a | |||
| 919dc543e0 | |||
| 83b4f9a5d7 | |||
| cbe7f6c3f5 | |||
| 9c9ad7248c | |||
| dfbe98cbbb | |||
| 1dae5d84f5 | |||
| 07c99c87d7 | |||
| 7944589905 | |||
| 690299e366 | |||
| cbef529e94 | |||
| c3f506e6aa | |||
| dc7e3a8b0f | |||
| 00f103890e | |||
| 2b979960ad | |||
| c83930bf2f | |||
| 8287d6a9bc | |||
| 5085871129 | |||
| 17aed77cc0 | |||
| 090b0d49db | |||
| 4099cf5c5f | |||
| 662670d3eb | |||
| ae3ad17500 | |||
| 780bac284c | |||
| 0ec333ebed | |||
| 33552b44a2 | |||
| 87ab827f04 | |||
| a7761b1742 | |||
| 3cdf4cf142 | |||
| ffa0efdb7c | |||
| 2cdb0021ea | |||
| cc94e52caa | |||
| 98e512fbba | |||
| d66474366c | |||
| 30a736ec45 | |||
| 49035ada5c | |||
| 36a752851c | |||
| fcfb8feff6 | |||
| cee5f2a57e | |||
| 6cfedd70f2 | |||
| 57d41c69b2 | |||
| 94dd84d1fd | |||
| e9229f2946 | |||
| d70855f08d | |||
| e7b11673dd | |||
| 7d19340a0a | |||
| 217a647528 | |||
| 9b8604c8e9 | |||
| 50a6912312 | |||
| daf3801a89 | |||
| ebdc1c9f50 | |||
| 5c73fa2e64 | |||
| 5abadca6eb | |||
| 7dbd1fc4ee | |||
| 69a088ab1c | |||
| 11bd89ab29 | |||
| 1c4707ef20 | |||
| e13dd8e284 | |||
| c37fdece10 | |||
| 368f9f66eb | |||
| bb804fb4eb | |||
| 687d0baf38 | |||
| 01c3731dd6 | |||
| 07b89a7ddd | |||
| a12713d824 | |||
| 827ac02e14 | |||
| 643e335430 | |||
| 9fd9c8e0d3 | |||
| 751236b093 | |||
| 546ded6a0f | |||
| 93477eb322 | |||
| 65d50d7b46 | |||
| ceec2eda2f | |||
| 1058c5e8c1 | |||
| aea7d67573 | |||
| 4f26f02318 | |||
| 905757742d | |||
| 9ea1fe9d58 | |||
| 788f30a40d | |||
| 6f6eeef1d2 | |||
| fe335ba114 | |||
| b7c39e31ca | |||
| 7dfbc4441e | |||
| 6ffca1c96d | |||
| f7394c02a3 | |||
| 8afd0a1ac0 | |||
| 51d1122dc2 | |||
| 343c6dd98c | |||
| 17795e629d | |||
| a906504c18 | |||
| e9bd143612 | |||
| 3f9487e507 | |||
| dfb81dd5c4 | |||
| d1218ad15b | |||
| 5d82605e4c | |||
| ff856ddcbc | |||
| c14df33b47 | |||
| aa3aef3786 | |||
| 12ff1257f7 | |||
| 878fe89966 | |||
| bfcad4b309 | |||
| 89b7db6957 | |||
| 6c402241db | |||
| c228375dc1 | |||
| 1be5bc68d5 | |||
| 1122d4d30f | |||
| bc0cd37fb6 | |||
| 182c63c87e | |||
| a16b01982a | |||
| 260e34054d | |||
| 73b055c6cb | |||
| dea71210b0 | |||
| 17289457c0 | |||
| 94d3092c33 | |||
| 66c9bd6455 | |||
| 154f242bb8 | |||
| 1e6bd607fc | |||
| dd467907ec | |||
| 9a8c753491 | |||
| d354f90036 | |||
| a33eb842f7 | |||
| d7ea4b55c4 | |||
| a40ece4b7c | |||
| 0b8062b471 | |||
| dadcdfc692 | |||
| e4177484ca | |||
| 5629674bbb | |||
| 253eb267d2 | |||
| 0650ef5f24 | |||
| cf19d8ed54 | |||
| ff66ed37dd | |||
| e7f615461d | |||
| 4ef14f0dce | |||
| 3296e8d2cd | |||
| 8b78b737cd | |||
| 3f7bfcb78e | |||
| fb1103c457 | |||
| af2d45dd55 | |||
| 560e64c13c | |||
| 5054665057 | |||
| 5fd442171f | |||
| 8894d4e5ff | |||
| eef28959e2 | |||
| 4ea83d389a | |||
| 02f74c26e9 | |||
| c0ea5adf5d | |||
| 6daf9f02e6 | |||
| afabe747f2 | |||
| 699232f653 | |||
| 262aa8cd4b | |||
| bcb9440046 | |||
| 93bd8b8356 | |||
| d14dd88149 | |||
| f520ddf911 | |||
| c2ea1eaf94 | |||
| fc105b61fc | |||
| 271c4e7efc | |||
| a4138f4b11 | |||
| ff74cf3ae1 | |||
| 663425a050 | |||
| b4e4a284bc | |||
| 3a8ac92ef8 | |||
| d3d3ab531e | |||
| 71cf5f42c7 | |||
| fce9e95910 | |||
| ee337596d6 | |||
| a2b3ddecdf | |||
| 0939533dc1 | |||
| 42f6f53be9 | |||
| 2651c00cfd | |||
| a462d09fb9 | |||
| ce617c185e | |||
| 131c5e79a5 | |||
| 86c9a843b1 | |||
| dd910f1550 | |||
| cd8d59c095 | |||
| 20bc7af2a4 | |||
| 65ea89b673 | |||
| 44445f3d2a | |||
| 648cc99aff | |||
| e8c2b72aa0 | |||
| 7d85dbca00 | |||
| 85e0cb1c76 | |||
| 3c32d2f6f5 | |||
| 9d8d0b9016 | |||
| 02e72a4c1d | |||
| 5fac554802 | |||
| bc62bf3700 | |||
| accb1404b0 | |||
| 326440aa22 | |||
| b4fe4ab5e7 | |||
| 8ff52fcd7b | |||
| e8a620e044 | |||
| 9ea80d6405 | |||
| abec847ca3 | |||
| 36214087e9 | |||
| 2b4f4f2ba0 | |||
| 258bc12f16 | |||
| d73cf7aeaf | |||
| b1e200408d | |||
| 95227750d9 | |||
| 34f71b59b9 | |||
| 547420d4bc | |||
| 510434fb65 | |||
| 862ce8352c | |||
| 552ab5718a | |||
| 2bacec206b | |||
| bd8ae0ec9b | |||
| 6ba17c6e1b | |||
| 50d7de7818 | |||
| 02348e780e | |||
| 80cc460d69 | |||
| e95a87a1d8 | |||
| 78ecd13c0a | |||
| 9eb4c8333c | |||
| 882b1acae7 | |||
| df39f7bd6d | |||
| c68d329dd2 | |||
| 5a2341407b | |||
| b4b49016d9 | |||
| 53a40c6c3c | |||
| fbf93d50be | |||
| 68144857e8 | |||
| 820a70c695 | |||
| bff7436490 | |||
| 2b76b0a55a | |||
| fa61e959fb | |||
| b427f087b0 | |||
| 7a8b90339f | |||
| cecc52bff9 | |||
| 378592a0ba | |||
| 89dff2ec03 | |||
| 4faad89b0c | |||
| 8f8b94862a | |||
| 7c29b33d27 | |||
| 8180e5d436 | |||
| e081dd59c6 | |||
| 295ef88a95 | |||
| 39292553b3 | |||
| 2652423c4d | |||
| fb836a0ee7 | |||
| 7a63aba489 | |||
| 7ec10d578c | |||
| 91f4eb7b51 | |||
| e552e2498f | |||
| ef34f255bf | |||
| 0f3390aba8 | |||
| 25033ae9c1 | |||
| d32fd9dd0a | |||
| 728c10974e | |||
| 50c5891b4e | |||
| 51818f6dc3 | |||
| dcaefa16d7 | |||
| 15936634b1 | |||
| 574bb25522 | |||
| 1125715a3f | |||
| 9e3a270988 | |||
| bf1a02bfcb | |||
| 57c59e3684 | |||
| 0e31d3885f | |||
| 27b05a3b75 | |||
| f99e36e101 | |||
| 5a17e7781f | |||
| 98f8d334a1 | |||
| c9bba3367d | |||
| 4ac8f937fb | |||
| 9648f54fc3 | |||
| 33e0bf8293 | |||
| 81ed57e9fb | |||
| 0943abba3e | |||
| 886752cd7a | |||
| f8ba24d3f1 | |||
| 1765c7d506 | |||
| 16f40a5289 | |||
| b80fb6d554 | |||
| e1932935fe | |||
| 5aa06e8fc0 | |||
| 914c5529ec | |||
| f406be9a5d | |||
| 002ba09258 | |||
| f89e18df88 | |||
| a7dcf1afea | |||
| 670785fd48 | |||
| 654a657788 | |||
| 52137b967a | |||
| 8e0bff68cb | |||
| af99be86ed | |||
| 3ed4f7cc1a | |||
| e72a06c19d | |||
| 6619538fb9 | |||
| 005950f2a0 | |||
| 468be1435c | |||
| 685422209e | |||
| ae58c4d021 | |||
| 464975916d | |||
| 2f39b9edce | |||
| a18831a0c5 | |||
| 42dcb56e22 | |||
| 3eeb83a16e | |||
| abf8bd7218 | |||
| c6de815de4 | |||
| 53b02b5830 | |||
| 40d2f70e08 | |||
| 1791e631f7 | |||
| f79b69a3f6 | |||
| b8d5ca7022 | |||
| f37ef8c994 | |||
| b4ae011b87 | |||
| 0adcb3c8a4 | |||
| ad1a263596 | |||
| 591a6ba333 | |||
| ebf2e56159 | |||
| c6b36bf126 | |||
| 2cd30797ac | |||
| 2aad60a3d7 | |||
| f561fcb45e | |||
| a68c0d48ba | |||
| ec2c5a521d | |||
| 39efe6f747 | |||
| 0816b4457c | |||
| 6c87da3dc3 | |||
| 05d4b2eebd | |||
| 6ee1e7d8f3 | |||
| f097148d99 | |||
| ef2ce6565d | |||
| 98ad67497b | |||
| 67644068b6 | |||
| 1b64380dbb | |||
| 465006f16b | |||
| e1ec12fb3e | |||
| d62e3a42d0 | |||
| 02be1ef282 | |||
| 85c4a0c9ec | |||
| baeb53ee82 | |||
| 933dc75c33 | |||
| fc45f016ee | |||
| 6a719f7c39 | |||
| d83db373fe | |||
| ccec7894c6 | |||
| 3319b5f0d3 | |||
| 81d9ae37e3 | |||
| 1990f2a272 | |||
| 3665639420 | |||
| 20e2ceb0db | |||
| 84a116fd27 | |||
| c3c402d380 | |||
| 92e8677e82 | |||
| 65365135e3 | |||
| 62e372c9d2 | |||
| 4279c1977d | |||
| 6406a7cf4f | |||
| 806e22e8b4 | |||
| c2934d0bd7 | |||
| 5316f1fdd9 | |||
| 66fed8d2a2 | |||
| 28e3584a67 | |||
| 78c94ddbc4 | |||
| cef95a03bf | |||
| bd3a35ee64 | |||
| 0b50c919e4 | |||
| afcf994545 | |||
| 5aaee26a09 | |||
| fb3c49bd09 | |||
| 2c381cbcd0 | |||
| f4e3ac2478 | |||
| 31de4af5e2 | |||
| 5e67e63032 |
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Run Benchmarks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Get Engine version
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- name: Check for CRLF
|
||||
run: Tools/check_crlf.py
|
||||
|
||||
@@ -10,18 +10,18 @@ jobs:
|
||||
if: ${{github.head_ref == 'master' || github.head_ref == 'main' || github.head_ref == 'develop'}}
|
||||
|
||||
steps:
|
||||
# - uses: superbrothers/close-pull-request@v3
|
||||
# with:
|
||||
# comment: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch."
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: "Thank you for contributing to the Monolith repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch."
|
||||
|
||||
# If you prefer to just comment on the pr and not close it, uncomment the bellow and comment the above
|
||||
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: ${{ github.event.number }},
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch. \n\n This pr won't be automatically closed. However, a maintainer may close it for this reason."
|
||||
})
|
||||
#- uses: actions/github-script@v7
|
||||
# with:
|
||||
# script: |
|
||||
# github.rest.issues.createComment({
|
||||
# issue_number: ${{ github.event.number }},
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# body: "Thank you for contributing to the Monolith repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name>` and resetting the master branch. \n\n This pr won't be automatically closed. However, a maintainer may close it for this reason."
|
||||
# })
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -35,9 +35,9 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install Dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -13,13 +13,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
|
||||
@@ -15,11 +15,11 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install -y python3-paramiko python3-lxml
|
||||
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Get changed files
|
||||
id: files
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v3.6.0
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
if: github.repository == 'Monolith-Station/Monolith' # Mono: space-wizards/space-station-14 > Monolith-Station/Monolith
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
ref: main
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
name: Validate RSIs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- name: Setup Submodule
|
||||
run: git submodule update --init
|
||||
- name: Pull engine updates
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- name: Setup submodule
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
cd RobustToolbox/
|
||||
git submodule update --init --recursive
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -51,6 +51,8 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
|
||||
//TODO make this adjustable via GUI
|
||||
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
|
||||
var playTime = _config.GetCVar(CCVars.AdminOverlayPlaytime);
|
||||
var startingJob = _config.GetCVar(CCVars.AdminOverlayStartingJob);
|
||||
|
||||
foreach (var playerInfo in _system.PlayerList)
|
||||
{
|
||||
@@ -77,25 +79,50 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
}
|
||||
|
||||
var uiScale = _userInterfaceManager.RootControl.UIScale;
|
||||
var lineoffset = new Vector2(0f, 11f) * uiScale;
|
||||
var lineoffset = new Vector2(0f, 14f) * uiScale;
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
|
||||
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
|
||||
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
|
||||
var balance = playerInfo.Balance == int.MinValue ? "NO BALANCE" : BankSystemExtensions.ToCurrencyString(playerInfo.Balance); // Frontier
|
||||
|
||||
var currentOffset = Vector2.Zero;
|
||||
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
var balance = playerInfo.Balance == int.MinValue
|
||||
? "NO BALANCE"
|
||||
: BankSystemExtensions.ToCurrencyString(playerInfo.Balance);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, $"Balance: {balance}", uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && playTime)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? Color.Orange : Color.White);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && startingJob)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? Color.GreenYellow : Color.White);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
if (classic && playerInfo.Antag)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 3), _antagLabelClassic, uiScale, _antagColorClassic); // Frontier: 2<3
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, _antagLabelClassic, uiScale, Color.OrangeRed);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
else if (!classic && _filter.Contains(playerInfo.RoleProto))
|
||||
{
|
||||
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
|
||||
var color = playerInfo.RoleProto.Color;
|
||||
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 3), label, uiScale, color); // Frontier: 2<3
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, label, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + lineoffset * 2, $"Balance: {balance}", uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White); // Frontier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<Control
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:viewport="clr-namespace:Content.Client.Viewport"
|
||||
MouseFilter="Stop">
|
||||
<PanelContainer StyleClasses="BackgroundDark" Name="AdminCameraWindowRoot" Access="Public">
|
||||
<BoxContainer Orientation="Vertical" Access="Public">
|
||||
<!-- Camera -->
|
||||
<Control VerticalExpand="True" Name="CameraViewBox">
|
||||
<viewport:ScalingViewport Name="CameraView"
|
||||
MinSize="100 100"
|
||||
MouseFilter="Ignore" />
|
||||
</Control>
|
||||
<!-- Controller buttons -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="5 5 5 5">
|
||||
<Button StyleClasses="OpenRight" Name="FollowButton" HorizontalExpand="True" Access="Public" Text="{Loc 'admin-camera-window-follow'}" />
|
||||
<Button StyleClasses="OpenLeft" Name="PopControl" HorizontalExpand="True" Access="Public" Text="{Loc 'admin-camera-window-pop-out'}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,101 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Eye;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminCamera;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminCameraControl : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
|
||||
public event Action? OnFollow;
|
||||
public event Action? OnPopoutControl;
|
||||
|
||||
private readonly EyeLerpingSystem _eyeLerpingSystem;
|
||||
private readonly FixedEye _defaultEye = new();
|
||||
private AdminCameraEuiState? _nextState;
|
||||
|
||||
private const float MinimumZoom = 0.1f;
|
||||
private const float MaximumZoom = 2.0f;
|
||||
|
||||
public EntityUid? CurrentCamera;
|
||||
public float Zoom = 1.0f;
|
||||
|
||||
public bool IsPoppedOut;
|
||||
|
||||
public AdminCameraControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_eyeLerpingSystem = _entManager.System<EyeLerpingSystem>();
|
||||
|
||||
CameraView.Eye = _defaultEye;
|
||||
|
||||
FollowButton.OnPressed += _ => OnFollow?.Invoke();
|
||||
PopControl.OnPressed += _ => OnPopoutControl?.Invoke();
|
||||
CameraView.OnResized += OnResized;
|
||||
}
|
||||
|
||||
private new void OnResized()
|
||||
{
|
||||
var width = Math.Max(CameraView.PixelWidth, (int)Math.Floor(CameraView.MinWidth));
|
||||
var height = Math.Max(CameraView.PixelHeight, (int)Math.Floor(CameraView.MinHeight));
|
||||
|
||||
CameraView.ViewportSize = new Vector2i(width, height);
|
||||
}
|
||||
|
||||
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||
{
|
||||
base.MouseWheel(args);
|
||||
|
||||
if (CameraView.Eye == null)
|
||||
return;
|
||||
|
||||
Zoom = Math.Clamp(Zoom - args.Delta.Y * 0.15f * Zoom, MinimumZoom, MaximumZoom);
|
||||
CameraView.Eye.Zoom = new Vector2(Zoom, Zoom);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
public void SetState(AdminCameraEuiState state)
|
||||
{
|
||||
_nextState = state;
|
||||
}
|
||||
|
||||
// I know that this is awful, but I copied this from the solution editor anyways.
|
||||
// This is needed because EUIs update before the gamestate is applied, which means it will fail to get the uid from the net entity.
|
||||
// The suggestion from the comment in the solution editor saying to use a BUI is not ideal either:
|
||||
// - We would need to bind the UI to an entity, but with how BUIs currently work we cannot open it in the same tick as we spawn that entity on the server.
|
||||
// - We want the UI opened by the user session, not by their currently attached entity. Otherwise it would close in cases where admins move from one entity to another, for example when ghosting.
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
if (_nextState == null || _timing.LastRealTick < _nextState.Tick) // make sure the last gamestate has been applied
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetEntity(_nextState.Camera, out var cameraUid))
|
||||
return;
|
||||
|
||||
if (CurrentCamera == null)
|
||||
{
|
||||
_eyeLerpingSystem.AddEye(cameraUid.Value);
|
||||
CurrentCamera = cameraUid;
|
||||
}
|
||||
else if (CurrentCamera != cameraUid)
|
||||
{
|
||||
_eyeLerpingSystem.RemoveEye(CurrentCamera.Value);
|
||||
_eyeLerpingSystem.AddEye(cameraUid.Value);
|
||||
CurrentCamera = cameraUid;
|
||||
}
|
||||
|
||||
if (_entManager.TryGetComponent<EyeComponent>(CurrentCamera, out var eye))
|
||||
CameraView.Eye = eye.Eye ?? _defaultEye;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminCamera;
|
||||
|
||||
/// <summary>
|
||||
/// Admin Eui for opening a viewport window to observe entities.
|
||||
/// Use the "Open Camera" admin verb or the "camera" command to open.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed partial class AdminCameraEui : BaseEui
|
||||
{
|
||||
private readonly AdminCameraWindow _window;
|
||||
private readonly AdminCameraControl _control;
|
||||
|
||||
// If not null the camera is in "popped out" mode and is in an external window.
|
||||
private OSWindow? _OSWindow;
|
||||
|
||||
// The last location the window was located at in game.
|
||||
// Is used for getting knowing where to "pop in" external windows.
|
||||
private Vector2 _lastLocation;
|
||||
|
||||
public AdminCameraEui()
|
||||
{
|
||||
_window = new AdminCameraWindow();
|
||||
_control = new AdminCameraControl();
|
||||
|
||||
_window.Contents.AddChild(_control);
|
||||
|
||||
_control.OnFollow += () => SendMessage(new AdminCameraFollowMessage());
|
||||
_window.OnClose += () =>
|
||||
{
|
||||
if (!_control.IsPoppedOut)
|
||||
SendMessage(new CloseEuiMessage());
|
||||
};
|
||||
|
||||
_control.OnPopoutControl += () =>
|
||||
{
|
||||
if (_control.IsPoppedOut)
|
||||
PopIn();
|
||||
else
|
||||
PopOut();
|
||||
};
|
||||
}
|
||||
|
||||
// Pop the window out into an external OS window
|
||||
private void PopOut()
|
||||
{
|
||||
_lastLocation = _window.Position;
|
||||
|
||||
// TODO: When there is a way to have a minimum window size, enforce something!
|
||||
_OSWindow = new OSWindow
|
||||
{
|
||||
SetSize = _window.Size,
|
||||
Title = _window.Title ?? Loc.GetString("admin-camera-window-title-placeholder"),
|
||||
};
|
||||
|
||||
_OSWindow.Show();
|
||||
|
||||
if (_OSWindow.Root == null)
|
||||
return;
|
||||
|
||||
_control.Orphan();
|
||||
_OSWindow.Root.AddChild(_control);
|
||||
|
||||
_OSWindow.Closed += () =>
|
||||
{
|
||||
if (_control.IsPoppedOut)
|
||||
SendMessage(new CloseEuiMessage());
|
||||
};
|
||||
|
||||
_control.IsPoppedOut = true;
|
||||
_control.PopControl.Text = Loc.GetString("admin-camera-window-pop-in");
|
||||
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
// Pop the window back into the in game window.
|
||||
private void PopIn()
|
||||
{
|
||||
_control.Orphan();
|
||||
_window.Contents.AddChild(_control);
|
||||
|
||||
_window.Open(_lastLocation);
|
||||
|
||||
_control.IsPoppedOut = false;
|
||||
_control.PopControl.Text = Loc.GetString("admin-camera-window-pop-out");
|
||||
|
||||
_OSWindow?.Close();
|
||||
_OSWindow = null;
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase baseState)
|
||||
{
|
||||
if (baseState is not AdminCameraEuiState state)
|
||||
return;
|
||||
|
||||
_window.SetState(state);
|
||||
_control.SetState(state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc admin-camera-window-title-placeholder}"
|
||||
SetSize="425 550"
|
||||
MinSize="200 225"
|
||||
Name="Window">
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminCamera;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminCameraWindow : DefaultWindow
|
||||
{
|
||||
public AdminCameraWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
ContentsContainer.Margin = new Thickness(5, 0, 5, 0);
|
||||
}
|
||||
|
||||
public void SetState(AdminCameraEuiState state)
|
||||
{
|
||||
Title = Loc.GetString("admin-camera-window-title", ("name", state.Name));
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
<Button Name="FreezeAndMuteToggleButton" Text="{Loc player-panel-freeze-and-mute}" Disabled="True"/>
|
||||
<Button Name="BanButton" Text="{Loc player-panel-ban}" Disabled="True"/>
|
||||
<controls:ConfirmButton Name="RejuvenateButton" Text="{Loc player-panel-rejuvenate}" Disabled="True"/>
|
||||
<Button Name="CameraButton" Text="{Loc player-panel-camera}" Disabled="True"/>
|
||||
</GridContainer>
|
||||
<Button Name="JobWhitelistsButton" Text="{Loc player-panel-job-whitelists}" SetWidth="136" SetHeight="27" Disabled="True"/> <!-- DeltaV: Job whitelists -->
|
||||
</BoxContainer>
|
||||
|
||||
@@ -19,6 +19,7 @@ public sealed partial class PlayerPanel : FancyWindow
|
||||
public event Action<NetUserId?>? OnOpenBans;
|
||||
public event Action<NetUserId?>? OnAhelp;
|
||||
public event Action<string?>? OnKick;
|
||||
public event Action<string?>? OnCamera;
|
||||
public event Action<NetUserId?>? OnOpenBanPanel;
|
||||
public event Action<NetUserId?, bool>? OnWhitelistToggle;
|
||||
public event Action? OnFreezeAndMuteToggle;
|
||||
@@ -40,6 +41,7 @@ public sealed partial class PlayerPanel : FancyWindow
|
||||
UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
|
||||
BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
|
||||
KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
|
||||
CameraButton.OnPressed += _ => OnCamera?.Invoke(TargetUsername);
|
||||
NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
|
||||
ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
|
||||
AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
|
||||
@@ -132,5 +134,6 @@ public sealed partial class PlayerPanel : FancyWindow
|
||||
RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
|
||||
DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
|
||||
JobWhitelistsButton.Disabled = !_adminManager.HasFlag(AdminFlags.Whitelist); // DeltaV
|
||||
CameraButton.Disabled = !_adminManager.CanCommand("camera");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed class PlayerPanelEui : BaseEui
|
||||
PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
|
||||
// Kick command does not support GUIDs
|
||||
PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
|
||||
PlayerPanel.OnCamera += username => _console.ExecuteCommand($"camera \"{username}\"");
|
||||
PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
|
||||
PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
|
||||
PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.AlertLevel;
|
||||
|
||||
public sealed class AlertLevelDisplaySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -21,26 +23,26 @@ public sealed class AlertLevelDisplaySystem : EntitySystem
|
||||
{
|
||||
return;
|
||||
}
|
||||
var layer = args.Sprite.LayerMapReserveBlank(AlertLevelDisplay.Layer);
|
||||
var layer = _sprite.LayerMapReserve((uid, args.Sprite), AlertLevelDisplay.Layer);
|
||||
|
||||
if (args.AppearanceData.TryGetValue(AlertLevelDisplay.Powered, out var poweredObject))
|
||||
{
|
||||
args.Sprite.LayerSetVisible(layer, poweredObject is true);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), layer, poweredObject is true);
|
||||
}
|
||||
|
||||
if (!args.AppearanceData.TryGetValue(AlertLevelDisplay.CurrentLevel, out var level))
|
||||
{
|
||||
args.Sprite.LayerSetState(layer, alertLevelDisplay.AlertVisuals.Values.First());
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), layer, alertLevelDisplay.AlertVisuals.Values.First());
|
||||
return;
|
||||
}
|
||||
|
||||
if (alertLevelDisplay.AlertVisuals.TryGetValue((string) level, out var visual))
|
||||
if (alertLevelDisplay.AlertVisuals.TryGetValue((string)level, out var visual))
|
||||
{
|
||||
args.Sprite.LayerSetState(layer, visual);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), layer, visual);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetState(layer, alertLevelDisplay.AlertVisuals.Values.First());
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), layer, alertLevelDisplay.AlertVisuals.Values.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -64,7 +65,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
visualState = JukeboxVisualState.On;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, visualState, component, sprite);
|
||||
UpdateAppearance((uid, sprite), visualState, component);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, JukeboxComponent component, ref AppearanceChangeEvent args)
|
||||
@@ -78,25 +79,25 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
visualState = JukeboxVisualState.On;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, visualState, component, args.Sprite);
|
||||
UpdateAppearance((uid, args.Sprite), visualState, component);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, JukeboxVisualState visualState, JukeboxComponent component, SpriteComponent sprite)
|
||||
private void UpdateAppearance(Entity<SpriteComponent> entity, JukeboxVisualState visualState, JukeboxComponent component)
|
||||
{
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, entity);
|
||||
|
||||
switch (visualState)
|
||||
{
|
||||
case JukeboxVisualState.On:
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OnState, sprite);
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OnState, entity);
|
||||
break;
|
||||
|
||||
case JukeboxVisualState.Off:
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, entity);
|
||||
break;
|
||||
|
||||
case JukeboxVisualState.Select:
|
||||
PlayAnimation(uid, JukeboxVisualLayers.Base, component.SelectState, 1.0f, sprite);
|
||||
PlayAnimation(entity.Owner, JukeboxVisualLayers.Base, component.SelectState, 1.0f, entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +110,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
if (!_animationPlayer.HasRunningAnimation(uid, state))
|
||||
{
|
||||
var animation = GetAnimation(layer, state, animationTime);
|
||||
sprite.LayerSetVisible(layer, true);
|
||||
_sprite.LayerSetVisible((uid, sprite), layer, true);
|
||||
_animationPlayer.Play(uid, animation, state);
|
||||
}
|
||||
}
|
||||
@@ -133,13 +134,13 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
};
|
||||
}
|
||||
|
||||
private void SetLayerState(JukeboxVisualLayers layer, string? state, SpriteComponent sprite)
|
||||
private void SetLayerState(JukeboxVisualLayers layer, string? state, Entity<SpriteComponent> sprite)
|
||||
{
|
||||
if (string.IsNullOrEmpty(state))
|
||||
return;
|
||||
|
||||
sprite.LayerSetVisible(layer, true);
|
||||
sprite.LayerSetAutoAnimated(layer, true);
|
||||
sprite.LayerSetState(layer, state);
|
||||
_sprite.LayerSetVisible(sprite.AsNullable(), layer, true);
|
||||
_sprite.LayerSetAutoAnimated(sprite.AsNullable(), layer, true);
|
||||
_sprite.LayerSetRsiState(sprite.AsNullable(), layer, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using Content.Client.Botany.Components;
|
||||
using Content.Shared.Botany;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Botany;
|
||||
|
||||
public sealed class PlantHolderVisualizerSystem : VisualizerSystem<PlantHolderVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -17,8 +20,8 @@ public sealed class PlantHolderVisualizerSystem : VisualizerSystem<PlantHolderVi
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.LayerMapReserveBlank(PlantHolderLayers.Plant);
|
||||
sprite.LayerSetVisible(PlantHolderLayers.Plant, false);
|
||||
_sprite.LayerMapReserve((uid, sprite), PlantHolderLayers.Plant);
|
||||
_sprite.LayerSetVisible((uid, sprite), PlantHolderLayers.Plant, false);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, PlantHolderVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
@@ -31,12 +34,12 @@ public sealed class PlantHolderVisualizerSystem : VisualizerSystem<PlantHolderVi
|
||||
{
|
||||
var valid = !string.IsNullOrWhiteSpace(state);
|
||||
|
||||
args.Sprite.LayerSetVisible(PlantHolderLayers.Plant, valid);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PlantHolderLayers.Plant, valid);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
args.Sprite.LayerSetRSI(PlantHolderLayers.Plant, rsi);
|
||||
args.Sprite.LayerSetState(PlantHolderLayers.Plant, state);
|
||||
_sprite.LayerSetRsi((uid, args.Sprite), PlantHolderLayers.Plant, new ResPath(rsi));
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), PlantHolderLayers.Plant, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
||||
[Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -73,11 +74,11 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
||||
{
|
||||
// This will only assign if empty, it won't get overwritten by new depth on multiple calls, which do happen easily
|
||||
buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
||||
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
||||
_sprite.SetDrawDepth((buckledEntity, buckledSprite), strapSprite.DrawDepth - 1);
|
||||
}
|
||||
else if (buckle.OriginalDrawDepth.HasValue)
|
||||
{
|
||||
buckledSprite.DrawDepth = buckle.OriginalDrawDepth.Value;
|
||||
_sprite.SetDrawDepth((buckledEntity, buckledSprite), buckle.OriginalDrawDepth.Value);
|
||||
buckle.OriginalDrawDepth = null;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +102,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
||||
return;
|
||||
|
||||
ent.Comp.OriginalDrawDepth ??= buckledSprite.DrawDepth;
|
||||
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
|
||||
_sprite.SetDrawDepth((ent.Owner, buckledSprite), strapSprite.DrawDepth - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +116,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
||||
if (!ent.Comp.OriginalDrawDepth.HasValue)
|
||||
return;
|
||||
|
||||
buckledSprite.DrawDepth = ent.Comp.OriginalDrawDepth.Value;
|
||||
_sprite.SetDrawDepth((ent.Owner, buckledSprite), ent.Comp.OriginalDrawDepth.Value);
|
||||
ent.Comp.OriginalDrawDepth = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!args.Sprite.LayerMapTryGet(component.Layer, out var fillLayer))
|
||||
if (!SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.Layer, out var fillLayer, false))
|
||||
return;
|
||||
|
||||
var maxFillLevels = component.MaxFillLevels;
|
||||
@@ -68,9 +68,9 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
||||
}
|
||||
if (component.Metamorphic)
|
||||
{
|
||||
if (args.Sprite.LayerMapTryGet(component.BaseLayer, out var baseLayer))
|
||||
if (SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.BaseLayer, out var baseLayer, false))
|
||||
{
|
||||
var hasOverlay = args.Sprite.LayerMapTryGet(component.OverlayLayer, out var overlayLayer);
|
||||
var hasOverlay = SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.OverlayLayer, out var overlayLayer, false);
|
||||
|
||||
if (AppearanceSystem.TryGetData<string>(uid, SolutionContainerVisuals.BaseOverride,
|
||||
out var baseOverride,
|
||||
@@ -80,35 +80,35 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
||||
|
||||
if (reagentProto?.MetamorphicSprite is { } sprite)
|
||||
{
|
||||
args.Sprite.LayerSetSprite(baseLayer, sprite);
|
||||
SpriteSystem.LayerSetSprite((uid, args.Sprite), baseLayer, sprite);
|
||||
if (reagentProto.MetamorphicMaxFillLevels > 0)
|
||||
{
|
||||
args.Sprite.LayerSetVisible(fillLayer, true);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, true);
|
||||
maxFillLevels = reagentProto.MetamorphicMaxFillLevels;
|
||||
fillBaseName = reagentProto.MetamorphicFillBaseName;
|
||||
changeColor = reagentProto.MetamorphicChangeColor;
|
||||
fillSprite = sprite;
|
||||
}
|
||||
else
|
||||
args.Sprite.LayerSetVisible(fillLayer, false);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, false);
|
||||
|
||||
if (hasOverlay)
|
||||
args.Sprite.LayerSetVisible(overlayLayer, false);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), overlayLayer, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetVisible(fillLayer, true);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, true);
|
||||
if (hasOverlay)
|
||||
args.Sprite.LayerSetVisible(overlayLayer, true);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), overlayLayer, true);
|
||||
if (component.MetamorphicDefaultSprite != null)
|
||||
args.Sprite.LayerSetSprite(baseLayer, component.MetamorphicDefaultSprite);
|
||||
SpriteSystem.LayerSetSprite((uid, args.Sprite), baseLayer, component.MetamorphicDefaultSprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetVisible(fillLayer, true);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, true);
|
||||
}
|
||||
|
||||
var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, maxFillLevels + 1);
|
||||
@@ -120,25 +120,25 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
||||
|
||||
var stateName = fillBaseName + closestFillSprite;
|
||||
if (fillSprite != null)
|
||||
args.Sprite.LayerSetSprite(fillLayer, fillSprite);
|
||||
args.Sprite.LayerSetState(fillLayer, stateName);
|
||||
SpriteSystem.LayerSetSprite((uid, args.Sprite), fillLayer, fillSprite);
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), fillLayer, stateName);
|
||||
|
||||
if (changeColor && AppearanceSystem.TryGetData<Color>(uid, SolutionContainerVisuals.Color, out var color, args.Component))
|
||||
args.Sprite.LayerSetColor(fillLayer, color);
|
||||
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, color);
|
||||
else
|
||||
args.Sprite.LayerSetColor(fillLayer, Color.White);
|
||||
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, Color.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (component.EmptySpriteName == null)
|
||||
args.Sprite.LayerSetVisible(fillLayer, false);
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, false);
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetState(fillLayer, component.EmptySpriteName);
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), fillLayer, component.EmptySpriteName);
|
||||
if (changeColor)
|
||||
args.Sprite.LayerSetColor(fillLayer, component.EmptySpriteColor);
|
||||
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, component.EmptySpriteColor);
|
||||
else
|
||||
args.Sprite.LayerSetColor(fillLayer, Color.White);
|
||||
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@ using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Humanoid;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Cuffs;
|
||||
|
||||
public sealed class CuffableSystem : SharedCuffableSystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -22,7 +24,7 @@ public sealed class CuffableSystem : SharedCuffableSystem
|
||||
private void OnCuffableShutdown(EntityUid uid, CuffableComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (TryComp<SpriteComponent>(uid, out var sprite))
|
||||
sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, false);
|
||||
_sprite.LayerSetVisible((uid, sprite), HumanoidVisualLayers.Handcuffs, false);
|
||||
}
|
||||
|
||||
private void OnCuffableHandleState(EntityUid uid, CuffableComponent component, ref ComponentHandleState args)
|
||||
@@ -39,22 +41,22 @@ public sealed class CuffableSystem : SharedCuffableSystem
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
var cuffed = cuffState.NumHandsCuffed > 0;
|
||||
sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, cuffed);
|
||||
_sprite.LayerSetVisible((uid, sprite), HumanoidVisualLayers.Handcuffs, cuffed);
|
||||
|
||||
// if they are not cuffed, that means that we didn't get a valid color,
|
||||
// iconstate, or RSI. that also means we don't need to update the sprites.
|
||||
if (!cuffed)
|
||||
return;
|
||||
sprite.LayerSetColor(HumanoidVisualLayers.Handcuffs, cuffState.Color!.Value);
|
||||
_sprite.LayerSetColor((uid, sprite), HumanoidVisualLayers.Handcuffs, cuffState.Color!.Value);
|
||||
|
||||
if (!Equals(component.CurrentRSI, cuffState.RSI) && cuffState.RSI != null) // we don't want to keep loading the same RSI
|
||||
{
|
||||
component.CurrentRSI = cuffState.RSI;
|
||||
sprite.LayerSetState(HumanoidVisualLayers.Handcuffs, cuffState.IconState, component.CurrentRSI);
|
||||
_sprite.LayerSetRsi((uid, sprite), _sprite.LayerMapGet((uid, sprite), HumanoidVisualLayers.Handcuffs), new ResPath(component.CurrentRSI), cuffState.IconState);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(HumanoidVisualLayers.Handcuffs, cuffState.IconState);
|
||||
_sprite.LayerSetRsiState((uid, sprite), HumanoidVisualLayers.Handcuffs, cuffState.IconState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -85,7 +86,7 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
state = DoorState.Closed;
|
||||
|
||||
if (AppearanceSystem.TryGetData<string>(entity, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
|
||||
UpdateSpriteLayers(args.Sprite, baseRsi);
|
||||
UpdateSpriteLayers((entity.Owner, args.Sprite), baseRsi);
|
||||
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
||||
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
||||
@@ -95,21 +96,21 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
|
||||
private void UpdateAppearanceForDoorState(Entity<DoorComponent> entity, SpriteComponent sprite, DoorState state)
|
||||
{
|
||||
sprite.DrawDepth = state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth;
|
||||
_sprite.SetDrawDepth((entity.Owner, sprite), state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case DoorState.Open:
|
||||
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
|
||||
{
|
||||
sprite.LayerSetState(layer, layerState);
|
||||
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
|
||||
}
|
||||
|
||||
return;
|
||||
case DoorState.Closed:
|
||||
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
|
||||
{
|
||||
sprite.LayerSetState(layer, layerState);
|
||||
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -138,7 +139,7 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSpriteLayers(SpriteComponent sprite, string baseRsi)
|
||||
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string baseRsi)
|
||||
{
|
||||
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
||||
{
|
||||
@@ -146,6 +147,6 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
return;
|
||||
}
|
||||
|
||||
sprite.BaseRSI = res.RSI;
|
||||
_sprite.SetBaseRsi(sprite.AsNullable(), res.RSI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Content.Client.Electrocution;
|
||||
public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<ElectrocutionHUDVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -53,15 +54,12 @@ public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<Electroc
|
||||
private void ShowHUD()
|
||||
{
|
||||
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
|
||||
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
|
||||
while (electrifiedQuery.MoveNext(out var uid, out _, out var appearanceComp, out var spriteComp))
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, appearanceComp))
|
||||
continue;
|
||||
|
||||
if (electrified)
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, true);
|
||||
else
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
_sprite.LayerSetVisible((uid, spriteComp), ElectrifiedLayers.HUD, electrified);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +68,9 @@ public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<Electroc
|
||||
private void RemoveHUD()
|
||||
{
|
||||
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
|
||||
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
|
||||
while (electrifiedQuery.MoveNext(out var uid, out _, out _, out var spriteComp))
|
||||
{
|
||||
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
_sprite.LayerSetVisible((uid, spriteComp), ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,9 +84,6 @@ public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<Electroc
|
||||
return;
|
||||
|
||||
var player = _playerMan.LocalEntity;
|
||||
if (electrified && HasComp<ShowElectrocutionHUDComponent>(player))
|
||||
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, true);
|
||||
else
|
||||
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), ElectrifiedLayers.HUD, electrified && HasComp<ShowElectrocutionHUDComponent>(player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using Content.Shared.Ensnaring;
|
||||
using Content.Shared.Ensnaring.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Ensnaring;
|
||||
|
||||
public sealed class EnsnareableSystem : SharedEnsnareableSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -19,25 +21,25 @@ public sealed class EnsnareableSystem : SharedEnsnareableSystem
|
||||
{
|
||||
base.OnEnsnareInit(ent, ref args);
|
||||
|
||||
if(!TryComp<SpriteComponent>(ent.Owner, out var sprite))
|
||||
if (!TryComp<SpriteComponent>(ent.Owner, out var sprite))
|
||||
return;
|
||||
|
||||
// TODO remove this, this should just be in yaml.
|
||||
sprite.LayerMapReserveBlank(EnsnaredVisualLayers.Ensnared);
|
||||
_sprite.LayerMapReserve((ent.Owner, sprite), EnsnaredVisualLayers.Ensnared);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, EnsnareableComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null || !args.Sprite.LayerMapTryGet(EnsnaredVisualLayers.Ensnared, out var layer))
|
||||
if (args.Sprite == null || !_sprite.LayerMapTryGet((uid, args.Sprite), EnsnaredVisualLayers.Ensnared, out var layer, false))
|
||||
return;
|
||||
|
||||
if (_appearance.TryGetData<bool>(uid, EnsnareableVisuals.IsEnsnared, out var isEnsnared, args.Component))
|
||||
{
|
||||
if (component.Sprite != null)
|
||||
{
|
||||
args.Sprite.LayerSetRSI(layer, component.Sprite);
|
||||
args.Sprite.LayerSetState(layer, component.State);
|
||||
args.Sprite.LayerSetVisible(layer, isEnsnared);
|
||||
_sprite.LayerSetRsi((uid, args.Sprite), layer, new ResPath(component.Sprite));
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), layer, component.State);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), layer, isEnsnared);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Client._NF.Emp.Overlays; // Frontier
|
||||
using Content.Client._Mono.Company; // Mono
|
||||
|
||||
namespace Content.Client.Entry
|
||||
{
|
||||
@@ -75,6 +76,7 @@ namespace Content.Client.Entry
|
||||
[Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!;
|
||||
[Dependency] private readonly TitleWindowManager _titleWindowManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly CompanyManager _companyManager = default!; // Mono
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
@@ -137,6 +139,7 @@ namespace Content.Client.Entry
|
||||
_extendedDisconnectInformation.Initialize();
|
||||
_jobRequirements.Initialize();
|
||||
_playbackMan.Initialize();
|
||||
_companyManager.Initialize();
|
||||
|
||||
//AUTOSCALING default Setup!
|
||||
_configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080);
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Content.Client.Fluids;
|
||||
public sealed class PuddleSystem : SharedPuddleSystem
|
||||
{
|
||||
[Dependency] private readonly IconSmoothSystem _smooth = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -27,7 +28,7 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
|
||||
if (args.AppearanceData.TryGetValue(PuddleVisuals.CurrentVolume, out var volumeObj))
|
||||
{
|
||||
volume = (float) volumeObj;
|
||||
volume = (float)volumeObj;
|
||||
}
|
||||
|
||||
// Update smoothing and sprite based on volume.
|
||||
@@ -35,19 +36,19 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
{
|
||||
if (volume < LowThreshold)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}a");
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), 0, $"{smooth.StateBase}a");
|
||||
_smooth.SetEnabled(uid, false, smooth);
|
||||
}
|
||||
else if (volume < MediumThreshold)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}b");
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), 0, $"{smooth.StateBase}b");
|
||||
_smooth.SetEnabled(uid, false, smooth);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!smooth.Enabled)
|
||||
{
|
||||
args.Sprite.LayerSetState(0, $"{smooth.StateBase}0");
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), 0, $"{smooth.StateBase}0");
|
||||
_smooth.SetEnabled(uid, true, smooth);
|
||||
_smooth.DirtyNeighbours(uid);
|
||||
}
|
||||
@@ -58,12 +59,12 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
|
||||
if (args.AppearanceData.TryGetValue(PuddleVisuals.SolutionColor, out var colorObj))
|
||||
{
|
||||
var color = (Color) colorObj;
|
||||
args.Sprite.Color = color * baseColor;
|
||||
var color = (Color)colorObj;
|
||||
_sprite.SetColor((uid, args.Sprite), color * baseColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.Color *= baseColor;
|
||||
_sprite.SetColor((uid, args.Sprite), args.Sprite.Color * baseColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client.Graphics;
|
||||
|
||||
/// <summary>
|
||||
/// A cache for <see cref="Overlay"/>s to store per-viewport render resources, such as render targets.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data stored in the cache.</typeparam>
|
||||
public sealed class OverlayResourceCache<T> : IDisposable where T : class, IDisposable
|
||||
{
|
||||
private readonly Dictionary<long, CacheEntry> _cache = new();
|
||||
|
||||
/// <summary>
|
||||
/// Get the data for a specific viewport, creating a new entry if necessary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The cached data may be cleared at any time if <see cref="IClydeViewport.ClearCachedResources"/> gets invoked.
|
||||
/// </remarks>
|
||||
/// <param name="viewport">The viewport for which to retrieve cached data.</param>
|
||||
/// <param name="factory">A delegate used to create the cached data, if necessary.</param>
|
||||
public T GetForViewport(IClydeViewport viewport, Func<IClydeViewport, T> factory)
|
||||
{
|
||||
return GetForViewport(viewport, out _, factory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the data for a specific viewport, creating a new entry if necessary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The cached data may be cleared at any time if <see cref="IClydeViewport.ClearCachedResources"/> gets invoked.
|
||||
/// </remarks>
|
||||
/// <param name="viewport">The viewport for which to retrieve cached data.</param>
|
||||
/// <param name="wasCached">True if the data was pulled from cache, false if it was created anew.</param>
|
||||
/// <param name="factory">A delegate used to create the cached data, if necessary.</param>
|
||||
public T GetForViewport(IClydeViewport viewport, out bool wasCached, Func<IClydeViewport, T> factory)
|
||||
{
|
||||
if (_cache.TryGetValue(viewport.Id, out var entry))
|
||||
{
|
||||
wasCached = true;
|
||||
return entry.Data;
|
||||
}
|
||||
|
||||
wasCached = false;
|
||||
|
||||
entry = new CacheEntry
|
||||
{
|
||||
Data = factory(viewport),
|
||||
Viewport = new WeakReference<IClydeViewport>(viewport),
|
||||
};
|
||||
_cache.Add(viewport.Id, entry);
|
||||
|
||||
viewport.ClearCachedResources += ViewportOnClearCachedResources;
|
||||
|
||||
return entry.Data;
|
||||
}
|
||||
|
||||
private void ViewportOnClearCachedResources(ClearCachedViewportResourcesEvent ev)
|
||||
{
|
||||
if (!_cache.Remove(ev.ViewportId, out var entry))
|
||||
{
|
||||
// I think this could theoretically happen if you manually dispose the cache *after* a leaked viewport got
|
||||
// GC'd, but before its ClearCachedResources got invoked.
|
||||
return;
|
||||
}
|
||||
|
||||
entry.Data.Dispose();
|
||||
|
||||
if (ev.Viewport != null)
|
||||
ev.Viewport.ClearCachedResources -= ViewportOnClearCachedResources;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var entry in _cache)
|
||||
{
|
||||
if (entry.Value.Viewport.TryGetTarget(out var viewport))
|
||||
viewport.ClearCachedResources -= ViewportOnClearCachedResources;
|
||||
|
||||
entry.Value.Data.Dispose();
|
||||
}
|
||||
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
private struct CacheEntry
|
||||
{
|
||||
public T Data;
|
||||
public WeakReference<IClydeViewport> Viewport;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Hands.Systems
|
||||
{
|
||||
@@ -30,6 +31,7 @@ namespace Content.Client.Hands.Systems
|
||||
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly StrippableSystem _stripSys = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly ExamineSystem _examine = default!;
|
||||
[Dependency] private readonly DisplacementMapSystem _displacement = default!;
|
||||
|
||||
@@ -330,7 +332,7 @@ namespace Content.Client.Hands.Systems
|
||||
{
|
||||
foreach (var key in revealedLayers)
|
||||
{
|
||||
sprite.RemoveLayer(key);
|
||||
_sprite.RemoveLayer((uid, sprite), key);
|
||||
}
|
||||
|
||||
revealedLayers.Clear();
|
||||
@@ -366,7 +368,7 @@ namespace Content.Client.Hands.Systems
|
||||
continue;
|
||||
}
|
||||
|
||||
var index = sprite.LayerMapReserveBlank(key);
|
||||
var index = _sprite.LayerMapReserve((uid, sprite), key);
|
||||
|
||||
// In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries.
|
||||
if (layerData.RsiPath == null
|
||||
@@ -374,12 +376,12 @@ namespace Content.Client.Hands.Systems
|
||||
&& sprite[index].Rsi == null)
|
||||
{
|
||||
if (TryComp<ItemComponent>(held, out var itemComponent) && itemComponent.RsiPath != null)
|
||||
sprite.LayerSetRSI(index, itemComponent.RsiPath);
|
||||
_sprite.LayerSetRsi((uid, sprite), index, new ResPath(itemComponent.RsiPath));
|
||||
else if (TryComp(held, out SpriteComponent? clothingSprite))
|
||||
sprite.LayerSetRSI(index, clothingSprite.BaseRSI);
|
||||
_sprite.LayerSetRsi((uid, sprite), index, clothingSprite.BaseRSI);
|
||||
}
|
||||
|
||||
sprite.LayerSetData(index, layerData);
|
||||
_sprite.LayerSetData((uid, sprite), index, layerData);
|
||||
|
||||
//Add displacement maps
|
||||
if (handComp.HandDisplacement is not null)
|
||||
|
||||
@@ -24,6 +24,7 @@ using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Players.RateLimiting;
|
||||
using Content.Client._Mono.Company; // Mono
|
||||
|
||||
namespace Content.Client.IoC
|
||||
{
|
||||
@@ -60,6 +61,7 @@ namespace Content.Client.IoC
|
||||
collection.Register<PlayerRateLimitManager>();
|
||||
collection.Register<SharedPlayerRateLimitManager, PlayerRateLimitManager>();
|
||||
collection.Register<TitleWindowManager>();
|
||||
collection.Register<CompanyManager>(); // Mono
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public sealed class AfterLightTargetOverlay : Overlay
|
||||
return;
|
||||
|
||||
var lightOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var lightRes = lightOverlay.GetCachedForViewport(args.Viewport);
|
||||
var bounds = args.WorldBounds;
|
||||
|
||||
// at 1-1 render scale it's mostly fine but at 4x4 it's way too fkn big
|
||||
@@ -38,7 +39,7 @@ public sealed class AfterLightTargetOverlay : Overlay
|
||||
|
||||
var localMatrix =
|
||||
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, newScale);
|
||||
var diff = (lightOverlay.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
|
||||
var diff = (lightRes.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
|
||||
var halfDiff = diff / 2;
|
||||
|
||||
// Pixels -> Metres -> Half distance.
|
||||
@@ -53,7 +54,7 @@ public sealed class AfterLightTargetOverlay : Overlay
|
||||
viewport.LightRenderTarget.Size.Y + halfDiff.Y);
|
||||
|
||||
worldHandle.SetTransform(localMatrix);
|
||||
worldHandle.DrawTextureRectRegion(lightOverlay.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
|
||||
worldHandle.DrawTextureRectRegion(lightRes.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
|
||||
}, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
/// <summary>
|
||||
/// Applies ambient-occlusion to the viewport.
|
||||
/// </summary>
|
||||
public sealed class AmbientOcclusionOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
|
||||
|
||||
private IRenderTexture? _aoTarget;
|
||||
|
||||
// Couldn't figure out a way to avoid this so if you can then please do.
|
||||
private IRenderTexture? _aoStencilTarget;
|
||||
|
||||
public AmbientOcclusionOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
ZIndex = AfterLightTargetOverlay.ContentZIndex + 1;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
/*
|
||||
* tl;dr
|
||||
* - we draw a black square on each "ambient occlusion" entity.
|
||||
* - we blur this.
|
||||
* - We apply it to the viewport.
|
||||
*
|
||||
* We do this while ignoring lighting because it will wash out the actual effect.
|
||||
* In 3D ambient occlusion is more complicated due top having to calculate normals but in 2D
|
||||
* we don't have a concept of depth / corners necessarily.
|
||||
*/
|
||||
|
||||
var viewport = args.Viewport;
|
||||
var mapId = args.MapId;
|
||||
var worldBounds = args.WorldBounds;
|
||||
var worldHandle = args.WorldHandle;
|
||||
var color = Color.FromHex(_cfgManager.GetCVar(CCVars.AmbientOcclusionColor));
|
||||
var distance = _cfgManager.GetCVar(CCVars.AmbientOcclusionDistance);
|
||||
//var color = Color.Red;
|
||||
var target = viewport.RenderTarget;
|
||||
var lightScale = target.Size / (Vector2) viewport.Size;
|
||||
var scale = viewport.RenderScale / (Vector2.One / lightScale);
|
||||
var maps = _entManager.System<SharedMapSystem>();
|
||||
var lookups = _entManager.System<EntityLookupSystem>();
|
||||
var query = _entManager.System<OccluderSystem>();
|
||||
var xformSystem = _entManager.System<SharedTransformSystem>();
|
||||
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
|
||||
|
||||
if (_aoTarget?.Texture.Size != target.Size)
|
||||
{
|
||||
_aoTarget?.Dispose();
|
||||
_aoTarget = _clyde.CreateRenderTarget(target.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "ambient-occlusion-target");
|
||||
}
|
||||
|
||||
if (_aoStencilTarget?.Texture.Size != target.Size)
|
||||
{
|
||||
_aoStencilTarget?.Dispose();
|
||||
_aoStencilTarget = _clyde.CreateRenderTarget(target.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "ambient-occlusion-stencil-target");
|
||||
}
|
||||
|
||||
// Draw the texture data to the texture.
|
||||
args.WorldHandle.RenderInRenderTarget(_aoTarget,
|
||||
() =>
|
||||
{
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
|
||||
var invMatrix = _aoTarget.GetWorldToLocalMatrix(viewport.Eye!, scale);
|
||||
|
||||
foreach (var entry in query.QueryAabb(mapId, worldBounds))
|
||||
{
|
||||
DebugTools.Assert(entry.Component.Enabled);
|
||||
var matrix = xformSystem.GetWorldMatrix(entry.Transform);
|
||||
var localMatrix = Matrix3x2.Multiply(matrix, invMatrix);
|
||||
|
||||
worldHandle.SetTransform(localMatrix);
|
||||
// 4 pixels
|
||||
worldHandle.DrawRect(Box2.UnitCentered.Enlarged(distance / EyeManager.PixelsPerMeter), Color.White);
|
||||
}
|
||||
}, Color.Transparent);
|
||||
|
||||
_clyde.BlurRenderTarget(viewport, _aoTarget, _aoTarget, viewport.Eye!, 14f);
|
||||
|
||||
// Need to do stencilling after blur as it will nuke it.
|
||||
// Draw stencil for the grid so we don't draw in space.
|
||||
args.WorldHandle.RenderInRenderTarget(_aoStencilTarget,
|
||||
() =>
|
||||
{
|
||||
// Don't want lighting affecting it.
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
{
|
||||
var transform = xformSystem.GetWorldMatrix(grid.Owner);
|
||||
var worldToTextureMatrix = Matrix3x2.Multiply(transform, invMatrix);
|
||||
var tiles = maps.GetTilesEnumerator(grid.Owner, grid, worldBounds);
|
||||
worldHandle.SetTransform(worldToTextureMatrix);
|
||||
while (tiles.MoveNext(out var tileRef))
|
||||
{
|
||||
if (tileRef.IsSpace(_tileDefManager))
|
||||
continue;
|
||||
|
||||
var bounds = lookups.GetLocalBounds(tileRef, grid.TileSize);
|
||||
worldHandle.DrawRect(bounds, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
}, Color.Transparent);
|
||||
|
||||
// Draw the stencil texture to depth buffer.
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_aoStencilTarget!.Texture, worldBounds);
|
||||
|
||||
// Draw the Blurred AO texture finally.
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilEqualDraw").Instance());
|
||||
worldHandle.DrawTextureRect(_aoTarget!.Texture, worldBounds, color);
|
||||
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilClear").Instance());
|
||||
worldHandle.DrawRect(worldBounds, Color.White);
|
||||
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
args.WorldHandle.UseShader(null);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Graphics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
@@ -13,7 +13,8 @@ public sealed class BeforeLightTargetOverlay : Overlay
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
|
||||
public IRenderTexture EnlargedLightTarget = default!;
|
||||
private readonly OverlayResourceCache<CachedResources> _resources = new();
|
||||
|
||||
public Box2Rotated EnlargedBounds;
|
||||
|
||||
/// <summary>
|
||||
@@ -36,16 +37,42 @@ public sealed class BeforeLightTargetOverlay : Overlay
|
||||
var size = args.Viewport.LightRenderTarget.Size + (int) (_skirting * EyeManager.PixelsPerMeter);
|
||||
EnlargedBounds = args.WorldBounds.Enlarged(_skirting / 2f);
|
||||
|
||||
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
|
||||
|
||||
// This just exists to copy the lightrendertarget and write back to it.
|
||||
if (EnlargedLightTarget?.Size != size)
|
||||
if (res.EnlargedLightTarget?.Size != size)
|
||||
{
|
||||
EnlargedLightTarget = _clyde
|
||||
res.EnlargedLightTarget = _clyde
|
||||
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-copy");
|
||||
}
|
||||
|
||||
args.WorldHandle.RenderInRenderTarget(EnlargedLightTarget,
|
||||
args.WorldHandle.RenderInRenderTarget(res.EnlargedLightTarget,
|
||||
() =>
|
||||
{
|
||||
}, _clyde.GetClearColor(args.MapUid));
|
||||
}
|
||||
|
||||
internal CachedResources GetCachedForViewport(IClydeViewport viewport)
|
||||
{
|
||||
return _resources.GetForViewport(viewport,
|
||||
static _ => throw new InvalidOperationException(
|
||||
"Expected BeforeLightTargetOverlay to have created its resources"));
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_resources.Dispose();
|
||||
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
internal sealed class CachedResources : IDisposable
|
||||
{
|
||||
public IRenderTexture EnlargedLightTarget = default!;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
EnlargedLightTarget?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Content.Client.Light.EntitySystems;
|
||||
|
||||
public sealed class EmergencyLightSystem : VisualizerSystem<EmergencyLightComponent>
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, EmergencyLightComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
@@ -14,13 +16,13 @@ public sealed class EmergencyLightSystem : VisualizerSystem<EmergencyLightCompon
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, EmergencyLightVisuals.On, out var on, args.Component))
|
||||
on = false;
|
||||
|
||||
args.Sprite.LayerSetVisible(EmergencyLightVisualLayers.LightOff, !on);
|
||||
args.Sprite.LayerSetVisible(EmergencyLightVisualLayers.LightOn, on);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), EmergencyLightVisualLayers.LightOff, !on);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), EmergencyLightVisualLayers.LightOn, on);
|
||||
|
||||
if (AppearanceSystem.TryGetData<Color>(uid, EmergencyLightVisuals.Color, out var color, args.Component))
|
||||
{
|
||||
args.Sprite.LayerSetColor(EmergencyLightVisualLayers.LightOn, color);
|
||||
args.Sprite.LayerSetColor(EmergencyLightVisualLayers.LightOff, color);
|
||||
_sprite.LayerSetColor((uid, args.Sprite), EmergencyLightVisualLayers.LightOn, color);
|
||||
_sprite.LayerSetColor((uid, args.Sprite), EmergencyLightVisualLayers.LightOff, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Content.Client.Light.Visualizers;
|
||||
|
||||
public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
@@ -16,13 +18,13 @@ public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
|
||||
switch (state)
|
||||
{
|
||||
case LightBulbState.Normal:
|
||||
args.Sprite.LayerSetState(LightBulbVisualLayers.Base, comp.NormalSpriteState);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
|
||||
break;
|
||||
case LightBulbState.Broken:
|
||||
args.Sprite.LayerSetState(LightBulbVisualLayers.Base, comp.BrokenSpriteState);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
|
||||
break;
|
||||
case LightBulbState.Burned:
|
||||
args.Sprite.LayerSetState(LightBulbVisualLayers.Base, comp.BurnedSpriteState);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +32,7 @@ public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
|
||||
// also update sprites color
|
||||
if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
|
||||
{
|
||||
args.Sprite.Color = color;
|
||||
_sprite.SetColor((uid, args.Sprite), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,51 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Light.EntitySystems;
|
||||
|
||||
public sealed class PlanetLightSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Enables / disables the ambient occlusion overlay.
|
||||
/// </summary>
|
||||
public bool AmbientOcclusion
|
||||
{
|
||||
get => _ambientOcclusion;
|
||||
set
|
||||
{
|
||||
if (_ambientOcclusion == value)
|
||||
return;
|
||||
|
||||
_ambientOcclusion = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
_overlayMan.AddOverlay(new AmbientOcclusionOverlay());
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayMan.RemoveOverlay<AmbientOcclusionOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _ambientOcclusion;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GetClearColorEvent>(OnClearColor);
|
||||
|
||||
_cfgManager.OnValueChanged(CCVars.AmbientOcclusion, val =>
|
||||
{
|
||||
AmbientOcclusion = val;
|
||||
}, true);
|
||||
|
||||
_overlayMan.AddOverlay(new BeforeLightTargetOverlay());
|
||||
_overlayMan.AddOverlay(new RoofOverlay(EntityManager));
|
||||
_overlayMan.AddOverlay(new TileEmissionOverlay(EntityManager));
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_appearance.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, args.Component))
|
||||
if (!_appearance.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, args.Component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Graphics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
@@ -15,7 +16,7 @@ public sealed class LightBlurOverlay : Overlay
|
||||
|
||||
public const int ContentZIndex = TileEmissionOverlay.ContentZIndex + 1;
|
||||
|
||||
private IRenderTarget? _blurTarget;
|
||||
private readonly OverlayResourceCache<CachedResources> _resources = new();
|
||||
|
||||
public LightBlurOverlay()
|
||||
{
|
||||
@@ -29,16 +30,36 @@ public sealed class LightBlurOverlay : Overlay
|
||||
return;
|
||||
|
||||
var beforeOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var size = beforeOverlay.EnlargedLightTarget.Size;
|
||||
var beforeLightRes = beforeOverlay.GetCachedForViewport(args.Viewport);
|
||||
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
|
||||
|
||||
if (_blurTarget?.Size != size)
|
||||
var size = beforeLightRes.EnlargedLightTarget.Size;
|
||||
|
||||
if (res.BlurTarget?.Size != size)
|
||||
{
|
||||
_blurTarget = _clyde
|
||||
res.BlurTarget = _clyde
|
||||
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-blur");
|
||||
}
|
||||
|
||||
var target = beforeOverlay.EnlargedLightTarget;
|
||||
var target = beforeLightRes.EnlargedLightTarget;
|
||||
// Yeah that's all this does keep walkin.
|
||||
_clyde.BlurRenderTarget(args.Viewport, target, _blurTarget, args.Viewport.Eye, 14f * 5f);
|
||||
_clyde.BlurRenderTarget(args.Viewport, target, res.BlurTarget, args.Viewport.Eye, 14f * 5f);
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_resources.Dispose();
|
||||
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private sealed class CachedResources : IDisposable
|
||||
{
|
||||
public IRenderTarget? BlurTarget;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BlurTarget?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public sealed class RoofOverlay : Overlay
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (args.Viewport.Eye == null)
|
||||
if (args.Viewport.Eye == null || !_entManager.HasComponent<MapLightComponent>(args.MapUid))
|
||||
return;
|
||||
|
||||
var viewport = args.Viewport;
|
||||
@@ -52,37 +52,23 @@ public sealed class RoofOverlay : Overlay
|
||||
var worldHandle = args.WorldHandle;
|
||||
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var bounds = lightoverlay.EnlargedBounds;
|
||||
var target = lightoverlay.EnlargedLightTarget;
|
||||
var target = lightoverlay.GetCachedForViewport(viewport).EnlargedLightTarget;
|
||||
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, bounds, ref _grids);
|
||||
|
||||
for (var i = _grids.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var grid = _grids[i];
|
||||
|
||||
if (_entManager.HasComponent<RoofComponent>(grid.Owner))
|
||||
continue;
|
||||
|
||||
_grids.RemoveAt(i);
|
||||
}
|
||||
|
||||
if (_grids.Count == 0)
|
||||
return;
|
||||
|
||||
_mapManager.FindGridsIntersecting(args.MapId, bounds, ref _grids, approx: true, includeMap: true);
|
||||
var lightScale = viewport.LightRenderTarget.Size / (Vector2) viewport.Size;
|
||||
var scale = viewport.RenderScale / (Vector2.One / lightScale);
|
||||
|
||||
worldHandle.RenderInRenderTarget(target,
|
||||
() =>
|
||||
{
|
||||
var invMatrix = target.GetWorldToLocalMatrix(eye, scale);
|
||||
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(grid.Owner, out RoofComponent? roof))
|
||||
continue;
|
||||
|
||||
var invMatrix = target.GetWorldToLocalMatrix(eye, scale);
|
||||
|
||||
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Graphics;
|
||||
using Content.Shared.Light.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
@@ -11,6 +12,8 @@ namespace Content.Client.Light;
|
||||
|
||||
public sealed class SunShadowOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> MixShader = "Mix";
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
@@ -22,8 +25,7 @@ public sealed class SunShadowOverlay : Overlay
|
||||
|
||||
private readonly HashSet<Entity<SunShadowCastComponent>> _shadows = new();
|
||||
|
||||
private IRenderTexture? _blurTarget;
|
||||
private IRenderTexture? _target;
|
||||
private readonly OverlayResourceCache<CachedResources> _resources = new();
|
||||
|
||||
public SunShadowOverlay()
|
||||
{
|
||||
@@ -53,16 +55,18 @@ public sealed class SunShadowOverlay : Overlay
|
||||
var worldBounds = args.WorldBounds;
|
||||
var targetSize = viewport.LightRenderTarget.Size;
|
||||
|
||||
if (_target?.Size != targetSize)
|
||||
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
|
||||
|
||||
if (res.Target?.Size != targetSize)
|
||||
{
|
||||
_target = _clyde
|
||||
res.Target = _clyde
|
||||
.CreateRenderTarget(targetSize,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
|
||||
name: "sun-shadow-target");
|
||||
|
||||
if (_blurTarget?.Size != targetSize)
|
||||
if (res.BlurTarget?.Size != targetSize)
|
||||
{
|
||||
_blurTarget = _clyde
|
||||
res.BlurTarget = _clyde
|
||||
.CreateRenderTarget(targetSize, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "sun-shadow-blur");
|
||||
}
|
||||
}
|
||||
@@ -91,11 +95,11 @@ public sealed class SunShadowOverlay : Overlay
|
||||
_shadows.Clear();
|
||||
|
||||
// Draw shadow polys to stencil
|
||||
args.WorldHandle.RenderInRenderTarget(_target,
|
||||
args.WorldHandle.RenderInRenderTarget(res.Target,
|
||||
() =>
|
||||
{
|
||||
var invMatrix =
|
||||
_target.GetWorldToLocalMatrix(eye, scale);
|
||||
res.Target.GetWorldToLocalMatrix(eye, scale);
|
||||
var indices = new Vector2[PhysicsConstants.MaxPolygonVertices * 2];
|
||||
|
||||
// Go through shadows in range.
|
||||
@@ -114,7 +118,9 @@ public sealed class SunShadowOverlay : Overlay
|
||||
foreach (var ent in _shadows)
|
||||
{
|
||||
var xform = _entManager.GetComponent<TransformComponent>(ent.Owner);
|
||||
var worldMatrix = _xformSys.GetWorldMatrix(xform);
|
||||
var (worldPos, worldRot) = _xformSys.GetWorldPositionRotation(xform);
|
||||
// Need no rotation on matrix as sun shadow direction doesn't care.
|
||||
var worldMatrix = Matrix3x2.CreateTranslation(worldPos);
|
||||
var renderMatrix = Matrix3x2.Multiply(worldMatrix, invMatrix);
|
||||
var pointCount = ent.Comp.Points.Length;
|
||||
|
||||
@@ -122,6 +128,10 @@ public sealed class SunShadowOverlay : Overlay
|
||||
|
||||
for (var i = 0; i < pointCount; i++)
|
||||
{
|
||||
// Update point based on entity rotation.
|
||||
indices[i] = worldRot.RotateVec(indices[i]);
|
||||
|
||||
// Add the offset point by the sun shadow direction.
|
||||
indices[pointCount + i] = indices[i] + direction;
|
||||
}
|
||||
|
||||
@@ -134,7 +144,7 @@ public sealed class SunShadowOverlay : Overlay
|
||||
Color.Transparent);
|
||||
|
||||
// Slightly blur it just to avoid aliasing issues on the later viewport-wide blur.
|
||||
_clyde.BlurRenderTarget(viewport, _target, _target, eye, 1f);
|
||||
_clyde.BlurRenderTarget(viewport, res.Target, res.BlurTarget!, eye, 1f);
|
||||
|
||||
// Draw stencil (see roofoverlay).
|
||||
args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
|
||||
@@ -144,11 +154,30 @@ public sealed class SunShadowOverlay : Overlay
|
||||
viewport.LightRenderTarget.GetWorldToLocalMatrix(eye, scale);
|
||||
worldHandle.SetTransform(invMatrix);
|
||||
|
||||
var maskShader = _protoManager.Index<ShaderPrototype>("Mix").Instance();
|
||||
var maskShader = _protoManager.Index(MixShader).Instance();
|
||||
worldHandle.UseShader(maskShader);
|
||||
|
||||
worldHandle.DrawTextureRect(_target.Texture, worldBounds, Color.Black.WithAlpha(alpha));
|
||||
worldHandle.DrawTextureRect(res.Target.Texture, worldBounds, Color.Black.WithAlpha(alpha));
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_resources.Dispose();
|
||||
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private sealed class CachedResources : IDisposable
|
||||
{
|
||||
public IRenderTexture? BlurTarget;
|
||||
public IRenderTexture? Target;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BlurTarget?.Dispose();
|
||||
Target?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public sealed class TileEmissionOverlay : Overlay
|
||||
var worldHandle = args.WorldHandle;
|
||||
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var bounds = lightoverlay.EnlargedBounds;
|
||||
var target = lightoverlay.EnlargedLightTarget;
|
||||
var target = lightoverlay.GetCachedForViewport(args.Viewport).EnlargedLightTarget;
|
||||
var viewport = args.Viewport;
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(mapId, bounds, ref _grids, approx: true);
|
||||
|
||||
@@ -12,6 +12,7 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -28,16 +29,16 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
|
||||
return;
|
||||
|
||||
if (comp.SpriteStateMap.TryGetValue(state, out var spriteState))
|
||||
args.Sprite.LayerSetState(PoweredLightLayers.Base, spriteState);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), PoweredLightLayers.Base, spriteState);
|
||||
|
||||
if (args.Sprite.LayerExists(PoweredLightLayers.Glow))
|
||||
if (_sprite.LayerExists((uid, args.Sprite), PoweredLightLayers.Glow))
|
||||
{
|
||||
if (TryComp<PointLightComponent>(uid, out var light))
|
||||
{
|
||||
args.Sprite.LayerSetColor(PoweredLightLayers.Glow, light.Color);
|
||||
_sprite.LayerSetColor((uid, args.Sprite), PoweredLightLayers.Glow, light.Color);
|
||||
}
|
||||
|
||||
args.Sprite.LayerSetVisible(PoweredLightLayers.Glow, state == PoweredLightState.On);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PoweredLightLayers.Glow, state == PoweredLightState.On);
|
||||
}
|
||||
|
||||
SetBlinkingAnimation(
|
||||
@@ -55,8 +56,7 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
|
||||
{
|
||||
if (args.Key != PoweredLightVisualsComponent.BlinkingAnimationKey)
|
||||
return;
|
||||
|
||||
if(!comp.IsBlinking)
|
||||
if (!comp.IsBlinking)
|
||||
return;
|
||||
|
||||
AnimationSystem.Play(uid, Comp<AnimationPlayerComponent>(uid), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Client._Mono.Company; // Mono
|
||||
using Content.Client._Mono.MonoCoins;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Humanoid;
|
||||
@@ -41,6 +42,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly JobRequirementsManager _requirements = default!;
|
||||
[Dependency] private readonly MarkingManager _markings = default!;
|
||||
[Dependency] private readonly CompanyManager _companyManager = default!; // Mono
|
||||
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||
@@ -350,7 +352,8 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
_prototypeManager,
|
||||
_resourceCache,
|
||||
_requirements,
|
||||
_markings);
|
||||
_markings,
|
||||
_companyManager); // Mono
|
||||
|
||||
_profileEditor.OnOpenGuidebook += _guide.OpenHelp;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using Content.Client._Mono.Company;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
@@ -52,6 +53,7 @@ namespace Content.Client.Lobby.UI
|
||||
private readonly JobRequirementsManager _requirements;
|
||||
private readonly LobbyUIController _controller;
|
||||
private readonly EntityWhitelistSystem _whitelist; // Frontier
|
||||
private readonly CompanyManager _companyManager; // Mono
|
||||
|
||||
private FlavorText.FlavorText? _flavorText;
|
||||
private TextEdit? _flavorTextEdit;
|
||||
@@ -116,7 +118,8 @@ namespace Content.Client.Lobby.UI
|
||||
IPrototypeManager prototypeManager,
|
||||
IResourceManager resManager,
|
||||
JobRequirementsManager requirements,
|
||||
MarkingManager markings)
|
||||
MarkingManager markings,
|
||||
CompanyManager manager) // Mono
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_sawmill = logManager.GetSawmill("profile.editor");
|
||||
@@ -127,6 +130,7 @@ namespace Content.Client.Lobby.UI
|
||||
_prototypeManager = prototypeManager;
|
||||
_markingManager = markings;
|
||||
_preferencesManager = preferencesManager;
|
||||
_companyManager = manager; // Mono
|
||||
_resManager = resManager;
|
||||
_requirements = requirements;
|
||||
_controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
@@ -446,12 +450,9 @@ namespace Content.Client.Lobby.UI
|
||||
// Clear any existing items
|
||||
CompanyButton.Clear();
|
||||
|
||||
var username = _playerManager.LocalPlayer?.Session?.Name; //Lua modified - company login support
|
||||
|
||||
// Add all companies from prototypes - use consistent sorting with UpdateCompanyControls
|
||||
var companies = _prototypeManager.EnumeratePrototypes<CompanyPrototype>()
|
||||
//.Where(c => !c.Disabled) // Filter out disabled companies
|
||||
.Where(c => !c.Disabled || (username != null && c.Logins.Contains(username))) //Lua modified - company login support
|
||||
.Where(c => !c.Disabled && _companyManager.IsAllowed(c.ID))
|
||||
.ToList();
|
||||
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
@@ -1451,7 +1452,7 @@ namespace Content.Client.Lobby.UI
|
||||
|
||||
_loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
|
||||
{
|
||||
Title = jobProto?.ID + "-loadout",
|
||||
Title = jobProto?.LocalizedName + " loadout", // Mono - replace the "-" with a space in "-loadout", change to LocalizedName from ID
|
||||
};
|
||||
|
||||
// Refresh the buttons etc.
|
||||
@@ -2194,11 +2195,8 @@ namespace Content.Client.Lobby.UI
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var username = _playerManager.LocalPlayer?.Session?.Name; //Lua modified - company login support
|
||||
|
||||
var companies = _prototypeManager.EnumeratePrototypes<CompanyPrototype>()
|
||||
//.Where(c => !c.Disabled) // Filter out disabled companies
|
||||
.Where(c => !c.Disabled || (username != null && c.Logins.Contains(username))) //Lua modified - company login support
|
||||
.Where(c => !c.Disabled && _companyManager.IsAllowed(c.ID))
|
||||
.ToList();
|
||||
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<ui:OptionDropDown Name="DropDownLightingQuality" Title="{Loc 'ui-options-lighting-label'}" />
|
||||
<CheckBox Name="ViewportLowResCheckBox" Text="{Loc 'ui-options-vp-low-res'}" />
|
||||
<CheckBox Name="ParallaxLowQualityCheckBox" Text="{Loc 'ui-options-parallax-low-quality'}" />
|
||||
<CheckBox Name="AmbientOcclusionCheckBox" Text="{Loc 'ui-options-ambient-occlusion'}" />
|
||||
|
||||
<!-- Interface -->
|
||||
<Label Text="{Loc 'ui-options-interface-label'}" StyleClasses="LabelKeyText"/>
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed partial class GraphicsTab : Control
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Control.AddOptionCheckBox(CVars.DisplayVSync, VSyncCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.AmbientOcclusion, AmbientOcclusionCheckBox);
|
||||
Control.AddOption(new OptionFullscreen(Control, _cfg, FullscreenCheckBox));
|
||||
Control.AddOption(new OptionLightingQuality(Control, _cfg, DropDownLightingQuality));
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@ namespace Content.Client.Overlays;
|
||||
|
||||
public sealed partial class StencilOverlay
|
||||
{
|
||||
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3x2 invMatrix)
|
||||
private void DrawRestrictedRange(
|
||||
in OverlayDrawArgs args,
|
||||
CachedResources res,
|
||||
RestrictedRangeComponent rangeComp,
|
||||
Matrix3x2 invMatrix)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var renderScale = args.Viewport.RenderScale.X;
|
||||
@@ -38,20 +42,20 @@ public sealed partial class StencilOverlay
|
||||
// Cut out the irrelevant bits via stencil
|
||||
// This is why we don't just use parallax; we might want specific tiles to get drawn over
|
||||
// particularly for planet maps or stations.
|
||||
worldHandle.RenderInRenderTarget(_blep!, () =>
|
||||
worldHandle.RenderInRenderTarget(res.Blep!, () =>
|
||||
{
|
||||
worldHandle.UseShader(_shader);
|
||||
worldHandle.DrawRect(localAABB, Color.White);
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
|
||||
worldHandle.DrawTextureRect(res.Blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
var sprite = _sprite.GetFrame(new SpriteSpecifier.Texture(new ResPath("/Textures/Parallaxes/noise.png")), curTime);
|
||||
|
||||
// Draw the rain
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
|
||||
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, new Vector2(0.5f, 0f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,12 @@ public sealed partial class StencilOverlay
|
||||
{
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3x2 invMatrix)
|
||||
private void DrawWeather(
|
||||
in OverlayDrawArgs args,
|
||||
CachedResources res,
|
||||
WeatherPrototype weatherProto,
|
||||
float alpha,
|
||||
Matrix3x2 invMatrix)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var mapId = args.MapId;
|
||||
@@ -22,7 +27,7 @@ public sealed partial class StencilOverlay
|
||||
// Cut out the irrelevant bits via stencil
|
||||
// This is why we don't just use parallax; we might want specific tiles to get drawn over
|
||||
// particularly for planet maps or stations.
|
||||
worldHandle.RenderInRenderTarget(_blep!, () =>
|
||||
worldHandle.RenderInRenderTarget(res.Blep!, () =>
|
||||
{
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
_grids.Clear();
|
||||
@@ -55,13 +60,13 @@ public sealed partial class StencilOverlay
|
||||
}, Color.Transparent);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
|
||||
worldHandle.DrawTextureRect(res.Blep!.Texture, worldBounds);
|
||||
var curTime = _timing.RealTime;
|
||||
var sprite = _sprite.GetFrame(weatherProto.Sprite, curTime);
|
||||
|
||||
// Draw the rain
|
||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
|
||||
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Graphics;
|
||||
using Content.Client.Parallax;
|
||||
using Content.Client.Weather;
|
||||
using Content.Shared.Salvage;
|
||||
@@ -17,6 +18,9 @@ namespace Content.Client.Overlays;
|
||||
/// </summary>
|
||||
public sealed partial class StencilOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> StencilMask = "StencilMask";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilDraw = "StencilDraw";
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -30,7 +34,7 @@ public sealed partial class StencilOverlay : Overlay
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
private IRenderTexture? _blep;
|
||||
private readonly OverlayResourceCache<CachedResources> _resources = new();
|
||||
|
||||
private readonly ShaderInstance _shader;
|
||||
|
||||
@@ -51,10 +55,12 @@ public sealed partial class StencilOverlay : Overlay
|
||||
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
|
||||
|
||||
if (_blep?.Texture.Size != args.Viewport.Size)
|
||||
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
|
||||
|
||||
if (res.Blep?.Texture.Size != args.Viewport.Size)
|
||||
{
|
||||
_blep?.Dispose();
|
||||
_blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil");
|
||||
res.Blep?.Dispose();
|
||||
res.Blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil");
|
||||
}
|
||||
|
||||
if (_entManager.TryGetComponent<WeatherComponent>(mapUid, out var comp))
|
||||
@@ -65,16 +71,33 @@ public sealed partial class StencilOverlay : Overlay
|
||||
continue;
|
||||
|
||||
var alpha = _weather.GetPercent(weather, mapUid);
|
||||
DrawWeather(args, weatherProto, alpha, invMatrix);
|
||||
DrawWeather(args, res, weatherProto, alpha, invMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
if (_entManager.TryGetComponent<RestrictedRangeComponent>(mapUid, out var restrictedRangeComponent))
|
||||
{
|
||||
DrawRestrictedRange(args, restrictedRangeComponent, invMatrix);
|
||||
DrawRestrictedRange(args, res, restrictedRangeComponent, invMatrix);
|
||||
}
|
||||
|
||||
args.WorldHandle.UseShader(null);
|
||||
args.WorldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_resources.Dispose();
|
||||
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private sealed class CachedResources : IDisposable
|
||||
{
|
||||
public IRenderTexture? Blep;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Blep?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,28 @@ namespace Content.Client.Paper.UI;
|
||||
|
||||
public sealed class PaperVisualizerSystem : VisualizerSystem<PaperVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, PaperVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status , out var writingStatus, args.Component))
|
||||
args.Sprite.LayerSetVisible(PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
|
||||
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status, out var writingStatus, args.Component))
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
|
||||
|
||||
if (AppearanceSystem.TryGetData<string>(uid, PaperVisuals.Stamp, out var stampState, args.Component))
|
||||
{
|
||||
args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
|
||||
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
|
||||
if (stampState != string.Empty)
|
||||
{
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), PaperVisualLayers.Stamp, stampState);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PaperVisualLayers.Stamp, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PaperVisualLayers.Stamp, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Singularity.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
@@ -6,12 +5,14 @@ namespace Content.Client.ParticleAccelerator;
|
||||
|
||||
public sealed class ParticleAcceleratorPartVisualizerSystem : VisualizerSystem<ParticleAcceleratorPartVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, ParticleAcceleratorPartVisualsComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!args.Sprite.LayerMapTryGet(ParticleAcceleratorVisualLayers.Unlit, out var index))
|
||||
if (!_sprite.LayerMapTryGet((uid, args.Sprite), ParticleAcceleratorVisualLayers.Unlit, out var index, false))
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<ParticleAcceleratorVisualState>(uid, ParticleAcceleratorVisuals.VisualState, out var state, args.Component))
|
||||
@@ -21,12 +22,12 @@ public sealed class ParticleAcceleratorPartVisualizerSystem : VisualizerSystem<P
|
||||
|
||||
if (state != ParticleAcceleratorVisualState.Unpowered)
|
||||
{
|
||||
args.Sprite.LayerSetVisible(index, true);
|
||||
args.Sprite.LayerSetState(index, comp.StateBase + comp.StatesSuffixes[state]);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), index, true);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), index, comp.StateBase + comp.StatesSuffixes[state]);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetVisible(index, false);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), index, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Content.Client.PowerCell;
|
||||
public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -44,19 +45,19 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!args.Sprite.TryGetLayer((int) PowerCellVisualLayers.Unshaded, out var unshadedLayer))
|
||||
if (!_sprite.LayerExists((uid, args.Sprite), PowerCellVisualLayers.Unshaded))
|
||||
return;
|
||||
|
||||
if (_appearance.TryGetData<byte>(uid, PowerCellVisuals.ChargeLevel, out var level, args.Component))
|
||||
{
|
||||
if (level == 0)
|
||||
{
|
||||
unshadedLayer.Visible = false;
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, false);
|
||||
return;
|
||||
}
|
||||
|
||||
unshadedLayer.Visible = true;
|
||||
args.Sprite.LayerSetState(PowerCellVisualLayers.Unshaded, $"o{level}");
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, false);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), PowerCellVisualLayers.Unshaded, $"o{level}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,16 @@
|
||||
|
||||
<!-- Entry layer (shows main categories) -->
|
||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
|
||||
|
||||
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-walls-and-flooring'}" TargetLayer="WallsAndFlooring" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/walls_and_flooring.png"/>
|
||||
</ui:RadialMenuTextureButtonWithSector>
|
||||
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-latticetiles'}" TargetLayer="LatticeTiles" Visible="False"> <!-- Mono -->
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lattice.png"/>
|
||||
</ui:RadialMenuTextureButtonWithSector>
|
||||
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-platingtiles'}" TargetLayer="PlatingTiles" Visible="False"> <!-- Mono -->
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/plating.png"/>
|
||||
</ui:RadialMenuTextureButtonWithSector>
|
||||
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-windows-and-grilles'}" TargetLayer="WindowsAndGrilles" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/windows_and_grilles.png"/>
|
||||
</ui:RadialMenuTextureButtonWithSector>
|
||||
@@ -27,11 +34,18 @@
|
||||
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-lighting'}" TargetLayer="Lighting" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lighting.png"/>
|
||||
</ui:RadialMenuTextureButtonWithSector>
|
||||
|
||||
</ui:RadialContainer>
|
||||
|
||||
<!-- Walls and flooring -->
|
||||
<ui:RadialContainer Name="WallsAndFlooring" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||
|
||||
<!-- Lattices Mono -->
|
||||
<ui:RadialContainer Name="LatticeTiles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||
|
||||
<!-- Plating Mono -->
|
||||
<ui:RadialContainer Name="PlatingTiles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||
|
||||
<!-- Windows and grilles -->
|
||||
<ui:RadialContainer Name="WindowsAndGrilles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
|
||||
|
||||
|
||||
@@ -77,6 +77,16 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
|
||||
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
|
||||
_selected = null;
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
var isAiControllable = false;
|
||||
|
||||
if (_selected != null)
|
||||
{
|
||||
_cyborgs.TryGetValue(_selected, out var data);
|
||||
isAiControllable = data.IsAiControllable;
|
||||
}
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
|
||||
var hasCyborgs = _cyborgs.Count > 0;
|
||||
NoCyborgs.Visible = !hasCyborgs;
|
||||
CyborgsContainer.Visible = hasCyborgs;
|
||||
|
||||
@@ -138,6 +138,293 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
}
|
||||
// End Frontier Corvax
|
||||
|
||||
// Mono
|
||||
protected void DrawAzimuthScale(DrawingHandleScreen handle, Angle rotation)
|
||||
{
|
||||
const int azimuthMinorTickStepDegrees = 10;
|
||||
const int azimuthMajorTickInterval = 3;
|
||||
const int azimuthCardinalTickInterval = 9;
|
||||
const float edgeInset = 10f;
|
||||
const float tickInset = 4f;
|
||||
const float minorTickLength = 5f;
|
||||
const float majorTickLength = 9f;
|
||||
const float cardinalTickLength = 12f;
|
||||
const float labelPadding = 3f;
|
||||
const float labelOffset = tickInset + cardinalTickLength + labelPadding;
|
||||
const float regularLabelScale = 0.62f;
|
||||
const float cardinalLabelScale = 0.72f;
|
||||
|
||||
var origin = MidPointVector;
|
||||
var viewportSize = new Vector2(PixelWidth, PixelHeight);
|
||||
|
||||
var subtleGray = Color.FromHex("#676767");
|
||||
var northAccent = Color.FromHex("#7F7442");
|
||||
var eastAccent = Color.FromHex("#456273");
|
||||
var baseTickColor = subtleGray.WithAlpha(0.34f);
|
||||
var majorTickColor = subtleGray.WithAlpha(0.5f);
|
||||
var cardinalTickColor = subtleGray.WithAlpha(0.68f);
|
||||
var regularLabelColor = subtleGray.WithAlpha(0.76f);
|
||||
var cardinalLabelColor = subtleGray.WithAlpha(0.9f);
|
||||
var labelShadowColor = Color.Black.WithAlpha(0.36f);
|
||||
|
||||
for (var step = 0; step < 360 / azimuthMinorTickStepDegrees; step++)
|
||||
{
|
||||
var degrees = step * azimuthMinorTickStepDegrees;
|
||||
var direction = GetAzimuthDirection(rotation, degrees);
|
||||
var isCardinal = step % azimuthCardinalTickInterval == 0;
|
||||
var isMajor = step % azimuthMajorTickInterval == 0;
|
||||
var isNorth = degrees == 0;
|
||||
var isEast = degrees == 90;
|
||||
var tickLength = isCardinal
|
||||
? cardinalTickLength
|
||||
: isMajor
|
||||
? majorTickLength
|
||||
: minorTickLength;
|
||||
|
||||
if (!TryGetViewportEdgePoint(origin, direction, viewportSize, edgeInset, out var edgePoint, out var inwardNormal))
|
||||
continue;
|
||||
|
||||
var outer = edgePoint + inwardNormal * tickInset;
|
||||
var inner = outer + inwardNormal * tickLength;
|
||||
var tickColor = isNorth
|
||||
? northAccent.WithAlpha(0.58f)
|
||||
: isEast
|
||||
? eastAccent.WithAlpha(0.58f)
|
||||
: isCardinal
|
||||
? cardinalTickColor
|
||||
: isMajor
|
||||
? majorTickColor
|
||||
: baseTickColor;
|
||||
handle.DrawLine(outer, inner, tickColor);
|
||||
if (isNorth)
|
||||
{
|
||||
var sideOffset = new Vector2(-inwardNormal.Y, inwardNormal.X) * 0.35f;
|
||||
handle.DrawLine(outer + sideOffset, inner + sideOffset, tickColor.WithAlpha(0.46f));
|
||||
}
|
||||
|
||||
if (!isMajor)
|
||||
continue;
|
||||
|
||||
var label = degrees.ToString();
|
||||
var labelScale = isCardinal ? cardinalLabelScale : regularLabelScale;
|
||||
var labelColor = isEast
|
||||
? eastAccent.WithAlpha(0.76f)
|
||||
: isCardinal
|
||||
? cardinalLabelColor
|
||||
: regularLabelColor;
|
||||
var labelSize = handle.GetDimensions(Font, label, labelScale);
|
||||
var labelPosition = GetEdgeLabelPosition(edgePoint, inwardNormal, labelSize, viewportSize, labelOffset);
|
||||
handle.DrawString(Font, labelPosition + new Vector2(1f, 1f), label, labelScale, labelShadowColor);
|
||||
handle.DrawString(Font, labelPosition, label, labelScale, labelColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawAzimuthRing(DrawingHandleScreen handle, Angle rotation)
|
||||
{
|
||||
const int azimuthMinorTickStepDegrees = 10;
|
||||
const int azimuthMajorTickInterval = 3;
|
||||
const int azimuthCardinalTickInterval = 9;
|
||||
const float ringInset = 16f;
|
||||
const float minorTickLength = 5f;
|
||||
const float majorTickLength = 9f;
|
||||
const float cardinalTickLength = 12f;
|
||||
const float labelPadding = 5f;
|
||||
const float regularLabelScale = 0.62f;
|
||||
const float cardinalLabelScale = 0.72f;
|
||||
|
||||
var origin = MidPointVector;
|
||||
var radius = MathF.Max(8f, SizeFull * 0.5f - ringInset);
|
||||
|
||||
var subtleGray = Color.FromHex("#676767");
|
||||
var northAccent = Color.FromHex("#7F7442");
|
||||
var eastAccent = Color.FromHex("#456273");
|
||||
var baseTickColor = subtleGray.WithAlpha(0.34f);
|
||||
var majorTickColor = subtleGray.WithAlpha(0.5f);
|
||||
var cardinalTickColor = subtleGray.WithAlpha(0.68f);
|
||||
var regularLabelColor = subtleGray.WithAlpha(0.76f);
|
||||
var cardinalLabelColor = subtleGray.WithAlpha(0.9f);
|
||||
var labelShadowColor = Color.Black.WithAlpha(0.36f);
|
||||
|
||||
handle.DrawCircle(origin, radius, subtleGray.WithAlpha(0.12f), false);
|
||||
|
||||
for (var step = 0; step < 360 / azimuthMinorTickStepDegrees; step++)
|
||||
{
|
||||
var degrees = step * azimuthMinorTickStepDegrees;
|
||||
var direction = GetAzimuthDirection(rotation, degrees);
|
||||
var isCardinal = step % azimuthCardinalTickInterval == 0;
|
||||
var isMajor = step % azimuthMajorTickInterval == 0;
|
||||
var isNorth = degrees == 0;
|
||||
var isEast = degrees == 90;
|
||||
var tickLength = isCardinal
|
||||
? cardinalTickLength
|
||||
: isMajor
|
||||
? majorTickLength
|
||||
: minorTickLength;
|
||||
|
||||
var outer = origin + direction * radius;
|
||||
var inner = origin + direction * (radius - tickLength);
|
||||
var tickColor = isNorth
|
||||
? northAccent.WithAlpha(0.58f)
|
||||
: isEast
|
||||
? eastAccent.WithAlpha(0.58f)
|
||||
: isCardinal
|
||||
? cardinalTickColor
|
||||
: isMajor
|
||||
? majorTickColor
|
||||
: baseTickColor;
|
||||
handle.DrawLine(outer, inner, tickColor);
|
||||
if (isNorth)
|
||||
{
|
||||
var sideOffset = new Vector2(-direction.Y, direction.X) * 0.35f;
|
||||
handle.DrawLine(outer + sideOffset, inner + sideOffset, tickColor.WithAlpha(0.46f));
|
||||
}
|
||||
|
||||
if (!isMajor)
|
||||
continue;
|
||||
|
||||
var label = degrees.ToString();
|
||||
var labelScale = isCardinal ? cardinalLabelScale : regularLabelScale;
|
||||
var labelColor = isEast
|
||||
? eastAccent.WithAlpha(0.76f)
|
||||
: isCardinal
|
||||
? cardinalLabelColor
|
||||
: regularLabelColor;
|
||||
var labelSize = handle.GetDimensions(Font, label, labelScale);
|
||||
var labelPosition = origin + direction * (radius - tickLength - labelPadding) - labelSize * 0.5f;
|
||||
handle.DrawString(Font, labelPosition + new Vector2(1f, 1f), label, labelScale, labelShadowColor);
|
||||
handle.DrawString(Font, labelPosition, label, labelScale, labelColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawCompassOverlay(DrawingHandleScreen handle, Angle heading)
|
||||
{
|
||||
var compassCenter = new Vector2(44f, 44f);
|
||||
const float compassRadius = 24f;
|
||||
var ringColor = Color.White.WithAlpha(0.08f);
|
||||
var northColor = Color.FromHex("#776C43");
|
||||
var eastColor = Color.FromHex("#4C6572");
|
||||
handle.DrawCircle(compassCenter, compassRadius, ringColor, false);
|
||||
handle.DrawCircle(compassCenter, 2.5f, ringColor.WithAlpha(0.48f), true);
|
||||
|
||||
DrawCompassNeedle(handle, compassCenter, compassRadius, GetAzimuthDirection(heading, 0f), "N", northColor);
|
||||
DrawCompassNeedle(handle, compassCenter, compassRadius * 0.82f, GetAzimuthDirection(heading, 90f), "E", eastColor);
|
||||
}
|
||||
|
||||
private void DrawCompassNeedle(
|
||||
DrawingHandleScreen handle,
|
||||
Vector2 compassCenter,
|
||||
float radius,
|
||||
Vector2 direction,
|
||||
string label,
|
||||
Color color)
|
||||
{
|
||||
var tip = compassCenter + direction * radius;
|
||||
var tail = compassCenter - direction * (radius * 0.35f);
|
||||
DrawArrowLine(handle, tail, tip, color.WithAlpha(0.58f), 6f);
|
||||
|
||||
var labelSize = handle.GetDimensions(Font, label, 0.9f);
|
||||
var labelPosition = tip + direction * 6f - new Vector2(labelSize.X * 0.5f, labelSize.Y * 0.5f);
|
||||
handle.DrawString(Font, labelPosition, label, 0.9f, color.WithAlpha(0.7f));
|
||||
}
|
||||
|
||||
private static void DrawArrowLine(
|
||||
DrawingHandleScreen handle,
|
||||
Vector2 start,
|
||||
Vector2 end,
|
||||
Color color,
|
||||
float headLength)
|
||||
{
|
||||
var direction = end - start;
|
||||
if (direction.LengthSquared() <= 0.001f)
|
||||
return;
|
||||
|
||||
direction = Vector2.Normalize(direction);
|
||||
var left = new Vector2(-direction.Y, direction.X);
|
||||
handle.DrawLine(start, end, color);
|
||||
handle.DrawLine(end, end - direction * headLength + left * (headLength * 0.45f), color);
|
||||
handle.DrawLine(end, end - direction * headLength - left * (headLength * 0.45f), color);
|
||||
}
|
||||
|
||||
private static bool TryGetViewportEdgePoint(
|
||||
Vector2 origin,
|
||||
Vector2 direction,
|
||||
Vector2 viewportSize,
|
||||
float inset,
|
||||
out Vector2 edgePoint,
|
||||
out Vector2 inwardNormal)
|
||||
{
|
||||
edgePoint = default;
|
||||
inwardNormal = default;
|
||||
|
||||
if (direction.LengthSquared() <= 0.001f)
|
||||
return false;
|
||||
|
||||
direction = Vector2.Normalize(direction);
|
||||
var min = new Vector2(inset, inset);
|
||||
var max = viewportSize - min;
|
||||
if (max.X <= min.X || max.Y <= min.Y)
|
||||
return false;
|
||||
|
||||
var tx = direction.X > 0f
|
||||
? (max.X - origin.X) / direction.X
|
||||
: direction.X < 0f
|
||||
? (min.X - origin.X) / direction.X
|
||||
: float.PositiveInfinity;
|
||||
var ty = direction.Y > 0f
|
||||
? (max.Y - origin.Y) / direction.Y
|
||||
: direction.Y < 0f
|
||||
? (min.Y - origin.Y) / direction.Y
|
||||
: float.PositiveInfinity;
|
||||
|
||||
var t = Math.Min(tx, ty);
|
||||
if (!float.IsFinite(t) || t <= 0f)
|
||||
return false;
|
||||
|
||||
edgePoint = origin + direction * t;
|
||||
|
||||
if (tx < ty)
|
||||
inwardNormal = direction.X > 0f ? -Vector2.UnitX : Vector2.UnitX;
|
||||
else
|
||||
inwardNormal = direction.Y > 0f ? -Vector2.UnitY : Vector2.UnitY;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Vector2 GetEdgeLabelPosition(
|
||||
Vector2 edgePoint,
|
||||
Vector2 inwardNormal,
|
||||
Vector2 labelSize,
|
||||
Vector2 viewportSize,
|
||||
float offset)
|
||||
{
|
||||
var anchor = edgePoint + inwardNormal * offset;
|
||||
Vector2 requested;
|
||||
if (Math.Abs(inwardNormal.X) > Math.Abs(inwardNormal.Y))
|
||||
{
|
||||
requested = inwardNormal.X > 0f
|
||||
? new Vector2(anchor.X, anchor.Y - labelSize.Y * 0.5f)
|
||||
: new Vector2(anchor.X - labelSize.X, anchor.Y - labelSize.Y * 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
requested = inwardNormal.Y > 0f
|
||||
? new Vector2(anchor.X - labelSize.X * 0.5f, anchor.Y)
|
||||
: new Vector2(anchor.X - labelSize.X * 0.5f, anchor.Y - labelSize.Y);
|
||||
}
|
||||
|
||||
var minPosition = new Vector2(offset, offset);
|
||||
var maxPosition = viewportSize - labelSize - minPosition;
|
||||
return Vector2.Clamp(requested, minPosition, maxPosition);
|
||||
}
|
||||
|
||||
private static Vector2 GetAzimuthDirection(Angle baseAngle, float azimuthDegrees)
|
||||
{
|
||||
var angle = baseAngle + Angle.FromDegrees(azimuthDegrees);
|
||||
var radians = (float) angle.Theta - MathF.PI / 2f;
|
||||
return new Vector2(MathF.Cos(radians), MathF.Sin(radians));
|
||||
}
|
||||
// End Mono
|
||||
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 gridToView, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
{
|
||||
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
Orientation="Vertical">
|
||||
<!-- FTL Lock Section -->
|
||||
<controls1:StripeBack MinSize="48 48">
|
||||
<controls:Label Text="{controls:Loc 'FTL Lock'}" HorizontalAlignment="Center"/>
|
||||
<controls:Label Text="{controls:Loc 'shuttle-console-ftl-lock'}" HorizontalAlignment="Center"/>
|
||||
</controls1:StripeBack>
|
||||
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
|
||||
<controls:PanelContainer.PanelOverride>
|
||||
@@ -36,12 +36,12 @@
|
||||
</controls:PanelContainer.PanelOverride>
|
||||
<controls:BoxContainer Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="5 5 5 5">
|
||||
<controls:Button Name="FTLLockEnabledButton"
|
||||
Text="{controls:Loc 'Enabled'}"
|
||||
Text="{controls:Loc 'shuttle-console-ftl-lock-enabled'}"
|
||||
ToggleMode="True"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenRight"/>
|
||||
<controls:Button Name="FTLLockDisabledButton"
|
||||
Text="{controls:Loc 'Disabled'}"
|
||||
Text="{controls:Loc 'shuttle-console-ftl-lock-disabled'}"
|
||||
ToggleMode="True"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenLeft"/>
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<!-- Docks Section -->
|
||||
<controls1:StripeBack MinSize="48 48">
|
||||
<controls:Label Text="{controls:Loc 'Docks'}" HorizontalAlignment="Center"/>
|
||||
<controls:Label Text="{controls:Loc 'shuttle-console-docks-label'}" HorizontalAlignment="Center"/>
|
||||
</controls1:StripeBack>
|
||||
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
|
||||
<controls:PanelContainer.PanelOverride>
|
||||
@@ -62,7 +62,7 @@
|
||||
<graphics:StyleBoxFlat BorderThickness="2" BorderColor="#333333" BackgroundColor="#121212"/>
|
||||
</controls:PanelContainer.PanelOverride>
|
||||
<controls:Button Name="UndockAllButton"
|
||||
Text="{controls:Loc 'Undock All'}"
|
||||
Text="{controls:Loc 'shuttle-console-undock-all'}"
|
||||
Margin="5 5 5 5"
|
||||
StyleClasses="ButtonSquare"/>
|
||||
</controls:PanelContainer>
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
|
||||
xmlns:controls1="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<ui:ShuttleNavControl Name="NavRadar"
|
||||
MouseFilter="Stop"
|
||||
VerticalAlignment="Stretch"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
Margin="5 4 10 5"/>
|
||||
<!-- Mono - wrap -->
|
||||
<controls:LayoutContainer Margin="5 4 10 5">
|
||||
<ui:ShuttleNavControl Name="NavRadar"
|
||||
MouseFilter="Stop"
|
||||
VerticalAlignment="Stretch"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"/>
|
||||
</controls:LayoutContainer>
|
||||
<!-- Nav controls -->
|
||||
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
|
||||
<controls:PanelContainer.PanelOverride>
|
||||
@@ -173,11 +175,8 @@
|
||||
<!-- Frontier - Maximum Shuttle Speed -->
|
||||
<controls:BoxContainer Orientation="Vertical" HorizontalExpand="True" Name="MaximumShuttleSpeedBox" Margin="4 4 4 4">
|
||||
<controls:Label Text="{controls:Loc 'shuttle-console-maximum-speed'}"/>
|
||||
<controls:SliderIntInput Name="MaximumShuttleSpeedValue"
|
||||
<controls:LineEdit Name="MaximumShuttleSpeedValue"
|
||||
Access="Public"
|
||||
MinValue="0"
|
||||
MaxValue="140"
|
||||
Value="140"
|
||||
HorizontalExpand="True"/>
|
||||
</controls:BoxContainer>
|
||||
</controls:BoxContainer>
|
||||
@@ -215,36 +214,43 @@
|
||||
Text="{controls:Loc 'shuttle-console-device-button-1'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton2"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-2'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton3"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-3'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton4"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-4'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton5"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-5'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton6"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-6'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton7"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-7'}"
|
||||
TextAlign="Center"
|
||||
StyleClasses="ButtonSquare"
|
||||
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
|
||||
SetWidth="55"/>
|
||||
<controls:Button Name="DeviceButton8"
|
||||
Text="{controls:Loc 'shuttle-console-device-button-8'}"
|
||||
|
||||
@@ -176,14 +176,15 @@ public sealed partial class NavScreen : BoxContainer
|
||||
var (_, worldRot, worldMatrix) = _xformSystem.GetWorldPositionRotationMatrix(gridXform);
|
||||
var worldPos = Vector2.Transform(gridBody.LocalCenter, worldMatrix);
|
||||
|
||||
// Get the positive reduced angle.
|
||||
var displayRot = -worldRot.Reduced();
|
||||
// Mono - remap to [0, 360)
|
||||
var displayRot = (-worldRot).Reduced();
|
||||
var displayRotDegrees = displayRot.FlipPositive().Degrees;
|
||||
|
||||
GridPosition.Text = Loc.GetString("shuttle-console-position-value",
|
||||
("X", $"{worldPos.X:0.0}"),
|
||||
("Y", $"{worldPos.Y:0.0}"));
|
||||
GridOrientation.Text = Loc.GetString("shuttle-console-orientation-value",
|
||||
("angle", $"{displayRot.Degrees:0.0}"));
|
||||
("angle", $"{displayRotDegrees:0.0}"));
|
||||
|
||||
var gridVelocity = gridBody.LinearVelocity;
|
||||
gridVelocity = displayRot.RotateVec(gridVelocity);
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
Title="{Loc 'radar-console-window-title'}"
|
||||
SetSize="648 672"
|
||||
MinSize="256 256">
|
||||
<ui:ShuttleNavControl Name="RadarScreen"
|
||||
Margin="4"
|
||||
HorizontalExpand = "True"
|
||||
VerticalExpand = "True"/>
|
||||
<LayoutContainer Margin="4">
|
||||
<!-- Mono - wrap -->
|
||||
<ui:ShuttleNavControl Name="RadarScreen"
|
||||
HorizontalExpand = "True"
|
||||
VerticalExpand = "True"/>
|
||||
</LayoutContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -13,6 +13,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
@@ -45,9 +46,10 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
/// <summary>
|
||||
/// Entity of controlling console
|
||||
/// </summary>
|
||||
private EntityUid? _consoleEntity;
|
||||
protected EntityUid? _consoleEntity; // Mono
|
||||
|
||||
protected Angle? _rotation; // Mono
|
||||
// Mono - is world rotation of the view.
|
||||
protected Angle? _rotation = Angle.Zero;
|
||||
|
||||
private Dictionary<NetEntity, List<DockingPortState>> _docks = new();
|
||||
|
||||
@@ -65,7 +67,6 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
public bool ShowIFF { get; set; } = true;
|
||||
public bool ShowIFFShuttles { get; set; } = true;
|
||||
public bool ShowDocks { get; set; } = true;
|
||||
public bool RotateWithEntity { get; set; } = true;
|
||||
|
||||
public float MaximumIFFDistance { get; set; } = 3000f; // Frontier // Mono - 3000 by default to not gigaclutter
|
||||
public bool HideCoords { get; set; } = false; // Frontier
|
||||
@@ -88,14 +89,16 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
public List<EntityUid>? Detectors = null;
|
||||
|
||||
#region Mono
|
||||
public bool RelativePanning = false;
|
||||
|
||||
// These 2 handle timing updates
|
||||
protected const float RadarUpdateInterval = 0f;
|
||||
protected float _updateAccumulator = 0f;
|
||||
|
||||
// doesn't support absolute panning and angle follow, but why would you need that?
|
||||
protected bool _relativePanning = false;
|
||||
protected bool _angleFollow = true;
|
||||
private bool _radarModeDefaultsApplied;
|
||||
|
||||
private bool _wasPanned = false;
|
||||
private EntityCoordinates? _oldCoordinates;
|
||||
#endregion
|
||||
|
||||
protected bool _isMouseDown;
|
||||
@@ -104,6 +107,20 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
protected float _lastFireTime;
|
||||
protected const float FireRateLimit = 0.1f; // 100ms between shots
|
||||
|
||||
private enum RadarAzimuthMode : byte
|
||||
{
|
||||
Edge,
|
||||
Ring,
|
||||
Off,
|
||||
}
|
||||
|
||||
private BoxContainer? _radarModeButtons;
|
||||
private RadarModeButton? _radarAzimuthButton;
|
||||
private RadarModeButton? _radarRotationButton;
|
||||
private RadarModeButton? _radarAnchorButton;
|
||||
private RadarModeButton? _radarResetButton;
|
||||
private RadarAzimuthMode _azimuthMode;
|
||||
|
||||
public ShuttleNavControl() : this(64f, 256f, 256f) { } // Mono
|
||||
|
||||
public ShuttleNavControl(float minRange, float maxRange, float range) : base(minRange, maxRange, range) // Mono
|
||||
@@ -135,12 +152,279 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
return;
|
||||
|
||||
_coordinates = coordinates;
|
||||
_rotation = angle;
|
||||
// Mono - TODO: either remove this or make this do anything
|
||||
// _rotation = angle;
|
||||
}
|
||||
|
||||
public void SetConsole(EntityUid? consoleEntity)
|
||||
{
|
||||
if (_consoleEntity == consoleEntity)
|
||||
return;
|
||||
|
||||
_consoleEntity = consoleEntity;
|
||||
_radarModeDefaultsApplied = false;
|
||||
ApplyRadarModeDefaults();
|
||||
}
|
||||
|
||||
// converts Offset to world-relative offset so we can modify radar parameters without conversion headaches
|
||||
// call this before potentially rotation-modifying operations and PanToEnt() after
|
||||
private void PanToWorld()
|
||||
{
|
||||
if (!_angleFollow || _coordinates is not { } cord)
|
||||
return;
|
||||
var coordEntRot = _transform.GetWorldRotation(cord.EntityId);
|
||||
Offset = coordEntRot.RotateVec(Offset);
|
||||
}
|
||||
|
||||
// converts world-relative Offset to relative to current view
|
||||
// call this after potentially rotation-modifying operations and PanToWorld() before
|
||||
private void PanToEnt()
|
||||
{
|
||||
if (!_angleFollow || _coordinates is not { } cord)
|
||||
return;
|
||||
var coordEntRot = _transform.GetWorldRotation(cord.EntityId);
|
||||
Offset = (-coordEntRot).RotateVec(Offset);
|
||||
}
|
||||
|
||||
// reanchor our view to console or grid
|
||||
private void ReanchorView()
|
||||
{
|
||||
if (_consoleEntity is not { } cons
|
||||
|| _coordinates is not { } cord
|
||||
|| !EntManager.TryGetComponent<TransformComponent>(cons, out var xform))
|
||||
return;
|
||||
|
||||
PanToWorld();
|
||||
var anchTo = cons;
|
||||
if (xform.GridUid != null)
|
||||
anchTo = xform.GridUid.Value;
|
||||
_coordinates = _transform.WithEntityId(cord, anchTo);
|
||||
_relativePanning = true;
|
||||
PanToEnt();
|
||||
}
|
||||
|
||||
private void EnsureRadarModeButtons()
|
||||
{
|
||||
if (_radarModeButtons != null)
|
||||
return;
|
||||
|
||||
if (Parent is not LayoutContainer parent)
|
||||
return;
|
||||
|
||||
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
_radarModeButtons = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
SeparationOverride = 2,
|
||||
HorizontalExpand = false,
|
||||
VerticalExpand = false,
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
};
|
||||
|
||||
_radarAzimuthButton = CreateRadarModeButton(RadarModeButtonIcon.Azimuth);
|
||||
_radarAzimuthButton.ToolTip = Loc.GetString("shuttle-console-button-azimuth");
|
||||
_radarAzimuthButton.OnPressed += _ =>
|
||||
{
|
||||
_azimuthMode = _azimuthMode switch
|
||||
{
|
||||
RadarAzimuthMode.Edge => RadarAzimuthMode.Ring,
|
||||
RadarAzimuthMode.Ring => RadarAzimuthMode.Off,
|
||||
_ => RadarAzimuthMode.Edge,
|
||||
};
|
||||
};
|
||||
|
||||
_radarRotationButton = CreateRadarModeButton(RadarModeButtonIcon.Rotation);
|
||||
_radarRotationButton.ToolTip = Loc.GetString("shuttle-console-button-rotation");
|
||||
_radarRotationButton.OnPressed += _ =>
|
||||
{
|
||||
PanToWorld();
|
||||
_angleFollow ^= true;
|
||||
PanToEnt();
|
||||
};
|
||||
|
||||
_radarAnchorButton = CreateRadarModeButton(RadarModeButtonIcon.Anchor);
|
||||
_radarAnchorButton.ToolTip = Loc.GetString("shuttle-console-button-anchor");
|
||||
_radarAnchorButton.OnPressed += _ =>
|
||||
{
|
||||
if (_coordinates is not { } cord)
|
||||
return;
|
||||
|
||||
// if we're anchored or not yet bound to map, just deanchor
|
||||
if (_relativePanning || !EntManager.HasComponent<MapComponent>(cord.EntityId))
|
||||
{
|
||||
PanToWorld();
|
||||
// we're deanchoring, so simply detach our view to the map
|
||||
_coordinates = _transform.ToCoordinates(_transform.ToMapCoordinates(cord));
|
||||
_relativePanning = false;
|
||||
_wasPanned = true;
|
||||
PanToEnt();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReanchorView();
|
||||
}
|
||||
};
|
||||
|
||||
_radarResetButton = CreateRadarModeButton(RadarModeButtonIcon.Reset);
|
||||
_radarResetButton.ToolTip = Loc.GetString("shuttle-console-button-reset");
|
||||
_radarResetButton.OnPressed += _ =>
|
||||
{
|
||||
_radarModeDefaultsApplied = false;
|
||||
ApplyRadarModeDefaults();
|
||||
if (EntManager.TryGetComponent<TransformComponent>(_consoleEntity, out var xform))
|
||||
{
|
||||
Offset = Vector2.Zero;
|
||||
_coordinates = xform.Coordinates;
|
||||
ReanchorView();
|
||||
}
|
||||
};
|
||||
|
||||
_radarModeButtons.AddChild(_radarAzimuthButton);
|
||||
_radarModeButtons.AddChild(_radarRotationButton);
|
||||
_radarModeButtons.AddChild(_radarAnchorButton);
|
||||
_radarModeButtons.AddChild(_radarResetButton);
|
||||
|
||||
parent.AddChild(_radarModeButtons);
|
||||
LayoutContainer.SetAnchorAndMarginPreset(_radarModeButtons, LayoutContainer.LayoutPreset.BottomLeft, margin: 10);
|
||||
}
|
||||
|
||||
private void ApplyRadarModeDefaults()
|
||||
{
|
||||
if (_radarModeDefaultsApplied)
|
||||
return;
|
||||
|
||||
if (!EntManager.TryGetComponent<RadarConsoleComponent>(_consoleEntity, out var radar))
|
||||
return;
|
||||
|
||||
_radarModeDefaultsApplied = true;
|
||||
Draggable = radar.Pannable;
|
||||
_angleFollow = !radar.NoRotate;
|
||||
_relativePanning = radar.RelativePanning;
|
||||
}
|
||||
|
||||
private static RadarModeButton CreateRadarModeButton(RadarModeButtonIcon icon = RadarModeButtonIcon.None)
|
||||
{
|
||||
return new RadarModeButton(icon)
|
||||
{
|
||||
MinWidth = 16f,
|
||||
MaxWidth = 16f,
|
||||
MinHeight = 16f,
|
||||
MaxHeight = 16f,
|
||||
HorizontalExpand = false,
|
||||
VerticalExpand = false,
|
||||
ModulateSelfOverride = Color.White.WithAlpha(0.55f),
|
||||
};
|
||||
}
|
||||
|
||||
internal enum RadarModeButtonIcon : byte
|
||||
{
|
||||
None,
|
||||
Azimuth,
|
||||
Rotation,
|
||||
Anchor,
|
||||
Reset
|
||||
}
|
||||
|
||||
public sealed class RadarModeButton : BaseButton
|
||||
{
|
||||
private readonly RadarModeButtonIcon _icon;
|
||||
|
||||
internal RadarModeButton(RadarModeButtonIcon icon)
|
||||
{
|
||||
_icon = icon;
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
return new Vector2(16f, 16f);
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
var alpha = DrawMode switch
|
||||
{
|
||||
DrawModeEnum.Pressed => 0.62f,
|
||||
DrawModeEnum.Hover => 0.48f,
|
||||
_ => 0.32f,
|
||||
};
|
||||
|
||||
var center = new Vector2(PixelSize.X * 0.5f, PixelSize.Y * 0.5f);
|
||||
var radius = MathF.Min(PixelSize.X, PixelSize.Y) * 0.42f;
|
||||
handle.DrawCircle(center, radius, Color.White.WithAlpha(alpha));
|
||||
|
||||
var up = new Vector2(0f, -radius);
|
||||
var down = new Vector2(0f, radius);
|
||||
var leftOffs = new Vector2(-radius, 0f);
|
||||
var rightOffs = new Vector2(radius, 0f);
|
||||
var top = center + up;
|
||||
var bottom = center + down;
|
||||
var left = center + leftOffs;
|
||||
var right = center + rightOffs;
|
||||
|
||||
switch (_icon) {
|
||||
case RadarModeButtonIcon.Azimuth:
|
||||
{
|
||||
var iconColor = Color.Black.WithAlpha(0.95f);
|
||||
var topLeft = center + (up + leftOffs) / MathF.Sqrt(2f);
|
||||
var topRight = center + (up + rightOffs) / MathF.Sqrt(2f);
|
||||
var bottomLeft = center + (down + leftOffs) / MathF.Sqrt(2f);
|
||||
var bottomRight = center + (down + rightOffs) / MathF.Sqrt(2f);
|
||||
|
||||
handle.DrawLine(topLeft, bottomRight, iconColor);
|
||||
handle.DrawLine(bottomLeft, topRight, iconColor);
|
||||
handle.DrawLine(left, right, iconColor);
|
||||
handle.DrawLine(bottom, top, iconColor);
|
||||
break;
|
||||
}
|
||||
case RadarModeButtonIcon.Rotation:
|
||||
{
|
||||
var iconColor = Color.Yellow.WithAlpha(0.95f);
|
||||
var innerRatio = 0.8f;
|
||||
var segCount = 3;
|
||||
var prevOffs = up * innerRatio;
|
||||
for (var i = 0; i < segCount; i++)
|
||||
{
|
||||
var angle = Angle.FromDegrees(120f * i / (float)segCount - 15f);
|
||||
var thisOffs = angle.RotateVec(up * innerRatio);
|
||||
var thisPointUp = center + thisOffs;
|
||||
var thisPointDown = center - thisOffs;
|
||||
var oldPointUp = center + prevOffs;
|
||||
var oldPointDown = center - prevOffs;
|
||||
handle.DrawLine(oldPointUp, thisPointUp, iconColor);
|
||||
handle.DrawLine(oldPointDown, thisPointDown, iconColor);
|
||||
prevOffs = thisOffs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RadarModeButtonIcon.Anchor:
|
||||
{
|
||||
var iconColor = Color.Blue.WithAlpha(0.95f);
|
||||
var stemTop = center + up * 0.5f;
|
||||
var crossLeft = center + leftOffs * 0.3f + up * 0.7f;
|
||||
var crossRight = center + rightOffs * 0.3f + up * 0.7f;;
|
||||
var leftFluke = center + leftOffs * 0.6f + down * 0.6f;
|
||||
var rightFluke = center + rightOffs * 0.6f + down * 0.6f;
|
||||
var innerRadius = radius * 0.4f;
|
||||
var innerTop = center + up * 0.6f;
|
||||
|
||||
handle.DrawCircle(innerTop, innerRadius, iconColor, false);
|
||||
handle.DrawLine(stemTop, bottom, iconColor);
|
||||
handle.DrawLine(crossLeft, crossRight, iconColor);
|
||||
handle.DrawLine(bottom, leftFluke, iconColor);
|
||||
handle.DrawLine(bottom, rightFluke, iconColor);
|
||||
break;
|
||||
}
|
||||
case RadarModeButtonIcon.Reset:
|
||||
{
|
||||
var iconColor = Color.Red.WithAlpha(0.95f);
|
||||
handle.DrawLine(left, right, iconColor);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mono - evil hack
|
||||
@@ -153,18 +437,15 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
if (!_draggin || _coordinates == null)
|
||||
return;
|
||||
|
||||
if (!_wasPanned)
|
||||
{
|
||||
_wasPanned = true;
|
||||
_oldCoordinates = RelativePanning ? _coordinates.Value : _transform.ToCoordinates(_transform.ToMapCoordinates(_coordinates.Value));
|
||||
if (!RelativePanning)
|
||||
_rotation = new Angle(0);
|
||||
}
|
||||
if (_oldCoordinates == null)
|
||||
return;
|
||||
if (!_relativePanning
|
||||
&& _coordinates?.EntityId is { } coordEnt
|
||||
&& EntManager.TryGetComponent<TransformComponent>(coordEnt, out var coordXform)
|
||||
&& coordXform.MapUid != coordEnt
|
||||
)
|
||||
_coordinates = _transform.ToCoordinates(_transform.ToMapCoordinates(_coordinates.Value));
|
||||
|
||||
var offs = (_rotation ?? new Angle(0)).RotateVec(InverseMapPosition(MidPointVector));
|
||||
_coordinates = _oldCoordinates.Value.Offset(offs);
|
||||
|
||||
_wasPanned = true;
|
||||
}
|
||||
|
||||
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
@@ -184,21 +465,11 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
base.KeyBindUp(args);
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isMouseDown = false;
|
||||
|
||||
if (_coordinates == null || _rotation == null || OnRadarClick == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var a = InverseScalePosition(args.RelativePosition);
|
||||
var relativeWorldPos = a with { Y = -a.Y };
|
||||
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
||||
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
||||
var coords = GetMouseEntityCoordinates(args.RelativePosition);
|
||||
OnRadarClick?.Invoke(coords);
|
||||
}
|
||||
|
||||
@@ -235,13 +506,7 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
|
||||
private void TryFireAtPosition(Vector2 relativePosition)
|
||||
{
|
||||
if (_coordinates == null || _rotation == null || OnRadarClick == null)
|
||||
return;
|
||||
|
||||
var a = InverseScalePosition(relativePosition);
|
||||
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
||||
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
||||
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
||||
var coords = GetMouseEntityCoordinates(relativePosition);
|
||||
OnRadarClick?.Invoke(coords);
|
||||
}
|
||||
|
||||
@@ -257,14 +522,13 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
}
|
||||
|
||||
var pos = _uiManager.MousePositionScaled.Position - GlobalPosition;
|
||||
var relativeWorldPos = _rotation.Value.RotateVec(pos);
|
||||
|
||||
// I am not sure why the resulting point is 20 units under the mouse.
|
||||
return _coordinates.Value.Offset(relativeWorldPos);
|
||||
return GetMouseEntityCoordinates(pos);
|
||||
}
|
||||
|
||||
public void UpdateState(NavInterfaceState state)
|
||||
{
|
||||
EnsureRadarModeButtons();
|
||||
ApplyRadarModeDefaults();
|
||||
SetMatrix(EntManager.GetCoordinates(state.Coordinates), state.Angle);
|
||||
|
||||
WorldMaxRange = state.MaxRange;
|
||||
@@ -279,14 +543,6 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
|
||||
ActualRadarRange = Math.Clamp(ActualRadarRange, WorldMinRange, WorldMaxRange);
|
||||
|
||||
// Mono
|
||||
RelativePanning = state.RelativePanning;
|
||||
Draggable = state.Pannable;
|
||||
if (!Draggable)
|
||||
_wasPanned = false; // also reset
|
||||
|
||||
RotateWithEntity = state.RotateWithEntity;
|
||||
|
||||
// Frontier
|
||||
if (state.MaxIffRange != null)
|
||||
MaximumIFFDistance = state.MaxIffRange.Value;
|
||||
@@ -321,12 +577,24 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
return;
|
||||
}
|
||||
|
||||
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
|
||||
var posMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, _rotation.Value);
|
||||
var ourEntRot = _wasPanned && !RelativePanning ? new Angle(0) : RotateWithEntity ? _transform.GetWorldRotation(xform) : _rotation.Value;
|
||||
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
|
||||
var shuttleToWorld = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
|
||||
Matrix3x2.Invert(shuttleToWorld, out var worldToShuttle);
|
||||
// Mono
|
||||
if (EntManager.TryGetComponent<RadarConsoleComponent>(_consoleEntity, out var radar))
|
||||
{
|
||||
Draggable = radar.Pannable;
|
||||
}
|
||||
// Mono - TODO: remove _rotation and make it a method instead
|
||||
var coordEnt = _coordinates.Value.EntityId;
|
||||
var coordEntRot = _transform.GetWorldRotation(coordEnt);
|
||||
if (_angleFollow && EntManager.TryGetComponent<TransformComponent>(coordEnt, out var coordXform))
|
||||
_rotation = coordEntRot;
|
||||
else
|
||||
_rotation = Angle.Zero;
|
||||
|
||||
var worldRot = _rotation.Value;
|
||||
var mapPos = _transform.ToMapCoordinates(_coordinates.Value).Offset(_rotation.Value.RotateVec(Offset));
|
||||
var mapCoord = _transform.ToCoordinates(mapPos);
|
||||
var worldToShuttle = Matrix3Helpers.CreateTranslation(-mapCoord.Position) * Matrix3Helpers.CreateRotation(-worldRot);
|
||||
Matrix3x2.Invert(worldToShuttle, out var shuttleToWorld);
|
||||
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
|
||||
var worldToView = worldToShuttle * shuttleToView;
|
||||
|
||||
@@ -334,8 +602,23 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
DrawShields(handle, xform, worldToShuttle);
|
||||
|
||||
// Frontier Corvax: north line drawing
|
||||
var rot = ourEntRot + _rotation.Value;
|
||||
DrawNorthLine(handle, rot);
|
||||
DrawNorthLine(handle, worldRot);
|
||||
|
||||
// Mono
|
||||
switch (_azimuthMode)
|
||||
{
|
||||
case RadarAzimuthMode.Edge:
|
||||
DrawAzimuthScale(handle, worldRot);
|
||||
break;
|
||||
case RadarAzimuthMode.Ring:
|
||||
DrawAzimuthRing(handle, worldRot);
|
||||
break;
|
||||
}
|
||||
|
||||
if (worldRot != Angle.Zero)
|
||||
{
|
||||
DrawCompassOverlay(handle, coordEntRot);
|
||||
}
|
||||
|
||||
// Draw our grid in detail
|
||||
var ourGridId = xform.GridUid;
|
||||
@@ -361,7 +644,7 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, radarPosVerts, Color.Lime);
|
||||
|
||||
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), rot, mapPos.Position);
|
||||
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), worldRot, mapPos.Position);
|
||||
var viewAABB = viewBounds.CalcBoundingBox();
|
||||
|
||||
_grids.Clear();
|
||||
@@ -426,9 +709,8 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
//shouldDrawIFF = NfCheckShouldDrawIffRangeCondition(shouldDrawIFF, mapCenter, curGridToWorld); // Frontier code
|
||||
// Frontier: range checks // Mono
|
||||
var gridMapPos = _transform.ToMapCoordinates(new EntityCoordinates(gUid, gridBody.LocalCenter)).Position;
|
||||
var ourPos = _transform.ToMapCoordinates(_coordinates.Value);
|
||||
if (!hideLabel) // Mono - show thermal signatures even at long range
|
||||
shouldDrawIFF = NfCheckShouldDrawIffRangeCondition(shouldDrawIFF, ourPos.Position - gridMapPos);
|
||||
shouldDrawIFF = NfCheckShouldDrawIffRangeCondition(shouldDrawIFF, mapPos.Position - gridMapPos);
|
||||
// End Frontier
|
||||
|
||||
// Mono
|
||||
@@ -629,7 +911,7 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
if (blip.Config.RespectZoom)
|
||||
box.Box = new Box2(box.Box.BottomLeft * MinimapScale, box.Box.TopRight * MinimapScale);
|
||||
if (blip.Config.Rotate)
|
||||
box.Rotation = ourEntRot - blip.Rotation;
|
||||
box.Rotation = worldRot - blip.Rotation;
|
||||
|
||||
if (blip.GridUid is { } grid)
|
||||
{
|
||||
@@ -873,11 +1155,24 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
|
||||
|
||||
protected Vector2 InverseScalePosition(Vector2 value)
|
||||
{
|
||||
// Account for UI scaling: value is unscaled, so adjust by UIScale
|
||||
var scaledValue = value * UIScale;
|
||||
return (scaledValue - MidPointVector) / MinimapScale;
|
||||
}
|
||||
|
||||
// Mono
|
||||
protected EntityCoordinates GetMouseEntityCoordinates(Vector2 relativePosition)
|
||||
{
|
||||
if (_coordinates is not { } cord || _rotation is not { } rot)
|
||||
return new();
|
||||
|
||||
// multiply by UIScale because UI is Very Intelligent and doesn't scale mouse position for whatever reason
|
||||
var screenRelativeWorldPos = InverseMapPosition(relativePosition * UIScale);
|
||||
var relativeWorldPos = rot.RotateVec(screenRelativeWorldPos);
|
||||
var coordEntRot = _transform.GetWorldRotation(cord.EntityId);
|
||||
var coords = cord.Offset((-coordEntRot).RotateVec(relativeWorldPos));
|
||||
return coords;
|
||||
}
|
||||
|
||||
public class BlipData
|
||||
{
|
||||
public bool IsOutsideRadarCircle { get; set; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Graphics;
|
||||
using Content.Shared.Silicons.StationAi;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
@@ -12,6 +13,10 @@ namespace Content.Client.Silicons.StationAi;
|
||||
|
||||
public sealed class StationAiOverlay : Overlay
|
||||
{
|
||||
private static readonly ProtoId<ShaderPrototype> CameraStaticShader = "CameraStatic";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
|
||||
private static readonly ProtoId<ShaderPrototype> StencilDrawShader = "StencilDraw";
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -22,8 +27,7 @@ public sealed class StationAiOverlay : Overlay
|
||||
|
||||
private readonly HashSet<Vector2i> _visibleTiles = new();
|
||||
|
||||
private IRenderTexture? _staticTexture;
|
||||
private IRenderTexture? _stencilTexture;
|
||||
private readonly OverlayResourceCache<CachedResources> _resources = new();
|
||||
|
||||
private float _updateRate = 1f / 30f;
|
||||
private float _accumulator;
|
||||
@@ -35,12 +39,14 @@ public sealed class StationAiOverlay : Overlay
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (_stencilTexture?.Texture.Size != args.Viewport.Size)
|
||||
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
|
||||
|
||||
if (res.StencilTexture?.Texture.Size != args.Viewport.Size)
|
||||
{
|
||||
_staticTexture?.Dispose();
|
||||
_stencilTexture?.Dispose();
|
||||
_stencilTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-stencil");
|
||||
_staticTexture = _clyde.CreateRenderTarget(args.Viewport.Size,
|
||||
res.StaticTexture?.Dispose();
|
||||
res.StencilTexture?.Dispose();
|
||||
res.StencilTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-stencil");
|
||||
res.StaticTexture = _clyde.CreateRenderTarget(args.Viewport.Size,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
|
||||
name: "station-ai-static");
|
||||
}
|
||||
@@ -74,7 +80,7 @@ public sealed class StationAiOverlay : Overlay
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||
|
||||
// Draw visible tiles to stencil
|
||||
worldHandle.RenderInRenderTarget(_stencilTexture!, () =>
|
||||
worldHandle.RenderInRenderTarget(res.StencilTexture!, () =>
|
||||
{
|
||||
worldHandle.SetTransform(matty);
|
||||
|
||||
@@ -87,11 +93,11 @@ public sealed class StationAiOverlay : Overlay
|
||||
Color.Transparent);
|
||||
|
||||
// Once this is gucci optimise rendering.
|
||||
worldHandle.RenderInRenderTarget(_staticTexture!,
|
||||
worldHandle.RenderInRenderTarget(res.StaticTexture!,
|
||||
() =>
|
||||
{
|
||||
worldHandle.SetTransform(invMatrix);
|
||||
var shader = _proto.Index<ShaderPrototype>("CameraStatic").Instance();
|
||||
var shader = _proto.Index(CameraStaticShader).Instance();
|
||||
worldHandle.UseShader(shader);
|
||||
worldHandle.DrawRect(worldBounds, Color.White);
|
||||
},
|
||||
@@ -100,12 +106,12 @@ public sealed class StationAiOverlay : Overlay
|
||||
// Not on a grid
|
||||
else
|
||||
{
|
||||
worldHandle.RenderInRenderTarget(_stencilTexture!, () =>
|
||||
worldHandle.RenderInRenderTarget(res.StencilTexture!, () =>
|
||||
{
|
||||
},
|
||||
Color.Transparent);
|
||||
|
||||
worldHandle.RenderInRenderTarget(_staticTexture!,
|
||||
worldHandle.RenderInRenderTarget(res.StaticTexture!,
|
||||
() =>
|
||||
{
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
@@ -114,15 +120,34 @@ public sealed class StationAiOverlay : Overlay
|
||||
}
|
||||
|
||||
// Use the lighting as a mask
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
|
||||
worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds);
|
||||
worldHandle.UseShader(_proto.Index(StencilMaskShader).Instance());
|
||||
worldHandle.DrawTextureRect(res.StencilTexture!.Texture, worldBounds);
|
||||
|
||||
// Draw the static
|
||||
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||
worldHandle.DrawTextureRect(_staticTexture!.Texture, worldBounds);
|
||||
worldHandle.UseShader(_proto.Index(StencilDrawShader).Instance());
|
||||
worldHandle.DrawTextureRect(res.StaticTexture!.Texture, worldBounds);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
worldHandle.UseShader(null);
|
||||
|
||||
}
|
||||
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_resources.Dispose();
|
||||
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private sealed class CachedResources : IDisposable
|
||||
{
|
||||
public IRenderTexture? StaticTexture;
|
||||
public IRenderTexture? StencilTexture;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StaticTexture?.Dispose();
|
||||
StencilTexture?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflection = default!;
|
||||
[Dependency] private readonly ClientClothingSystem _clothing = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -63,10 +64,10 @@ public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
|
||||
int index;
|
||||
if (_reflection.TryParseEnumReference(layer.Key, out var @enum))
|
||||
{
|
||||
if (!sprite.LayerMapTryGet(@enum, out index, logError: true))
|
||||
if (!_sprite.LayerMapTryGet((uid, sprite), @enum, out index, logMissing: true))
|
||||
continue;
|
||||
}
|
||||
else if (!sprite.LayerMapTryGet(layer.Key, out index))
|
||||
else if (!_sprite.LayerMapTryGet((uid, sprite), layer.Key, out index, false))
|
||||
{
|
||||
if (layer.Key is not { } strKey || !int.TryParse(strKey, out index))
|
||||
{
|
||||
@@ -74,8 +75,8 @@ public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sprite.LayerSetState(index, layer.Value.State);
|
||||
sprite.LayerSetColor(index, layer.Value.Color ?? Color.White);
|
||||
_sprite.LayerSetRsiState((uid, sprite), index, layer.Value.State);
|
||||
_sprite.LayerSetColor((uid, sprite), index, layer.Value.Color ?? Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Content.Client.Storage.Systems;
|
||||
public sealed class ItemCounterSystem : SharedItemCounterSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -41,23 +42,23 @@ public sealed class ItemCounterSystem : SharedItemCounterSystem
|
||||
public void ProcessOpaqueSprite(EntityUid uid, string layer, int count, int maxCount, List<string> states, bool hide = false, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(uid, ref sprite)
|
||||
|| !sprite.LayerMapTryGet(layer, out var layerKey, logError: true))
|
||||
|| !_sprite.LayerMapTryGet((uid, sprite), layer, out var layerKey, logMissing: true))
|
||||
return;
|
||||
|
||||
|
||||
var activeState = ContentHelpers.RoundToEqualLevels(count, maxCount, states.Count);
|
||||
sprite.LayerSetState(layerKey, states[activeState]);
|
||||
sprite.LayerSetVisible(layerKey, !hide);
|
||||
_sprite.LayerSetRsiState((uid, sprite), layerKey, states[activeState]);
|
||||
_sprite.LayerSetVisible((uid, sprite), layerKey, !hide);
|
||||
}
|
||||
|
||||
public void ProcessCompositeSprite(EntityUid uid, int count, int maxCount, List<string> layers, bool hide = false, SpriteComponent? sprite = null)
|
||||
{
|
||||
if(!Resolve(uid, ref sprite))
|
||||
if (!Resolve(uid, ref sprite))
|
||||
return;
|
||||
|
||||
|
||||
var activeTill = ContentHelpers.RoundToNearestLevels(count, maxCount, layers.Count);
|
||||
for(var i = 0; i < layers.Count; ++i)
|
||||
for (var i = 0; i < layers.Count; ++i)
|
||||
{
|
||||
sprite.LayerSetVisible(layers[i], !hide && i < activeTill);
|
||||
_sprite.LayerSetVisible((uid, sprite), layers[i], !hide && i < activeTill);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Content.Client.Storage.Systems;
|
||||
public sealed class ItemMapperSystem : SharedItemMapperSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -48,9 +49,9 @@ public sealed class ItemMapperSystem : SharedItemMapperSystem
|
||||
|
||||
foreach (var sprite in component.SpriteLayers)
|
||||
{
|
||||
spriteComponent.LayerMapReserveBlank(sprite);
|
||||
spriteComponent.LayerSetSprite(sprite, new SpriteSpecifier.Rsi(component.RSIPath!.Value, sprite));
|
||||
spriteComponent.LayerSetVisible(sprite, false);
|
||||
_sprite.LayerMapReserve((owner, spriteComponent), sprite);
|
||||
_sprite.LayerSetSprite((owner, spriteComponent), sprite, new SpriteSpecifier.Rsi(component.RSIPath!.Value, sprite));
|
||||
_sprite.LayerSetVisible((owner, spriteComponent), sprite, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ public sealed class ItemMapperSystem : SharedItemMapperSystem
|
||||
foreach (var layerName in component.SpriteLayers)
|
||||
{
|
||||
var show = wrapper.QueuedEntities.Contains(layerName);
|
||||
spriteComponent.LayerSetVisible(layerName, show);
|
||||
_sprite.LayerSetVisible((owner, spriteComponent), layerName, show);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.Storage.Systems;
|
||||
/// <inheritdoc cref="StorageContainerVisualsComponent"/>
|
||||
public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageContainerVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, StorageContainerVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
@@ -19,9 +21,9 @@ public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageCont
|
||||
if (!AppearanceSystem.TryGetData<int>(uid, StorageVisuals.Capacity, out var capacity, args.Component))
|
||||
return;
|
||||
|
||||
var fraction = used / (float) capacity;
|
||||
var fraction = used / (float)capacity;
|
||||
|
||||
if (!args.Sprite.LayerMapTryGet(component.FillLayer, out var fillLayer))
|
||||
if (!_sprite.LayerMapTryGet((uid, args.Sprite), component.FillLayer, out var fillLayer, false))
|
||||
return;
|
||||
|
||||
var closestFillSprite = Math.Min(ContentHelpers.RoundToNearestLevels(fraction, 1, component.MaxFillLevels + 1),
|
||||
@@ -32,13 +34,13 @@ public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageCont
|
||||
if (component.FillBaseName == null)
|
||||
return;
|
||||
|
||||
args.Sprite.LayerSetVisible(fillLayer, true);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), fillLayer, true);
|
||||
var stateName = component.FillBaseName + closestFillSprite;
|
||||
args.Sprite.LayerSetState(fillLayer, stateName);
|
||||
_sprite.LayerSetRsiState((uid, args.Sprite), fillLayer, stateName);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetVisible(fillLayer, false);
|
||||
_sprite.LayerSetVisible((uid, args.Sprite), fillLayer, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow
|
||||
|
||||
CameraView.ViewportSize = new Vector2i(500, 500);
|
||||
CameraView.Eye = _defaultEye; // sure
|
||||
CameraView.Shader = _prototypeManager.Index<ShaderPrototype>("crt_vhs").Instance().Duplicate(); // Mono - fix shader ID
|
||||
CameraViewBackground.Stretch = TextureRect.StretchMode.Scale;
|
||||
CameraViewBackground.Texture = texture;
|
||||
CameraViewBackground.ShaderOverride = shader;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
/// <summary>
|
||||
/// Component that handles the toggling the visuals of some light emitting entity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will toggle the visibility of layers on an entity's sprite, the in-hand visuals, and the clothing/equipment
|
||||
/// visuals. This will modify the color of any attached point lights.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ToggleableLightVisualsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sprite layer that will have its visibility toggled when this item is toggled.
|
||||
/// </summary>
|
||||
[DataField("spriteLayer")]
|
||||
public string? SpriteLayer = "light";
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is holding this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField("inhandVisuals")]
|
||||
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is wearing this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField("clothingVisuals")]
|
||||
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using Content.Client.Clothing;
|
||||
using Content.Client.Items.Systems;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
public sealed class ToggleableLightVisualsSystem : VisualizerSystem<ToggleableLightVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _lights = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ToggleableLightVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals, after: new[] { typeof(ItemSystem) });
|
||||
SubscribeLocalEvent<ToggleableLightVisualsComponent, GetEquipmentVisualsEvent>(OnGetEquipmentVisuals, after: new[] { typeof(ClientClothingSystem) });
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, ToggleableLightVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, args.Component))
|
||||
return;
|
||||
|
||||
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, args.Component);
|
||||
|
||||
// Update the item's sprite
|
||||
if (args.Sprite != null && component.SpriteLayer != null && args.Sprite.LayerMapTryGet(component.SpriteLayer, out var layer))
|
||||
{
|
||||
args.Sprite.LayerSetVisible(layer, enabled);
|
||||
if (modulate)
|
||||
args.Sprite.LayerSetColor(layer, color);
|
||||
}
|
||||
|
||||
// Update any point-lights
|
||||
if (TryComp(uid, out PointLightComponent? light))
|
||||
{
|
||||
DebugTools.Assert(!light.NetSyncEnabled, "light visualizers require point lights without net-sync");
|
||||
_lights.SetEnabled(uid, enabled, light);
|
||||
if (enabled && modulate)
|
||||
{
|
||||
_lights.SetColor(uid, color, light);
|
||||
}
|
||||
}
|
||||
|
||||
// update clothing & in-hand visuals.
|
||||
_itemSys.VisualsChanged(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the unshaded light overlays to any clothing sprites.
|
||||
/// </summary>
|
||||
private void OnGetEquipmentVisuals(EntityUid uid, ToggleableLightVisualsComponent component, GetEquipmentVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
|
||||
return;
|
||||
List<PrototypeLayerData>? layers = null;
|
||||
|
||||
// attempt to get species specific data
|
||||
if (inventory.SpeciesId != null)
|
||||
component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
|
||||
|
||||
// No species specific data. Try to default to generic data.
|
||||
if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
|
||||
return;
|
||||
|
||||
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulate)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetHeldVisuals(EntityUid uid, ToggleableLightVisualsComponent component, GetInhandVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!component.InhandVisuals.TryGetValue(args.Location, out var layers))
|
||||
return;
|
||||
|
||||
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle";
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulate)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
/// <summary>
|
||||
/// Component that handles toggling the visuals of an entity, including layers on an entity's sprite,
|
||||
/// the in-hand visuals, and the clothing/equipment visuals.
|
||||
/// </summary>
|
||||
/// <see cref="ToggleableVisualsSystem"/>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ToggleableVisualsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sprite layer that will have its visibility toggled when this item is toggled.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public string? SpriteLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is holding this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is wearing this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Clothing;
|
||||
using Content.Client.Items.Systems;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the behavior of <see cref="ToggleableVisualsComponent"/> by reacting to
|
||||
/// <see cref="AppearanceChangeEvent"/>, for the sprite directly; <see cref="OnGetHeldVisuals"/> for the
|
||||
/// in-hand visuals; and <see cref="OnGetEquipmentVisuals"/> for the clothing visuals.
|
||||
/// </summary>
|
||||
/// <see cref="ToggleableVisualsComponent"/>
|
||||
public sealed class ToggleableVisualsSystem : VisualizerSystem<ToggleableVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ToggleableVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals,
|
||||
after: [typeof(ItemSystem)]);
|
||||
SubscribeLocalEvent<ToggleableVisualsComponent, GetEquipmentVisualsEvent>(OnGetEquipmentVisuals,
|
||||
after: [typeof(ClientClothingSystem)]);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid,
|
||||
ToggleableVisualsComponent component,
|
||||
ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, args.Component))
|
||||
return;
|
||||
|
||||
var modulateColor =
|
||||
AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, args.Component);
|
||||
|
||||
// Update the item's sprite
|
||||
if (args.Sprite != null && component.SpriteLayer != null &&
|
||||
SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.SpriteLayer, out var layer, false))
|
||||
{
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), layer, enabled);
|
||||
if (modulateColor)
|
||||
SpriteSystem.LayerSetColor((uid, args.Sprite), component.SpriteLayer, color);
|
||||
}
|
||||
|
||||
// If there's a `ItemTogglePointLightComponent` that says to apply the color to attached lights, do so.
|
||||
if (TryComp<ItemTogglePointLightComponent>(uid, out var toggleLights) &&
|
||||
TryComp(uid, out PointLightComponent? light))
|
||||
{
|
||||
DebugTools.Assert(!light.NetSyncEnabled,
|
||||
$"{typeof(ItemTogglePointLightComponent)} requires point lights without net-sync");
|
||||
_pointLight.SetEnabled(uid, enabled, light);
|
||||
if (modulateColor && toggleLights.ToggleableVisualsColorModulatesLights)
|
||||
{
|
||||
_pointLight.SetColor(uid, color, light);
|
||||
}
|
||||
}
|
||||
|
||||
// update clothing & in-hand visuals.
|
||||
_item.VisualsChanged(uid);
|
||||
}
|
||||
|
||||
private void OnGetEquipmentVisuals(EntityUid uid,
|
||||
ToggleableVisualsComponent component,
|
||||
GetEquipmentVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
|
||||
return;
|
||||
List<PrototypeLayerData>? layers = null;
|
||||
|
||||
// attempt to get species specific data
|
||||
if (inventory.SpeciesId != null)
|
||||
component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
|
||||
|
||||
// No species specific data. Try to default to generic data.
|
||||
if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
|
||||
return;
|
||||
|
||||
var modulateColor = AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulateColor)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetHeldVisuals(EntityUid uid, ToggleableVisualsComponent component, GetInhandVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!component.InhandVisuals.TryGetValue(args.Location, out var layers))
|
||||
return;
|
||||
|
||||
var modulateColor = AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle";
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulateColor)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@ public sealed partial class ChatUIController : IOnSystemChanged<CharacterInfoSys
|
||||
}
|
||||
|
||||
// Make it so it doesn't hightlight your own name when you are talking
|
||||
keyword = StartAtSign.Replace(keyword, @"(?<=\[BubbleContent\].*)");
|
||||
keyword = StartAtSign.Replace(keyword, @"(?<=\[BubbleContent\].*|""\[/color\].*)");
|
||||
|
||||
return keyword;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public sealed class DamageOverlayUiController : UIController
|
||||
{
|
||||
if (mobState == null && !EntityManager.TryGetComponent(entity, out mobState) ||
|
||||
thresholds == null && !EntityManager.TryGetComponent(entity, out thresholds) ||
|
||||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
|
||||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
|
||||
return;
|
||||
|
||||
if (!_mobThresholdSystem.TryGetIncapThreshold(entity, out var foundThreshold, thresholds))
|
||||
@@ -94,47 +94,48 @@ public sealed class DamageOverlayUiController : UIController
|
||||
switch (mobState.CurrentState)
|
||||
{
|
||||
case MobState.Alive:
|
||||
{
|
||||
if (EntityManager.HasComponent<PainNumbnessComponent>(entity))
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
else if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||
{
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
||||
}
|
||||
if (!EntityManager.HasComponent<PainNumbnessComponent>(entity)) // Mono - makes this look better
|
||||
{
|
||||
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
||||
if (damageable.DamagePerGroup.TryGetValue("Burn", out var burnDamage)) // Adds burn damage to pain overlay - Mono
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, _overlay.BruteLevel + (burnDamage / critThreshold)).Float();
|
||||
}
|
||||
else
|
||||
_overlay.BruteLevel = 0;
|
||||
|
||||
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
|
||||
{
|
||||
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
|
||||
}
|
||||
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
|
||||
{
|
||||
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
|
||||
}
|
||||
|
||||
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
|
||||
_overlay.CritLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
_overlay.CritLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Critical:
|
||||
{
|
||||
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
|
||||
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
|
||||
return;
|
||||
_overlay.CritLevel = critLevel.Value.Float();
|
||||
{
|
||||
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
|
||||
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
|
||||
return;
|
||||
_overlay.CritLevel = critLevel.Value.Float();
|
||||
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Dead:
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.CritLevel = 0;
|
||||
break;
|
||||
}
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.CritLevel = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Content.Client.VendingMachines.UI
|
||||
/// Populates the list of available items on the vending machine interface
|
||||
/// and sets icons based on their prototypes
|
||||
/// </summary>
|
||||
public void Populate(List<VendingMachineInventoryEntry> inventory, float priceModifier, int balance, int? cashSlotBalance) // Frontier: add balance, cashSlotBalance
|
||||
public void Populate(List<VendingMachineInventoryEntry> inventory, float priceModifier, int balance, int? cashSlotBalance, bool requiresCash) // Frontier: add balance, cashSlotBalance
|
||||
{
|
||||
UpdateBalance(balance); // Frontier
|
||||
UpdateCashSlotBalance(cashSlotBalance); // Frontier
|
||||
@@ -124,84 +124,11 @@ namespace Content.Client.VendingMachines.UI
|
||||
_dummies.Add(entry.ID, dummy);
|
||||
}
|
||||
|
||||
// Frontier: item pricing
|
||||
// ok so we dont really have access to the pricing system so we are doing a quick price check
|
||||
// based on prototype info since the items inside a vending machine dont actually exist as entities
|
||||
// until they are spawned. So this little alg does the following:
|
||||
// first, checks for a staticprice component, and if it has one, checks to make sure its not 0 since
|
||||
// stacks and other items have 0 cost.
|
||||
// If the price is 0, then we check for both a stack price and a stack component, since if it has one
|
||||
// it should have the other too, and then calculates the price based on that.
|
||||
// If the price is still 0 or non-existant (this is the case for food and containers since their value is
|
||||
// determined dynamically by their contents/inventory), it then falls back to the default mystery
|
||||
// hardcoded value of 20xMarketModifier.
|
||||
var cost = 20;
|
||||
if (prototype != null && prototype.TryGetComponent<StaticPriceComponent>(out var priceComponent, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceComponent.Price != 0)
|
||||
{
|
||||
var price = (float)priceComponent.Price;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prototype.TryGetComponent<StackPriceComponent>(out var stackPrice, _entityManager.ComponentFactory)
|
||||
&& prototype.TryGetComponent<StackComponent>(out var stack, _entityManager.ComponentFactory))
|
||||
{
|
||||
var price = stackPrice.Price * stack.Count;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
|
||||
if (prototype != null && prototype.TryGetComponent<SolutionContainerManagerComponent>(out var priceSolutions, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceSolutions.Solutions != null)
|
||||
{
|
||||
foreach (var solution in priceSolutions.Solutions.Values)
|
||||
{
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype,
|
||||
out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
var costReagent = quantity.Float() * reagentProto.PricePerUnit;
|
||||
cost += (int)(costReagent * priceModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End Frontier: item pricing
|
||||
|
||||
// Frontier: calculate vending price (this duplicates Content.Server.PricingSystem.GetVendPrice - this should be moved to Content.Shared if possible)
|
||||
if (prototype != null)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
if (prototype.TryGetComponent<StaticPriceComponent>(out var staticComp, _entityManager.ComponentFactory) && staticComp.VendPrice > 0.0)
|
||||
{
|
||||
price += staticComp.VendPrice;
|
||||
}
|
||||
else if (prototype.TryGetComponent<StackPriceComponent>(out var stackComp, _entityManager.ComponentFactory) && stackComp.VendPrice > 0.0)
|
||||
{
|
||||
price += stackComp.VendPrice;
|
||||
}
|
||||
|
||||
// If there is anything that explicitly sets vending price - higher OR lower, override the base.
|
||||
if (price > 0.0)
|
||||
{
|
||||
cost = (int)price;
|
||||
}
|
||||
}
|
||||
// End Frontier
|
||||
|
||||
var itemName = Identity.Name(dummy, _entityManager);
|
||||
var cost = 0; // mono
|
||||
if (requiresCash) // frontier
|
||||
cost = GetPrice(entry, prototype, priceModifier);
|
||||
|
||||
string itemText;
|
||||
|
||||
// Frontier: unlimited vending
|
||||
@@ -211,7 +138,6 @@ namespace Content.Client.VendingMachines.UI
|
||||
itemText = $"[{BankSystemExtensions.ToSpesoString(cost)}] {itemName}";
|
||||
// End Frontier: unlimited vending
|
||||
|
||||
|
||||
if (itemText.Length > longestEntry.Length)
|
||||
longestEntry = itemText;
|
||||
|
||||
@@ -223,6 +149,88 @@ namespace Content.Client.VendingMachines.UI
|
||||
SetSizeAfterUpdate(longestEntry.Length, inventory.Count);
|
||||
}
|
||||
|
||||
// Mono: Moved out frontier pricing logic to the separate method
|
||||
private int GetPrice(VendingMachineInventoryEntry entry, EntityPrototype? prototype, float priceModifier){
|
||||
// Frontier: item pricing
|
||||
// ok so we dont really have access to the pricing system so we are doing a quick price check
|
||||
// based on prototype info since the items inside a vending machine dont actually exist as entities
|
||||
// until they are spawned. So this little alg does the following:
|
||||
// first, checks for a staticprice component, and if it has one, checks to make sure its not 0 since
|
||||
// stacks and other items have 0 cost.
|
||||
// If the price is 0, then we check for both a stack price and a stack component, since if it has one
|
||||
// it should have the other too, and then calculates the price based on that.
|
||||
// If the price is still 0 or non-existant (this is the case for food and containers since their value is
|
||||
// determined dynamically by their contents/inventory), it then falls back to the default mystery
|
||||
// hardcoded value of 20xMarketModifier.
|
||||
var cost = 20;
|
||||
if (prototype != null && prototype.TryGetComponent<StaticPriceComponent>(out var priceComponent, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceComponent.Price != 0)
|
||||
{
|
||||
var price = (float)priceComponent.Price;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prototype.TryGetComponent<StackPriceComponent>(out var stackPrice, _entityManager.ComponentFactory)
|
||||
&& prototype.TryGetComponent<StackComponent>(out var stack, _entityManager.ComponentFactory))
|
||||
{
|
||||
var price = stackPrice.Price * stack.Count;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
|
||||
if (prototype != null && prototype.TryGetComponent<SolutionContainerManagerComponent>(out var priceSolutions, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceSolutions.Solutions != null)
|
||||
{
|
||||
foreach (var solution in priceSolutions.Solutions.Values)
|
||||
{
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype,
|
||||
out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
var costReagent = quantity.Float() * reagentProto.PricePerUnit;
|
||||
cost += (int)(costReagent * priceModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End Frontier: item pricing
|
||||
|
||||
// Frontier: calculate vending price (this duplicates Content.Server.PricingSystem.GetVendPrice - this should be moved to Content.Shared if possible)
|
||||
if (prototype != null)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
if (prototype.TryGetComponent<StaticPriceComponent>(out var staticComp, _entityManager.ComponentFactory) && staticComp.VendPrice > 0.0)
|
||||
{
|
||||
price += staticComp.VendPrice;
|
||||
}
|
||||
else if (prototype.TryGetComponent<StackPriceComponent>(out var stackComp, _entityManager.ComponentFactory) && stackComp.VendPrice > 0.0)
|
||||
{
|
||||
price += stackComp.VendPrice;
|
||||
}
|
||||
|
||||
// If there is anything that explicitly sets vending price - higher OR lower, override the base.
|
||||
if (price > 0.0)
|
||||
{
|
||||
cost = (int)price;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
// End Frontier
|
||||
}
|
||||
|
||||
// Frontier
|
||||
public void UpdateBalance(int balance)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace Content.Client.VendingMachines
|
||||
[ViewVariables]
|
||||
private int _cashSlotBalance = 0;
|
||||
// End Frontier
|
||||
[ViewVariables]
|
||||
private bool _requiresCash; // mono
|
||||
|
||||
public VendingMachineBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
@@ -74,6 +76,7 @@ namespace Content.Client.VendingMachines
|
||||
if (EntMan.TryGetComponent<VendingMachineComponent>(Owner, out var vendingMachine))
|
||||
{
|
||||
_cashSlotBalance = vendingMachine.CashSlotBalance;
|
||||
_requiresCash = vendingMachine.RequiresCash; // mono
|
||||
if (vendingMachine.CashSlotName != null)
|
||||
cashSlotValue = _cashSlotBalance;
|
||||
}
|
||||
@@ -83,7 +86,7 @@ namespace Content.Client.VendingMachines
|
||||
}
|
||||
// End Frontier
|
||||
|
||||
_menu?.Populate(_cachedInventory, _mod, _balance, cashSlotValue); // Frontier: add _balance
|
||||
_menu?.Populate(_cachedInventory, _mod, _balance, cashSlotValue, _requiresCash); // Frontier: add _balance, mono: add _requiresCash
|
||||
}
|
||||
|
||||
private void OnItemSelected(GUIBoundKeyEventArgs args, ListData data)
|
||||
|
||||
@@ -37,6 +37,10 @@ namespace Content.Client.Viewport
|
||||
|
||||
private readonly List<CopyPixelsDelegate<Rgba32>> _queuedScreenshots = new();
|
||||
|
||||
// Fire edit start
|
||||
public ShaderInstance? Shader;
|
||||
// Fire edit end
|
||||
|
||||
public int CurrentRenderScale => _curRenderScale;
|
||||
|
||||
/// <summary>
|
||||
@@ -170,7 +174,11 @@ namespace Content.Client.Viewport
|
||||
var drawBox = GetDrawBox();
|
||||
var drawBoxGlobal = drawBox.Translated(GlobalPixelPosition);
|
||||
_viewport.RenderScreenOverlaysBelow(handle, this, drawBoxGlobal);
|
||||
// Fire edit start
|
||||
handle.DrawingHandleScreen.UseShader(Shader);
|
||||
handle.DrawingHandleScreen.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox);
|
||||
handle.DrawingHandleScreen.UseShader(null);
|
||||
// Fire edit end
|
||||
_viewport.RenderScreenOverlaysAbove(handle, this, drawBoxGlobal);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Content.Client.Weapons.Marker;
|
||||
public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -20,16 +21,16 @@ public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
|
||||
if (!_timing.ApplyingState || component.Effect == null || !TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
var layer = sprite.LayerMapReserveBlank(DamageMarkerKey.Key);
|
||||
sprite.LayerSetState(layer, component.Effect.RsiState, component.Effect.RsiPath);
|
||||
var layer = _sprite.LayerMapReserve((uid, sprite), DamageMarkerKey.Key);
|
||||
_sprite.LayerSetRsi((uid, sprite), layer, component.Effect.RsiPath, component.Effect.RsiState);
|
||||
}
|
||||
|
||||
private void OnMarkerShutdown(EntityUid uid, DamageMarkerComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!_timing.ApplyingState || !TryComp<SpriteComponent>(uid, out var sprite) || !sprite.LayerMapTryGet(DamageMarkerKey.Key, out var weh))
|
||||
if (!_timing.ApplyingState || !TryComp<SpriteComponent>(uid, out var sprite) || !_sprite.LayerMapTryGet((uid, sprite), DamageMarkerKey.Key, out var weh, false))
|
||||
return;
|
||||
|
||||
sprite.RemoveLayer(weh);
|
||||
_sprite.RemoveLayer((uid, sprite), weh);
|
||||
}
|
||||
|
||||
private enum DamageMarkerKey : byte
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class MeleeWeaponSystem
|
||||
/// <summary>
|
||||
/// Does all of the melee effects for a player that are predicted, i.e. character lunge and weapon animation.
|
||||
/// </summary>
|
||||
public override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true)
|
||||
public override void DoLunge(EntityUid user, EntityUid playerUid, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true)
|
||||
{
|
||||
if (!Timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
@@ -66,10 +66,11 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
|
||||
var entity = entityNull.Value;
|
||||
|
||||
if (!TryGetWeapon(entity, out var weaponUid, out var weapon))
|
||||
// Mono - add user override
|
||||
if (!TryGetWeapon(entity, out var weaponUid, out var weapon, out var userOverride))
|
||||
return;
|
||||
|
||||
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(entity, weapon: (weaponUid, weapon)))
|
||||
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(userOverride, weapon: (weaponUid, weapon)))
|
||||
{
|
||||
weapon.Attacking = false;
|
||||
return;
|
||||
@@ -120,15 +121,15 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
switch(altFireComponent.AttackType)
|
||||
{
|
||||
case AltFireAttackType.Light:
|
||||
ClientLightAttack(entity, mousePos, coordinates, weaponUid, weapon);
|
||||
ClientLightAttack(userOverride, mousePos, coordinates, weaponUid, weapon);
|
||||
break;
|
||||
|
||||
case AltFireAttackType.Heavy:
|
||||
ClientHeavyAttack(entity, coordinates, weaponUid, weapon);
|
||||
ClientHeavyAttack(userOverride, coordinates, weaponUid, weapon);
|
||||
break;
|
||||
|
||||
case AltFireAttackType.Disarm:
|
||||
ClientDisarm(entity, mousePos, coordinates);
|
||||
ClientDisarm(userOverride, mousePos, coordinates);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
if (altDown == BoundKeyState.Down)
|
||||
{
|
||||
// If it's an unarmed attack then do a disarm
|
||||
if (weapon.AltDisarm && weaponUid == entity)
|
||||
if (weapon.AltDisarm && weaponUid == userOverride)
|
||||
{
|
||||
EntityUid? target = null;
|
||||
|
||||
@@ -155,7 +156,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
// WD EDIT START
|
||||
if (TryComp(weaponUid, out BlinkComponent? blink) && blink.IsActive)
|
||||
{
|
||||
if (!_xformQuery.TryGetComponent(entity, out var userXform))
|
||||
if (!_xformQuery.TryGetComponent(userOverride, out var userXform))
|
||||
return;
|
||||
|
||||
var targetMap = _transform.ToMapCoordinates(coordinates);
|
||||
@@ -171,14 +172,14 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
}
|
||||
// WD EDIT END
|
||||
|
||||
ClientHeavyAttack(entity, coordinates, weaponUid, weapon);
|
||||
ClientHeavyAttack(userOverride, coordinates, weaponUid, weapon);
|
||||
return;
|
||||
}
|
||||
|
||||
// Light attack
|
||||
if (useDown == BoundKeyState.Down)
|
||||
{
|
||||
var attackerPos = TransformSystem.GetMapCoordinates(entity);
|
||||
var attackerPos = TransformSystem.GetMapCoordinates(userOverride);
|
||||
|
||||
if (mousePos.MapId != attackerPos.MapId ||
|
||||
(attackerPos.Position - mousePos.Position).Length() > weapon.Range)
|
||||
@@ -193,15 +194,15 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
target = screen.GetDamageableClickedEntity(mousePos); // Goob edit
|
||||
}
|
||||
|
||||
// Don't light-attack if interaction will be handling this instead
|
||||
if (Interaction.CombatModeCanHandInteract(entity, target))
|
||||
// Don't light-attack if interaction will be handling this instead // Mono - add hands check (why is this duplicated?)
|
||||
if (HasComp<HandsComponent>(userOverride) && Interaction.CombatModeCanHandInteract(userOverride, target))
|
||||
return;
|
||||
|
||||
RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates)));
|
||||
}
|
||||
|
||||
if (useDown == BoundKeyState.Down)
|
||||
ClientLightAttack(entity, mousePos, coordinates, weaponUid, weapon);
|
||||
ClientLightAttack(userOverride, mousePos, coordinates, weaponUid, weapon);
|
||||
}
|
||||
|
||||
protected override bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session)
|
||||
@@ -298,8 +299,8 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
if (_stateManager.CurrentState is GameplayStateBase screen)
|
||||
target = screen.GetClickedEntity(mousePos);
|
||||
|
||||
// Don't light-attack if interaction will be handling this instead
|
||||
if (Interaction.CombatModeCanHandInteract(attacker, target))
|
||||
// Don't light-attack if interaction will be handling this instead // Mono - only if attacker has hands
|
||||
if (HasComp<HandsComponent>(attacker) && Interaction.CombatModeCanHandInteract(attacker, target))
|
||||
return;
|
||||
|
||||
RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates)));
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly MapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -33,7 +34,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||
if (!TryComp<SpriteComponent>(component.Tethered, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Color = component.LineColor;
|
||||
_sprite.SetColor((component.Tethered.Value, sprite), component.LineColor);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -58,7 +59,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||
var player = _player.LocalEntity;
|
||||
|
||||
if (player == null ||
|
||||
!TryGetTetherGun(player.Value, out var gunUid, out var gun) ||
|
||||
!TryGetTetherGun(player.Value, out _, out var gun) ||
|
||||
gun.TetherEntity == null)
|
||||
{
|
||||
return;
|
||||
@@ -81,11 +82,11 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||
coords = TransformSystem.ToCoordinates(_mapSystem.GetMap(mouseWorldPos.MapId), mouseWorldPos);
|
||||
}
|
||||
|
||||
const float BufferDistance = 0.1f;
|
||||
const float bufferDistance = 0.1f;
|
||||
|
||||
if (TryComp(gun.TetherEntity, out TransformComponent? tetherXform) &&
|
||||
tetherXform.Coordinates.TryDistance(EntityManager, TransformSystem, coords, out var distance) &&
|
||||
distance < BufferDistance)
|
||||
distance < bufferDistance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -105,11 +106,11 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||
|
||||
if (TryComp<ForceGunComponent>(component.Tetherer, out var force))
|
||||
{
|
||||
sprite.Color = force.LineColor;
|
||||
_sprite.SetColor((uid, sprite), force.LineColor);
|
||||
}
|
||||
else if (TryComp<TetherGunComponent>(component.Tetherer, out var tether))
|
||||
{
|
||||
sprite.Color = tether.LineColor;
|
||||
_sprite.SetColor((uid, sprite), tether.LineColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +119,6 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Color = Color.White;
|
||||
_sprite.SetColor((uid, sprite), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Borgs;
|
||||
|
||||
public sealed partial class AiRemoteControlSystem : SharedAiRemoteControlSystem
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="0 0 0 10">
|
||||
<PanelContainer VerticalExpand="True" StyleClasses="BackgroundDark">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
Margin="5 5 5 5">
|
||||
<customControls:HSeparator Margin="0 5 0 5"/>
|
||||
<RichTextLabel Name="DeviceName"/>
|
||||
<BoxContainer Name="RemoteControlButtons" Orientation="Horizontal" HorizontalExpand="True" Margin="0 5 0 0">
|
||||
<Button Name="MoveButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-remote-ui-menu-moveto}"/>
|
||||
<Control Margin="2 0 0 0"/>
|
||||
<Button Name="TakeControlButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-remote-control}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RemoteDeviceDisplay : Control
|
||||
{
|
||||
public event Action<RemoteDeviceActionEvent>? OnRemoteDeviceAction;
|
||||
|
||||
public RemoteDeviceDisplay(NetEntity netEntityUid, string displayName)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
DeviceName.SetMessage(displayName);
|
||||
|
||||
MoveButton.OnPressed += _ =>
|
||||
{
|
||||
OnRemoteDeviceAction?.Invoke(new RemoteDeviceActionEvent(
|
||||
RemoteDeviceActionEvent.RemoteDeviceActionType.MoveToDevice,
|
||||
netEntityUid));
|
||||
};
|
||||
|
||||
TakeControlButton.OnPressed += _ =>
|
||||
{
|
||||
OnRemoteDeviceAction?.Invoke(new RemoteDeviceActionEvent(
|
||||
RemoteDeviceActionEvent.RemoteDeviceActionType.TakeControl,
|
||||
netEntityUid));
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using static Content.Shared._CorvaxNext.Silicons.Borgs.Components.AiRemoteControllerComponent;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
|
||||
|
||||
public sealed class RemoteDevicesBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private RemoteDevicesMenu? _menu;
|
||||
private EntityUid _owner;
|
||||
|
||||
public RemoteDevicesBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<RemoteDevicesMenu>();
|
||||
_menu.OnRemoteDeviceAction += SendAction;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not RemoteDevicesBuiState msg)
|
||||
return;
|
||||
|
||||
_menu?.Update(_owner, msg);
|
||||
}
|
||||
|
||||
public void SendAction(RemoteDeviceActionEvent action)
|
||||
{
|
||||
SendMessage(new RemoteDeviceActionMessage(action));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc ai-remote-ui-menu-title}"
|
||||
MinSize="355 100"
|
||||
SetSize="355 415">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<PanelContainer VerticalExpand="True" Margin="10 10 10 10">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer
|
||||
HScrollEnabled="False"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RemoteDevicesDisplayContainer"
|
||||
Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
Margin="10 10 10 0">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RemoteDevicesMenu : FancyWindow
|
||||
{
|
||||
public event Action<RemoteDeviceActionEvent>? OnRemoteDeviceAction;
|
||||
|
||||
public RemoteDevicesMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void Update(EntityUid uid, RemoteDevicesBuiState state)
|
||||
{
|
||||
RemoteDevicesDisplayContainer.Children.Clear();
|
||||
|
||||
if (state.DeviceList == null)
|
||||
return;
|
||||
|
||||
foreach (var device in state.DeviceList)
|
||||
{
|
||||
var control = new RemoteDeviceDisplay(device.NetEntityUid, device.DisplayName);
|
||||
|
||||
control.OnRemoteDeviceAction += action =>
|
||||
{
|
||||
OnRemoteDeviceAction?.Invoke(action);
|
||||
};
|
||||
|
||||
RemoteDevicesDisplayContainer.AddChild(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,13 @@
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<ui:ShuttleNavControl Name="NavRadar"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
MinHeight="400"/>
|
||||
<!-- Mono - wrap -->
|
||||
<LayoutContainer>
|
||||
<ui:ShuttleNavControl Name="NavRadar"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
MinHeight="400"/>
|
||||
</LayoutContainer>
|
||||
|
||||
</GridContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed class ShipShieldOverlay : Overlay
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
private readonly List<DrawVertexUV2D> _verts = new(128); // Mono
|
||||
private readonly Texture _shieldTexture;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld;
|
||||
|
||||
@@ -30,6 +31,7 @@ public sealed class ShipShieldOverlay : Overlay
|
||||
_entManager = entityManager;
|
||||
_fixture = _entManager.EntitySysManager.GetEntitySystem<FixtureSystem>();
|
||||
_physics = _entManager.EntitySysManager.GetEntitySystem<Robust.Client.Physics.PhysicsSystem>();
|
||||
_shieldTexture = _resourceCache.GetTexture("/Textures/_Crescent/ShipShields/shieldtex.png");
|
||||
|
||||
_unshadedShader = prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
|
||||
@@ -56,9 +58,7 @@ public sealed class ShipShieldOverlay : Overlay
|
||||
if (fixture is not { Shape: ChainShape chain })
|
||||
continue;
|
||||
|
||||
var texture = _resourceCache.GetTexture("/Textures/_Crescent/ShipShields/shieldtex.png");
|
||||
|
||||
DrawShield(handle, uid, chain, xform, texture, visuals.ShieldColor, _verts);
|
||||
DrawShield(handle, uid, chain, xform, _shieldTexture, visuals.ShieldColor, _verts);
|
||||
_verts.Clear(); // Clear for next shield - Mono
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using Robust.Shared.Map;
|
||||
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
|
||||
using Content.Client.Examine;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
|
||||
namespace Content.Client._FarHorizons.Power.Generation.FissionGenerator;
|
||||
|
||||
public sealed class NuclearReactorSystem : SharedNuclearReactorSystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NuclearReactorComponent, ComponentInit>(OnInit);
|
||||
|
||||
SubscribeLocalEvent<NuclearReactorComponent, ClientExaminedEvent>(ReactorExamined);
|
||||
SubscribeLocalEvent<NuclearReactorComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, NuclearReactorComponent comp, ref ComponentInit args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!_resourceCache.TryGetResource("/Textures/_FarHorizons/Structures/Power/Generation/FissionGenerator/reactor_component_cap.rsi", out RSIResource? resource))
|
||||
return;
|
||||
|
||||
Entity<SpriteComponent?> entSprite = (uid, sprite);
|
||||
var xspace = comp.Gridbounds[0] / 32f;
|
||||
var yspace = comp.Gridbounds[1] / 32f;
|
||||
var xoff = comp.Gridbounds[2] / 32f;
|
||||
var yoff = comp.Gridbounds[3] / 32f;
|
||||
|
||||
var gridWidth = comp.ReactorGridWidth;
|
||||
var gridHeight = comp.ReactorGridHeight;
|
||||
|
||||
var xAdj = (gridWidth - 1) / 2f;
|
||||
var yAdj = (gridHeight - 1) / 2f;
|
||||
|
||||
for (var x = 0; x < gridWidth; x++)
|
||||
{
|
||||
for (var y = 0; y < gridHeight; y++)
|
||||
{
|
||||
var layerID = _sprite.AddRsiLayer(entSprite, "empty_cap", resource.RSI);
|
||||
_sprite.LayerMapSet(entSprite, FormatMap(x, y), layerID);
|
||||
_sprite.LayerSetOffset(entSprite, layerID, new((xspace * (y - yAdj)) - xoff, (-yspace * (x - xAdj)) - yoff));
|
||||
_sprite.LayerSetColor(entSprite, layerID, Color.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatMap(int x, int y) => "NuclearReactorCap" + x + "/" + y;
|
||||
|
||||
private void ReactorExamined(EntityUid uid, NuclearReactorComponent comp, ClientExaminedEvent args) => Spawn(comp.ArrowPrototype, new EntityCoordinates(uid, 0, 0));
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, NuclearReactorComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
for (var x = 0; x < comp.ReactorGridWidth; x++)
|
||||
{
|
||||
for (var y = 0; y < comp.ReactorGridHeight; y++)
|
||||
{
|
||||
if(comp.VisualData.TryGetValue(new(x,y), out var data))
|
||||
UpdateRodAppearance(uid, FormatMap(x,y), data.cap, data.color);
|
||||
else
|
||||
UpdateRodAppearance(uid, FormatMap(x, y), "empty_cap", Color.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRodAppearance(EntityUid uid, string map, string state, Color color)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
Entity<SpriteComponent?> entSprite = (uid, sprite);
|
||||
|
||||
if (!_sprite.LayerMapTryGet(entSprite, map, out var layer, false))
|
||||
return;
|
||||
|
||||
_sprite.LayerSetRsiState(entSprite, layer, state);
|
||||
_sprite.LayerSetColor(entSprite, layer, color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._FarHorizons.Power.Generation.FissionGenerator;
|
||||
|
||||
public sealed class ReactorPartSystem : SharedReactorPartSystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReactorPartComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<ReactorPartComponent, ComponentInit>(OnComponentInit);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, ReactorPartComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
// Re-enable if/when there are multiple sprites
|
||||
//if (!_sprite.LayerMapTryGet((uid, args.Sprite), ReactorCapVisualLayers.Sprite, out var layer, false))
|
||||
// return;
|
||||
|
||||
_sprite.LayerSetColor((uid, args.Sprite), 0, _proto.Index(component.Material).Color);
|
||||
}
|
||||
|
||||
private void OnComponentInit(Entity<ReactorPartComponent> ent, ref ComponentInit args)
|
||||
=> _sprite.LayerSetColor((ent.Owner, EntityManager.GetComponent<SpriteComponent>(ent.Owner)), 0, _proto.Index(ent.Comp.Material).Color);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Client.GameObjects;
|
||||
using Content.Shared.Repairable;
|
||||
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
|
||||
using Content.Client.Popups;
|
||||
using Content.Client.Examine;
|
||||
using Robust.Client.Animations;
|
||||
|
||||
namespace Content.Client._FarHorizons.Power.Generation.FissionGenerator;
|
||||
|
||||
public sealed class TurbineSystem : SharedTurbineSystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TurbineComponent, ClientExaminedEvent>(TurbineExamined);
|
||||
|
||||
SubscribeLocalEvent<TurbineComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<TurbineComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
}
|
||||
|
||||
protected override void OnRepairTurbineFinished(Entity<TurbineComponent> ent, ref RepairFinishedEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!TryComp(ent.Owner, out TurbineComponent? comp))
|
||||
return;
|
||||
|
||||
_popupSystem.PopupClient(Loc.GetString("turbine-repair", ("target", ent.Owner), ("tool", args.Used!)), ent.Owner, args.User);
|
||||
}
|
||||
|
||||
private void TurbineExamined(EntityUid uid, TurbineComponent comp, ClientExaminedEvent args) => Spawn(comp.ArrowPrototype, new EntityCoordinates(uid, 0, 0));
|
||||
|
||||
private void OnAnimationCompleted(Entity<TurbineComponent> ent, ref AnimationCompletedEvent args) => PlayAnimation(ent);
|
||||
|
||||
private void OnAppearanceChange(Entity<TurbineComponent> ent, ref AppearanceChangeEvent args) => PlayAnimation(ent);
|
||||
|
||||
protected override void AccUpdate()
|
||||
{
|
||||
var query = EntityQueryEnumerator<TurbineComponent>();
|
||||
while (query.MoveNext(out var uid, out var component))
|
||||
{
|
||||
// Makes sure the anim doesn't get stuck at low RPM
|
||||
if (Math.Abs(component.RPM - component.AnimRPM) > component.BestRPM * 0.1)
|
||||
PlayAnimation((uid, component));
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayAnimation(Entity<TurbineComponent> ent)
|
||||
{
|
||||
if (ent.Comp.RPM < 1)
|
||||
return;
|
||||
|
||||
if (!TryComp<SpriteComponent>(ent.Owner, out var sprite) || !_sprite.TryGetLayer((ent.Owner,sprite), TurbineVisualLayers.TurbineSpeed, out var layer, false))
|
||||
return;
|
||||
|
||||
var state = "speedanim";
|
||||
if (_animationPlayer.HasRunningAnimation(ent.Owner, state))
|
||||
_animationPlayer.Stop(ent.Owner, state);
|
||||
|
||||
if (_animationPlayer.HasRunningAnimation(ent.Owner, state)) // Despite what you'd think, this is not always true
|
||||
return;
|
||||
|
||||
ent.Comp.AnimRPM = ent.Comp.RPM;
|
||||
var layerKey = TurbineVisualLayers.TurbineSpeed;
|
||||
var time = 0.5f * ent.Comp.BestRPM / ent.Comp.RPM;
|
||||
var timestep = time/12;
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(time),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = layerKey,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_00", 0),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_01", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_02", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_03", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_04", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_05", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_06", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_07", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_08", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_09", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_10", timestep),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_11", timestep)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
_sprite.LayerSetVisible(layer, true);
|
||||
_animationPlayer.Play(ent.Owner, animation, state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
|
||||
|
||||
namespace Content.Client._FarHorizons.Power.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="NuclearReactorWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class NuclearReactorBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private NuclearReactorWindow? _window;
|
||||
|
||||
public NuclearReactorBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
EntityUid? reactorUid = null;
|
||||
if (_entityManager.TryGetComponent<NuclearReactorMonitorComponent>(Owner, out var reactorMonitorComponent))
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(reactorMonitorComponent.reactor, out reactorUid) || reactorUid == null
|
||||
|| !_entityManager.TryGetComponent<NuclearReactorComponent>(reactorUid, out var monitoredReactorComponent) || monitoredReactorComponent.Melted)
|
||||
return;
|
||||
}
|
||||
else if (!_entityManager.TryGetComponent<NuclearReactorComponent>(Owner, out var reactorComponent) || reactorComponent.Melted)
|
||||
return;
|
||||
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<NuclearReactorWindow>();
|
||||
if (_entityManager.EntityExists(reactorUid))
|
||||
_window.SetEntity(reactorUid.Value, Owner);
|
||||
else
|
||||
_window.SetEntity(Owner);
|
||||
|
||||
_window.ItemActionButtonPressed += OnActionButtonPressed;
|
||||
_window.EjectButtonPressed += OnEjectButtonPressed;
|
||||
_window.ControlRodModify += OnControlRodModify;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (state is not NuclearReactorBuiState reactorState)
|
||||
return;
|
||||
|
||||
_window?.Update(reactorState);
|
||||
}
|
||||
|
||||
private void OnActionButtonPressed(Vector2d vector)
|
||||
{
|
||||
if (_window is null ) return;
|
||||
|
||||
SendPredictedMessage(new ReactorItemActionMessage(vector));
|
||||
}
|
||||
|
||||
private void OnEjectButtonPressed()
|
||||
{
|
||||
if (_window is null) return;
|
||||
|
||||
SendPredictedMessage(new ReactorEjectItemMessage());
|
||||
}
|
||||
|
||||
private void OnControlRodModify(float amount)
|
||||
{
|
||||
if (_window is null) return;
|
||||
|
||||
SendPredictedMessage(new ReactorControlRodModifyMessage(amount));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
MinSize="690 445" Title="Nuclear Reactor">
|
||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
|
||||
<!--Main Content-->
|
||||
<BoxContainer Name="MainContent" Orientation="Horizontal" VerticalExpand="True" Margin="4">
|
||||
|
||||
<!--Left Panel-->
|
||||
<BoxContainer Name="LeftPanel" Orientation="Vertical">
|
||||
|
||||
<!--Insert/remove controls-->
|
||||
<BoxContainer Name="Controls" Orientation="Vertical">
|
||||
<GridContainer Name="ControlGrid" Columns="3" Margin="2 2 2 2">
|
||||
<Button Name="XDecrement" Text="-X" Disabled="true"/>
|
||||
<Label Name="XPos" Text="0" StyleClasses="LabelKeyText" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
<Button Name="XIncrement" Text="+X"/>
|
||||
<Button Name="YDecrement" Text="-Y" Disabled="true"/>
|
||||
<Label Name="YPos" Text="0" StyleClasses="LabelKeyText" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
<Button Name="YIncrement" Text="+Y"/>
|
||||
</GridContainer>
|
||||
<controls:StripeBack>
|
||||
<Button Name="ItemAction" Text="{Loc comp-nuclear-reactor-ui-insert-button}"/>
|
||||
<Label Name="ItemActionLock" Text="{Loc comp-nuclear-reactor-ui-locked}" Visible="false" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="150" Margin="2 5" />
|
||||
|
||||
<!--Target info-->
|
||||
<BoxContainer Name="TargetData" Orientation="Vertical" VerticalExpand="True">
|
||||
<BoxContainer Name="TargetInfo" Orientation="Vertical">
|
||||
<Label Name="TargetName" Text="Rod" ClipText="True"/>
|
||||
<GridContainer Name="TargetTemperatureGrid" Columns="2" Margin="2 2 2 2">
|
||||
<Label Text="T: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Button Name="TargetTemperature" Text="20C" HorizontalAlignment="Right"/>
|
||||
</GridContainer>
|
||||
<GridContainer Name="TargetNRadiationGrid" Columns="2" Margin="2 2 2 2">
|
||||
<Label Text="N: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="TargetNRadiation" Text="0" HorizontalAlignment="Right"/>
|
||||
</GridContainer>
|
||||
<GridContainer Name="TargetRadiationGrid" Columns="2" Margin="2 2 2 2">
|
||||
<Label Text="R: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="TargetRadiation" Text="0" HorizontalAlignment="Right"/>
|
||||
</GridContainer>
|
||||
<GridContainer Name="TargetSpentGrid" Columns="2" Margin="2 2 2 2">
|
||||
<Label Text="S: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="TargetSpent" Text="0" HorizontalAlignment="Right"/>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!--"Shelf"-->
|
||||
<BoxContainer Name="Shelf" Orientation="Vertical">
|
||||
<Button Name="EjectItem" Text="{Loc comp-nuclear-reactor-ui-eject-button}" />
|
||||
<Label Name="ItemName" Text="empty" ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!--Reactor display-->
|
||||
<PanelContainer Name="GridVisuals" StyleClasses="Inset" HorizontalExpand="True" Margin="5 5 5 5">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Button Name="ChangeViewButton" Text="{Loc comp-nuclear-reactor-ui-view-change}"/>
|
||||
<Label Name="ViewLabel" Text="{Loc comp-nuclear-reactor-ui-view-temp}" StyleClasses="LabelKeyText" />
|
||||
<GridContainer Name="ReactorGrid" Columns="7" VerticalExpand="True" Margin="2 2 2 2">
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
<!--Right Panel-->
|
||||
<BoxContainer Name="RightPanel" Orientation="Vertical">
|
||||
<Label Name="StatusPanelLabel" Text="{Loc comp-nuclear-reactor-ui-status-panel}" StyleClasses="LabelKeyText" />
|
||||
|
||||
<BoxContainer Name="StatusIndicators" Orientation="Vertical">
|
||||
<BoxContainer Name="ReactorTemp" Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Name="ReactorTempLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-temp}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="ReactorTempValue" Text="20C" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
<ProgressBar Name="ReactorTempBar" MaxValue="2000" SetSize="150 5" Margin="5" />
|
||||
|
||||
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="200" Margin="2 5" />
|
||||
|
||||
<BoxContainer Name="ReactorRads" Orientation="Horizontal">
|
||||
<Label Name="ReactorRadsLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-rads}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="ReactorRadsValue" Text="0" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
<ProgressBar Name="ReactorRadsBar" MaxValue="50" SetSize="150 5" Margin="5" />
|
||||
|
||||
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="200" Margin="2 5" />
|
||||
|
||||
<BoxContainer Name="ReactorTherm" Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Name="ReactorThermLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-therm}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="ReactorThermValue" Text="0" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
<ProgressBar Name="ReactorThermBar" MaxValue="10000000" SetSize="150 5" Margin="5" />
|
||||
|
||||
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="200" Margin="2 5" />
|
||||
|
||||
|
||||
<BoxContainer Name="ControlRodsText" Orientation="Horizontal">
|
||||
<Label Name="ControlRodsLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-control}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
|
||||
<Label Name="ControlRodsValue" Text="100%" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="ControlRods" Orientation="Horizontal">
|
||||
<ProgressBar Name="ControlRodsActual" MaxValue="2" SetSize="5 100" Margin="5" Vertical="True" />
|
||||
<ProgressBar Name="ControlRodsSet" MaxValue="2" SetSize="5 100" Margin="5" Vertical="True" />
|
||||
<controls:StripeBack>
|
||||
<BoxContainer Name="ControlRodsButtons" Orientation="Vertical">
|
||||
<Button Name="ControlRodsInsertLarge" Text="+ +"/>
|
||||
<Button Name="ControlRodsInsert" Text=" + "/>
|
||||
<Button Name="ControlRodsRemove" Text=" - "/>
|
||||
<Button Name="ControlRodsRemoveLarge" Text="- -"/>
|
||||
</BoxContainer>
|
||||
<Label Name="ControlRodsLock" Text="{Loc comp-nuclear-reactor-ui-locked}" Visible="false" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</controls:StripeBack>
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<!--Footer-->
|
||||
<BoxContainer Name="Footer" Orientation="Vertical">
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||
<Label Text="{Loc comp-nuclear-reactor-ui-footer-left}" StyleClasses="WindowFooterText" />
|
||||
<Label Text="{Loc comp-nuclear-reactor-ui-footer-right}" StyleClasses="WindowFooterText"
|
||||
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
||||
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,355 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Lock;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._FarHorizons.Power.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Client-side UI used to view a nuclear reactor.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class NuclearReactorWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = null!;
|
||||
private readonly LockSystem _lock;
|
||||
|
||||
private readonly Dictionary<Vector2i, StyleBoxFlat> _reactorGrid = [];
|
||||
private readonly Dictionary<Vector2i, TextureRect> _reactorRect = [];
|
||||
private readonly Dictionary<Vector2i, Button> _reactorButton = [];
|
||||
|
||||
private readonly StyleBoxFlat _temperatureBar = new(Color.Black);
|
||||
private readonly StyleBoxFlat _radiationBar = new(Color.Black);
|
||||
private readonly StyleBoxFlat _powerBar = new(Color.Black);
|
||||
|
||||
private Dictionary<Vector2i, ReactorSlotBUIData> _data = [];
|
||||
private int _gridWidth = 0;
|
||||
private int _gridHeight = 0;
|
||||
|
||||
private byte _displayMode = 1<<0;
|
||||
|
||||
private enum DisplayModes : byte
|
||||
{
|
||||
Temperature = 1 << 0,
|
||||
Neutron = 1 << 1,
|
||||
Target = 1 << 2
|
||||
}
|
||||
|
||||
private byte _temperatureMode = 1 << 0;
|
||||
|
||||
private enum TemperatureModes : byte
|
||||
{
|
||||
Celcius = 1 << 0,
|
||||
Kelvin = 1 << 1,
|
||||
// Fahrenheit = 1 << 2
|
||||
}
|
||||
|
||||
private int _targetX = 0;
|
||||
private int _targetY = 0;
|
||||
|
||||
public event Action<Vector2d>? ItemActionButtonPressed;
|
||||
public event Action? EjectButtonPressed;
|
||||
|
||||
public event Action<float>? ControlRodModify;
|
||||
|
||||
private EntityUid _reactor;
|
||||
|
||||
private bool _isMonitor = false;
|
||||
private EntityUid _monitor;
|
||||
|
||||
public NuclearReactorWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_lock = _entityManager.System<LockSystem>();
|
||||
|
||||
ReactorTempBar.ForegroundStyleBoxOverride = _temperatureBar;
|
||||
ReactorRadsBar.ForegroundStyleBoxOverride = _radiationBar;
|
||||
ReactorThermBar.ForegroundStyleBoxOverride = _powerBar;
|
||||
|
||||
ChangeViewButton.OnPressed += _ => OnChangeViewButtonPressed();
|
||||
XIncrement.OnPressed += _ => MoveTarget(1, 0);
|
||||
XDecrement.OnPressed += _ => MoveTarget(-1, 0);
|
||||
YIncrement.OnPressed += _ => MoveTarget(0, 1);
|
||||
YDecrement.OnPressed += _ => MoveTarget(0, -1);
|
||||
ItemAction.OnPressed += _ => ItemActionButtonPressed?.Invoke(new(_targetY, _targetX));
|
||||
EjectItem.OnPressed += _ => EjectButtonPressed?.Invoke();
|
||||
|
||||
ControlRodsInsertLarge.OnPressed += _ => AdjustControlRods(0.1f);
|
||||
ControlRodsInsert.OnPressed += _ => AdjustControlRods(0.01f);
|
||||
ControlRodsRemove.OnPressed += _ => AdjustControlRods(-0.01f);
|
||||
ControlRodsRemoveLarge.OnPressed += _ => AdjustControlRods(-0.1f);
|
||||
|
||||
TargetTemperature.OnPressed += _ => ChangeTemp();
|
||||
}
|
||||
|
||||
public void Update(NuclearReactorBuiState msg)
|
||||
{
|
||||
_data = msg.SlotData;
|
||||
|
||||
ReactorTempValue.Text = FormatTemperature(msg.ReactorTemp);
|
||||
ReactorTempBar.Value = msg.ReactorTemp;
|
||||
_temperatureBar.BackgroundColor = GetColor(Atmospherics.T20C, ReactorTempBar.MaxValue * 0.75, msg.ReactorTemp);
|
||||
|
||||
ReactorRadsValue.Text = Math.Round(msg.ReactorRads, 1).ToString();
|
||||
ReactorRadsBar.Value = msg.ReactorRads;
|
||||
_radiationBar.BackgroundColor = GetColor(0, ReactorRadsBar.MaxValue * 0.5, msg.ReactorRads);
|
||||
|
||||
ReactorThermValue.Text = FormatPower(msg.ReactorTherm);
|
||||
ReactorThermBar.Value = msg.ReactorTherm;
|
||||
_powerBar.BackgroundColor = GetSteppedColor(ReactorThermBar.MaxValue * 0.75, ReactorThermBar.MaxValue, msg.ReactorTherm);
|
||||
|
||||
ControlRodsValue.Text = Math.Round(msg.ControlRodActual * 50, 1).ToString() + "%";
|
||||
ControlRodsActual.Value = msg.ControlRodActual;
|
||||
ControlRodsSet.Value = msg.ControlRodSet;
|
||||
|
||||
var locktarget = _isMonitor ? _monitor : _reactor;
|
||||
|
||||
ControlRodsButtons.Visible = !_lock.IsLocked(locktarget);
|
||||
ItemAction.Visible = !_lock.IsLocked(_reactor) && !_isMonitor;
|
||||
|
||||
ItemActionLock.Visible = _lock.IsLocked(_reactor) && !_isMonitor;
|
||||
ControlRodsLock.Visible = _lock.IsLocked(locktarget);
|
||||
|
||||
Shelf.Visible = !_isMonitor;
|
||||
|
||||
ItemName.Text = msg.ItemName ?? "empty";
|
||||
}
|
||||
|
||||
public void SetEntity(EntityUid reactor, EntityUid? monitor = null)
|
||||
{
|
||||
_reactor = reactor;
|
||||
|
||||
if (monitor != null)
|
||||
{
|
||||
_monitor = monitor.Value;
|
||||
_isMonitor = true;
|
||||
}
|
||||
|
||||
this.SetInfoFromEntity(_entityManager, _isMonitor ? _monitor : _reactor);
|
||||
|
||||
if (!_entityManager.TryGetComponent<NuclearReactorComponent>(_reactor, out var reactorComponent))
|
||||
return;
|
||||
|
||||
_gridWidth = reactorComponent.ReactorGridWidth;
|
||||
_gridHeight = reactorComponent.ReactorGridHeight;
|
||||
|
||||
ReactorGrid.Columns = _gridWidth;
|
||||
|
||||
ReactorTempBar.MaxValue = reactorComponent.ReactorMeltdownTemp;
|
||||
ReactorRadsBar.MaxValue = reactorComponent.MaximumRadiation;
|
||||
ReactorThermBar.MaxValue = reactorComponent.MaximumThermalPower;
|
||||
|
||||
InitReactorGrid();
|
||||
}
|
||||
|
||||
public void InitReactorGrid()
|
||||
{
|
||||
for (var x = 0; x < _gridWidth; x++)
|
||||
{
|
||||
for (var y = 0; y < _gridHeight; y++)
|
||||
{
|
||||
var styleBox = new StyleBoxFlat();
|
||||
var icon = new TextureRect()
|
||||
{
|
||||
SetSize = new(32, 32),
|
||||
TexturePath = "/Textures/_FarHorizons/Structures/Power/Generation/FissionGenerator/reactor_part_inserted/base.png"
|
||||
};
|
||||
var button = new Button
|
||||
{
|
||||
Margin = new(0),
|
||||
StyleBoxOverride = new StyleBoxFlat(Color.Transparent),
|
||||
ToolTip = "",
|
||||
TooltipDelay = 0.5f
|
||||
};
|
||||
|
||||
var vect = new Vector2i(x, y);
|
||||
_reactorGrid.Add(vect, styleBox);
|
||||
_reactorRect.Add(vect, icon);
|
||||
_reactorButton.Add(vect, button);
|
||||
|
||||
var control = new PanelContainer
|
||||
{
|
||||
Margin = new Thickness(2),
|
||||
PanelOverride = styleBox,
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
};
|
||||
control.AddChild(button);
|
||||
var nvect = new Vector2i(y, x); // Can't be declared as a part of the line below for some reason, will break if you try
|
||||
button.OnPressed += _ => SetTarget(nvect);
|
||||
button.AddChild(icon);
|
||||
ReactorGrid.AddChild(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
//Update the grid
|
||||
for (var x = 0; x < _gridWidth; x++)
|
||||
{
|
||||
for (var y = 0; y < _gridHeight; y++)
|
||||
{
|
||||
var vect = new Vector2i(x, y);
|
||||
var exists = _data.ContainsKey(vect);
|
||||
var box = _reactorGrid[vect];
|
||||
switch (_displayMode)
|
||||
{
|
||||
case (byte)DisplayModes.Temperature:
|
||||
box.BackgroundColor = GetColor(293.15, 1200, exists ? _data[vect].Temperature : 0);
|
||||
ViewLabel.Text = Loc.GetString("comp-nuclear-reactor-ui-view-temp");
|
||||
break;
|
||||
case (byte)DisplayModes.Neutron:
|
||||
box.BackgroundColor = GetColor(0, 7, exists ? _data[vect].NeutronCount : 0);
|
||||
ViewLabel.Text = Loc.GetString("comp-nuclear-reactor-ui-view-neutron");
|
||||
break;
|
||||
case (byte)DisplayModes.Target:
|
||||
box.BackgroundColor = y == _targetX && x == _targetY ? Color.Yellow : Color.Gray;
|
||||
ViewLabel.Text = Loc.GetString("comp-nuclear-reactor-ui-view-target");
|
||||
break;
|
||||
}
|
||||
|
||||
var icon = exists ? _data[vect].IconName : "base";
|
||||
_reactorRect[vect].TexturePath = "/Textures/_FarHorizons/Structures/Power/Generation/FissionGenerator/reactor_part_inserted/" + icon + ".png";
|
||||
|
||||
_reactorButton[vect].ToolTip = exists && _data[vect].SpentFuel > 0
|
||||
? "Fuel Level: " + (int)Math.Round((1 - (_data[vect].SpentFuel / (_data[vect].SpentFuel + (_data[vect].Radioactivity * 0.5) + (_data[vect].NeutronRadioactivity * 0.25)))) * 100) + "%"
|
||||
: "";
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTargetInfo();
|
||||
UpdateItemAction();
|
||||
}
|
||||
|
||||
private static Color GetColor(double pointA, double pointB, double value)
|
||||
{
|
||||
var mid = pointA+((pointB-pointA) / 2);
|
||||
Color result;
|
||||
|
||||
if (value < pointA && pointA > 0)
|
||||
result = Color.InterpolateBetween(Color.DarkBlue, Color.FromHex("#31843E"), (float)(value / pointA));
|
||||
else if (value >= pointA && value < mid)
|
||||
result = Color.InterpolateBetween(Color.FromHex("#31843E"), Color.FromHex("#BBBB00"), (float)((value - pointA) / (mid - pointA)));
|
||||
else if (value >= mid && value < pointB)
|
||||
result = Color.InterpolateBetween(Color.FromHex("#BBBB00"), Color.FromHex("#BB3232"), (float)((value - mid) / (pointB - mid)));
|
||||
else if (value >= pointB && value < pointB * 1.4)
|
||||
result = Color.InterpolateBetween(Color.FromHex("#BB3232"), Color.FromHex("#550000"), (float)((value - pointB) / ((pointB * 1.4) - pointB)));
|
||||
else if (value >= pointB * 1.4)
|
||||
result = Color.FromHex("#550000"); // Death.
|
||||
else
|
||||
result = Color.Black;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Color GetSteppedColor(double pointA, double pointB, double value)
|
||||
{
|
||||
Color result;
|
||||
|
||||
if (value > pointB)
|
||||
result = Color.FromHex("#BB3232");
|
||||
else if (value > pointA)
|
||||
result = Color.FromHex("#BBBB00");
|
||||
else
|
||||
result = Color.FromHex("#31843E");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void OnChangeViewButtonPressed()
|
||||
{
|
||||
// The bits go marching one by one...
|
||||
_displayMode <<= 1;
|
||||
if (_displayMode >= 1 << Enum.GetNames<DisplayModes>().Length)
|
||||
_displayMode = 1 << 0;
|
||||
}
|
||||
|
||||
private void ChangeTemp()
|
||||
{
|
||||
_temperatureMode <<= 1;
|
||||
if (_temperatureMode >= 1 << Enum.GetNames<TemperatureModes>().Length)
|
||||
_temperatureMode = 1 << 0;
|
||||
}
|
||||
|
||||
private string FormatTemperature(double temperature) => _temperatureMode switch
|
||||
{
|
||||
(byte)TemperatureModes.Celcius => Math.Round(temperature - Atmospherics.T0C, 1).ToString() + "C",
|
||||
(byte)TemperatureModes.Kelvin => Math.Round(temperature, 1).ToString() + "K",
|
||||
// (byte)TemperatureModes.Fahrenheit => Math.Round(((temperature - Atmospherics.T0C) * 9 / 5) + 32, 1).ToString() + "F",
|
||||
_ => "NaN",
|
||||
};
|
||||
|
||||
private void UpdateTargetInfo()
|
||||
{
|
||||
var vect = new Vector2i(_targetY, _targetX);
|
||||
if(!_data.TryGetValue(vect, out var value))
|
||||
{
|
||||
TargetName.Text = "empty";
|
||||
TargetTemperatureGrid.Visible = TargetNRadiationGrid.Visible = TargetRadiationGrid.Visible = TargetSpentGrid.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
TargetName.Text = value.PartName;
|
||||
|
||||
TargetTemperatureGrid.Visible = value.Temperature > 0;
|
||||
TargetTemperature.Text = FormatTemperature(value.Temperature);
|
||||
|
||||
TargetNRadiationGrid.Visible = value.NeutronRadioactivity > 0;
|
||||
TargetNRadiation.Text = Math.Round(value.NeutronRadioactivity, 2).ToString();
|
||||
|
||||
TargetRadiationGrid.Visible = value.Radioactivity > 0;
|
||||
TargetRadiation.Text = Math.Round(value.Radioactivity, 2).ToString();
|
||||
|
||||
TargetSpentGrid.Visible = value.SpentFuel > 0;
|
||||
TargetSpent.Text = Math.Round(value.SpentFuel, 2).ToString();
|
||||
}
|
||||
|
||||
private void UpdateItemAction()
|
||||
{
|
||||
if(ItemName.Text == "empty" == (TargetName.Text == "empty"))
|
||||
{
|
||||
ItemAction.Disabled = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
ItemAction.Disabled = false;
|
||||
|
||||
ItemAction.Text = TargetName.Text != "empty"
|
||||
? Loc.GetString("comp-nuclear-reactor-ui-remove-button")
|
||||
: Loc.GetString("comp-nuclear-reactor-ui-insert-button");
|
||||
}
|
||||
|
||||
private void MoveTarget(int x, int y) => SetTarget(_targetX + x, _targetY + y);
|
||||
|
||||
private void SetTarget(Vector2i vect) => SetTarget(vect.X, vect.Y);
|
||||
|
||||
private void SetTarget(int x, int y)
|
||||
{
|
||||
_targetX = Math.Clamp(x, 0, _gridWidth - 1);
|
||||
_targetY = Math.Clamp(y, 0, _gridHeight - 1);
|
||||
|
||||
XPos.Text = _targetX.ToString();
|
||||
YPos.Text = _targetY.ToString();
|
||||
|
||||
XIncrement.Disabled = _targetX >= _gridWidth - 1;
|
||||
XDecrement.Disabled = _targetX <= 0;
|
||||
YIncrement.Disabled = _targetY >= _gridHeight - 1;
|
||||
YDecrement.Disabled = _targetY <= 0;
|
||||
}
|
||||
|
||||
public void SetItemName(string? itemName) => ItemName.Text = itemName ?? "empty";
|
||||
|
||||
private static string FormatPower(float power) => Loc.GetString("comp-nuclear-reactor-ui-therm-format", ("power", power));
|
||||
|
||||
private void AdjustControlRods(float amount) => ControlRodModify?.Invoke(amount);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.Timing;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
|
||||
using Content.Client.UserInterface;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Client._FarHorizons.Power.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="TurbineWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class TurbineBoundUserInterface : BoundUserInterface, IBuiPreTickUpdate
|
||||
{
|
||||
[Dependency] private readonly IClientGameTiming _gameTiming = null!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = null!;
|
||||
|
||||
[ViewVariables]
|
||||
private TurbineWindow? _window;
|
||||
|
||||
private BuiPredictionState? _pred;
|
||||
private InputCoalescer<float> _flowRateCoalescer;
|
||||
private InputCoalescer<float> _statorLoadCoalescer;
|
||||
|
||||
public TurbineBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
EntityUid? turbineUid = null;
|
||||
if (_entityManager.TryGetComponent<GasTurbineMonitorComponent>(Owner, out var turbineMonitorComponent))
|
||||
if (!_entityManager.TryGetEntity(turbineMonitorComponent.turbine, out turbineUid) || turbineUid == null
|
||||
|| !_entityManager.HasComponent<TurbineComponent>(turbineUid))
|
||||
return;
|
||||
|
||||
base.Open();
|
||||
|
||||
_pred = new BuiPredictionState(this, _gameTiming);
|
||||
|
||||
_window = this.CreateWindow<TurbineWindow>();
|
||||
if (_entityManager.EntityExists(turbineUid))
|
||||
_window.SetEntity(turbineUid.Value, Owner);
|
||||
else
|
||||
_window.SetEntity(Owner);
|
||||
|
||||
_window.TurbineFlowRateChanged += val => _flowRateCoalescer.Set(val);
|
||||
_window.TurbineStatorLoadChanged += val => _statorLoadCoalescer.Set(val);
|
||||
Update();
|
||||
}
|
||||
|
||||
void IBuiPreTickUpdate.PreTickUpdate()
|
||||
{
|
||||
if (_flowRateCoalescer.CheckIsModified(out var flowRateValue))
|
||||
_pred!.SendMessage(new TurbineChangeFlowRateMessage(flowRateValue));
|
||||
|
||||
if (_statorLoadCoalescer.CheckIsModified(out var statorLoadValue))
|
||||
_pred!.SendMessage(new TurbineChangeStatorLoadMessage(statorLoadValue));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (state is not TurbineBuiState turbineState)
|
||||
return;
|
||||
|
||||
if (!_entityManager.TryGetComponent<TurbineComponent>(Owner, out var comp))
|
||||
if(!TryGetTurbineComp(Owner, out comp))
|
||||
return;
|
||||
|
||||
foreach (var replayMsg in _pred!.MessagesToReplay())
|
||||
{
|
||||
switch (replayMsg)
|
||||
{
|
||||
case TurbineChangeFlowRateMessage setFlowRate:
|
||||
turbineState.FlowRate = Math.Clamp(setFlowRate.FlowRate, 0f, comp.FlowRateMax);
|
||||
break;
|
||||
|
||||
case TurbineChangeStatorLoadMessage setStatorLoad:
|
||||
turbineState.StatorLoad = Math.Clamp(setStatorLoad.StatorLoad, 1000f, comp.StatorLoadMax);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_window?.Update(turbineState);
|
||||
}
|
||||
|
||||
public bool TryGetTurbineComp(EntityUid uid, [NotNullWhen(true)] out TurbineComponent? turbineComponent)
|
||||
{
|
||||
turbineComponent = null;
|
||||
if (!_entityManager.TryGetComponent<GasTurbineMonitorComponent>(uid, out var turbineMonitor))
|
||||
return false;
|
||||
|
||||
if (!_entityManager.TryGetEntity(turbineMonitor.turbine, out var turbineUid) || turbineUid == null)
|
||||
return false;
|
||||
|
||||
if (!_entityManager.TryGetComponent<TurbineComponent>(turbineUid, out var turbine))
|
||||
return false;
|
||||
|
||||
turbineComponent = turbine;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user