Compare commits
4 Commits
fishing
...
ed-03-07-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60a87f1c35 | ||
|
|
786c0ed97c | ||
|
|
d3895e8288 | ||
|
|
d184f795c4 |
@@ -129,7 +129,7 @@ csharp_indent_braces = false
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_cast = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
@@ -344,9 +344,6 @@ resharper_keep_existing_attribute_arrangement = true
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_csharp_trailing_comma_in_multiline_lists = true
|
||||
resharper_csharp_qualified_using_at_nested_scope = false
|
||||
resharper_csharp_prefer_qualified_reference = false
|
||||
resharper_csharp_allow_alias = false
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
5
.envrc
5
.envrc
@@ -1,5 +1,4 @@
|
||||
set -e
|
||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.6; then
|
||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.6/direnvrc" "sha256-RYcUJaRMf8oF5LznDrlCXbkOQrywm0HDv1VjYGaJGdM="
|
||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
|
||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
|
||||
fi
|
||||
use flake
|
||||
|
||||
39
.github/CODEOWNERS
vendored
39
.github/CODEOWNERS
vendored
@@ -2,30 +2,49 @@
|
||||
|
||||
# Sorting by path instead of by who added it one day :(
|
||||
# this isn't how codeowners rules work pls read the first comment instead of trying to force a sorting order
|
||||
/Resources/ConfigPresets/WizardsDen/ @Chief-Engineer
|
||||
|
||||
/Resources/ConfigPresets/WizardsDen/ @nikthechampiongr @crazybrain23
|
||||
/Content.*/Administration/ @DrSmugleaf @nikthechampiongr @crazybrain23
|
||||
/Resources/ServerInfo/ @nikthechampiongr @crazybrain23
|
||||
/Resources/ServerInfo/Guidebook/ServerRules/ @nikthechampiongr @crazybrain23
|
||||
# Moony's Gargantuan List Of Things She Cares About, or MGLOTSCA for short.
|
||||
# You need to add your name to these entries, not make a new one, if you care about them.
|
||||
/Content.*/Toolshed/ @moonheart08
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
/Content.*/Administration/ @moonheart08 @DrSmugleaf @Chief-Engineer
|
||||
/Content.*/Station/ @moonheart08
|
||||
/Content.*/Maps/ @moonheart08
|
||||
/Content.*/GameTicking/ @moonheart08 @EmoGarbage404
|
||||
/Resources/ServerInfo/ @moonheart08 @Chief-Engineer
|
||||
/Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404
|
||||
/Resources/ServerInfo/Guidebook/ServerRules/ @Chief-Engineer
|
||||
/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||
/Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||
|
||||
/Resources/Prototypes/Maps/** @Emisse
|
||||
/Resources/Prototypes/Maps/ @Emisse
|
||||
|
||||
/Resources/Prototypes/Body/ @DrSmugleaf # suffering
|
||||
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
|
||||
/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
|
||||
/Resources/Prototypes/Guidebook/rules.yml @nikthechampiongr @crazybrain23
|
||||
/Resources/Prototypes/Guidebook/rules.yml @Chief-Engineer
|
||||
/Content.*/Body/ @DrSmugleaf
|
||||
/Content.YAMLLinter @DrSmugleaf
|
||||
/Content.Shared/Damage/ @DrSmugleaf
|
||||
|
||||
/Content.*/Anomaly/ @TheShuEd
|
||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @TheShuEd
|
||||
/Content.*/Anomaly/ @EmoGarbage404 @TheShuEd
|
||||
/Content.*/Lathe/ @EmoGarbage404
|
||||
/Content.*/Materials/ @EmoGarbage404
|
||||
/Content.*/Mech/ @EmoGarbage404
|
||||
/Content.*/Research/ @EmoGarbage404
|
||||
/Content.*/Stack/ @EmoGarbage404
|
||||
/Content.*/Xenoarchaeology/ @EmoGarbage404
|
||||
/Content.*/Zombies/ @EmoGarbage404
|
||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404 @TheShuEd
|
||||
/Resources/Prototypes/Research/ @EmoGarbage404
|
||||
|
||||
/Content.*/Forensics/ @ficcialfaint
|
||||
|
||||
# SKREEEE
|
||||
/Content.*.Database/ @PJB3005 @DrSmugleaf
|
||||
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @nikthechampiongr @crazybrain23
|
||||
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @Chief-Engineer
|
||||
/Pow3r/ @PJB3005
|
||||
/Content.Server/Power/Pow3r/ @PJB3005
|
||||
|
||||
@@ -33,7 +52,7 @@
|
||||
/Content.*/Atmos/ @Partmedia
|
||||
/Content.*/Botany/ @Partmedia
|
||||
|
||||
# Jezi
|
||||
#Jezi
|
||||
/Content.*/Medical @Jezithyr
|
||||
/Content.*/Body @Jezithyr
|
||||
|
||||
|
||||
14
.github/FUNDING.yml
vendored
14
.github/FUNDING.yml
vendored
@@ -1,14 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
custom: ['https://boosty.to/theshued']
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -7,11 +7,11 @@
|
||||
<!-- Зачем нужно это изменение? Прикрепите любые обсуждения или проблемы здесь. Опишите, как это повлияет на текущий баланс игры. -->
|
||||
|
||||
## Media
|
||||
<!--
|
||||
<!--
|
||||
PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes.
|
||||
Small fixes/refactors are exempt.
|
||||
-->
|
||||
<!--
|
||||
<!--
|
||||
Пулл реквесты, которые несут за собой игровые изменения (добавления одежды, предметов и так далее) требуют чтобы вы прикрепили скриншоты или видеоролики, демонстрирующие эти изменения.
|
||||
Небольшие исправления не считаются.
|
||||
-->
|
||||
|
||||
14
.github/labeler.yml
vendored
14
.github/labeler.yml
vendored
@@ -5,22 +5,14 @@
|
||||
"Changes: Map":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'Resources/Maps/**/*.yml'
|
||||
- 'Resources/Prototypes/Maps/**/*.yml'
|
||||
- 'Resources/Maps/*.yml'
|
||||
- 'Resources/Prototypes/Maps/*.yml'
|
||||
|
||||
"Changes: UI":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.xaml*'
|
||||
|
||||
"Changes: Shaders":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.swsl'
|
||||
|
||||
"Changes: Audio":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.ogg'
|
||||
|
||||
"Changes: No C#":
|
||||
"No C#":
|
||||
- changed-files:
|
||||
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
|
||||
- all-globs-to-all-files: "!**/*.cs"
|
||||
|
||||
2
.github/rsi-schema.json
vendored
2
.github/rsi-schema.json
vendored
@@ -85,7 +85,7 @@
|
||||
"CC-BY-NC-SA-3.0",
|
||||
"CC-BY-NC-SA-4.0",
|
||||
"CC0-1.0",
|
||||
"CLA"
|
||||
"All rights reserved for the CrystallPunk14 project only"
|
||||
],
|
||||
"examples":[
|
||||
"CC-BY-SA-3.0"
|
||||
|
||||
20
.github/workflows/CP14Publish.yml
vendored
20
.github/workflows/CP14Publish.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
concurrency:
|
||||
group: publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send POST-request
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.BUILD_HOST }}
|
||||
username: ${{ secrets.BUILD_USER }}
|
||||
password: ${{ secrets.BUILD_PASS }}
|
||||
port: 22
|
||||
script: sh update.sh &> /dev/null
|
||||
2
.github/workflows/benchmarks.yml
vendored
2
.github/workflows/benchmarks.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Run Benchmarks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Get Engine version
|
||||
|
||||
6
.github/workflows/build-docfx.yml
vendored
6
.github/workflows/build-docfx.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
docfx:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- name: Setup submodule
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
@@ -19,9 +19,9 @@ jobs:
|
||||
cd RobustToolbox/
|
||||
git submodule update --init --recursive
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
10
.github/workflows/build-map-renderer.yml
vendored
10
.github/workflows/build-map-renderer.yml
vendored
@@ -2,11 +2,11 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v3.6.0
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -34,9 +34,9 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
10
.github/workflows/build-test-debug.yml
vendored
10
.github/workflows/build-test-debug.yml
vendored
@@ -2,11 +2,11 @@ name: Build & Test Debug
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v3.6.0
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -34,9 +34,9 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
2
.github/workflows/check-crlf.yml
vendored
2
.github/workflows/check-crlf.yml
vendored
@@ -10,6 +10,6 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- name: Check for CRLF
|
||||
run: Tools/check_crlf.py
|
||||
|
||||
38
.github/workflows/cla.yml
vendored
38
.github/workflows/cla.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: "CLA Assistant"
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_target:
|
||||
types: [opened,closed,synchronize]
|
||||
paths:
|
||||
- '**CP14**'
|
||||
|
||||
# explicitly configure permissions, in case your GITHUB_TOKEN workflow permissions are set to read-only in repository settings
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write # this can be 'read' if the signatures are in remote repository
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
CLAAssistant:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "CLA Assistant"
|
||||
if: ((github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target') && github.actor != 'TheShuEd'
|
||||
uses: contributor-assistant/github-action@v2.6.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# the below token should have repo scope and must be manually added by you in the repository's secret
|
||||
# This token is required only if you have configured to store the signatures in a remote repository/organization
|
||||
# PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
with:
|
||||
path-to-signatures: 'signatures/version1/cla.json'
|
||||
path-to-document: 'https://github.com/crystallpunk-14/crystall-punk-14/blob/master/CLA.md' # e.g. a CLA or a DCO document
|
||||
# branch should not be protected
|
||||
branch: 'master'
|
||||
allowlist: TheShuEd,bot*
|
||||
|
||||
# the followings are the optional inputs - If the optional inputs are not given, then default values will be taken
|
||||
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
|
||||
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
|
||||
21
.github/workflows/conflict-labeler.yml
vendored
Normal file
21
.github/workflows/conflict-labeler.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Check Merge Conflicts
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
Label:
|
||||
if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||
with:
|
||||
dirtyLabel: "Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
|
||||
21
.github/workflows/labeler-conflict.yml
vendored
21
.github/workflows/labeler-conflict.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Check Merge Conflicts
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
Label:
|
||||
if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||
with:
|
||||
dirtyLabel: "S: Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
|
||||
4
.github/workflows/labeler-needsreview.yml
vendored
4
.github/workflows/labeler-needsreview.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "S: Needs Review"
|
||||
labels: "Status: Needs Review"
|
||||
- uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: "S: Awaiting Changes"
|
||||
labels: "Status: Awaiting Changes"
|
||||
|
||||
23
.github/workflows/labeler-review.yml
vendored
23
.github/workflows/labeler-review.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: "Labels: Approved"
|
||||
on:
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
jobs:
|
||||
add_label:
|
||||
# Change the repository name after you've made sure the team name is correct for your fork!
|
||||
if: ${{ (github.repository == 'space-wizards/space-station-14') && (github.event.review.state == 'APPROVED') }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tspascoal/get-user-teams-membership@v3
|
||||
id: checkUserMember
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
team: "content-maintainers,junior-maintainers"
|
||||
GITHUB_TOKEN: ${{ secrets.LABELER_PAT }}
|
||||
- if: ${{ steps.checkUserMember.outputs.isTeamMember == 'true' }}
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "S: Approved"
|
||||
20
.github/workflows/labeler-size.yml
vendored
20
.github/workflows/labeler-size.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: "Labels: Size"
|
||||
on: pull_request_target
|
||||
jobs:
|
||||
size-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: size-label
|
||||
uses: "pascalgn/size-label-action@v0.5.5"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
with:
|
||||
# Custom size configuration
|
||||
sizes: >
|
||||
{
|
||||
"0": "XS",
|
||||
"10": "S",
|
||||
"100": "M",
|
||||
"1000": "L",
|
||||
"5000": "XL"
|
||||
}
|
||||
16
.github/workflows/labeler-stable.yml
vendored
16
.github/workflows/labeler-stable.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: "Labels: Branch stable"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- 'stable'
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Branch: Stable"
|
||||
16
.github/workflows/labeler-staging.yml
vendored
16
.github/workflows/labeler-staging.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: "Labels: Branch staging"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Branch: Staging"
|
||||
4
.github/workflows/labeler-untriaged.yml
vendored
4
.github/workflows/labeler-untriaged.yml
vendored
@@ -3,8 +3,6 @@
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
@@ -13,4 +11,4 @@ jobs:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
if: join(github.event.issue.labels) == ''
|
||||
with:
|
||||
labels: "S: Untriaged"
|
||||
labels: "Status: Untriaged"
|
||||
|
||||
45
.github/workflows/publish-testing.yml
vendored
45
.github/workflows/publish-testing.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: Publish Testing
|
||||
|
||||
concurrency:
|
||||
group: publish-testing
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
cd RobustToolbox
|
||||
git fetch --depth=1
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build Packaging
|
||||
run: dotnet build Content.Packaging --configuration Release --no-restore /m
|
||||
|
||||
- name: Package server
|
||||
run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
|
||||
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Publish version
|
||||
run: Tools/publish_multi_request.py --fork-id wizards-testing
|
||||
env:
|
||||
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
||||
40
.github/workflows/publish.yml
vendored
40
.github/workflows/publish.yml
vendored
@@ -5,8 +5,8 @@ concurrency:
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# schedule:
|
||||
# - cron: '0 10 * * *'
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -16,13 +16,13 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install -y python3-paramiko python3-lxml
|
||||
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
@@ -41,11 +41,31 @@ jobs:
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Publish version
|
||||
run: Tools/publish_multi_request.py
|
||||
env:
|
||||
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }}
|
||||
- name: Update Build Info
|
||||
run: Tools/gen_build_info.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
mkdir "release/${{ github.sha }}"
|
||||
mv release/*.zip "release/${{ github.sha }}"
|
||||
|
||||
- name: Upload files to centcomm
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
username: wizards-build-push
|
||||
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
|
||||
source: "release/${{ github.sha }}"
|
||||
target: "/home/wizards-build-push/builds_dir/builds/"
|
||||
strip_components: 1
|
||||
|
||||
- name: Update manifest JSON
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: centcomm.spacestation14.io
|
||||
username: wizards-build-push
|
||||
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
|
||||
script: /home/wizards-build-push/push.ps1 ${{ github.sha }}
|
||||
|
||||
- name: Publish changelog (Discord)
|
||||
run: Tools/actions_changelogs_since_last_run.py
|
||||
|
||||
4
.github/workflows/rsi-diff.yml
vendored
4
.github/workflows/rsi-diff.yml
vendored
@@ -11,14 +11,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v3.6.0
|
||||
|
||||
- name: Get changed files
|
||||
id: files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
with:
|
||||
format: 'space-delimited'
|
||||
filter: |
|
||||
filter: |
|
||||
**.rsi
|
||||
**.png
|
||||
|
||||
|
||||
18
.github/workflows/test-packaging.yml
vendored
18
.github/workflows/test-packaging.yml
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
@@ -16,7 +16,7 @@ on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v3.6.0
|
||||
|
||||
- name: Setup Submodule
|
||||
run: |
|
||||
@@ -49,9 +49,9 @@ jobs:
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
@@ -64,3 +64,11 @@ jobs:
|
||||
|
||||
- name: Package client
|
||||
run: dotnet run --project Content.Packaging client --no-wipe-release
|
||||
|
||||
- name: Update Build Info
|
||||
run: Tools/gen_build_info.py
|
||||
|
||||
- name: Shuffle files around
|
||||
run: |
|
||||
mkdir "release/${{ github.sha }}"
|
||||
mv release/*.zip "release/${{ github.sha }}"
|
||||
|
||||
22
.github/workflows/update-credits.yml
vendored
22
.github/workflows/update-credits.yml
vendored
@@ -4,46 +4,44 @@ on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 0 * * 0
|
||||
|
||||
|
||||
jobs:
|
||||
get_credits:
|
||||
runs-on: ubuntu-latest
|
||||
# Hey there fork dev! If you like to include your own contributors in this then you can probably just change this to your own repo
|
||||
# Do this in dump_github_contributors.ps1 too into your own repo
|
||||
if: github.repository == 'space-wizards/space-station-14'
|
||||
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
with:
|
||||
ref: master
|
||||
|
||||
|
||||
- name: Get this week's Contributors
|
||||
shell: pwsh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
run: Tools/dump_github_contributors.ps1 > Resources/Credits/GitHub.txt
|
||||
|
||||
# TODO
|
||||
#- name: Get this week's Patreons
|
||||
# run: Tools/script2dumppatreons > Resources/Credits/Patrons.yml
|
||||
|
||||
# run: Tools/script2dumppatreons > Resources/Credits/Patrons.yml
|
||||
|
||||
# MAKE SURE YOU ENABLED "Allow GitHub Actions to create and approve pull requests" IN YOUR ACTIONS, OTHERWISE IT WILL MOST LIKELY FAIL
|
||||
|
||||
|
||||
# For this you can use a pat token of an account with direct push access to the repo if you have protected branches.
|
||||
# For this you can use a pat token of an account with direct push access to the repo if you have protected branches.
|
||||
# Uncomment this and comment the other line if you do this.
|
||||
# https://github.com/stefanzweifel/git-auto-commit-action#push-to-protected-branches
|
||||
|
||||
|
||||
#- name: Commit new credit files
|
||||
# uses: stefanzweifel/git-auto-commit-action@v4
|
||||
# with:
|
||||
# commit_message: Update Credits
|
||||
# commit_author: PJBot <pieterjan.briers+bot@gmail.com>
|
||||
|
||||
|
||||
# This will make a PR
|
||||
- name: Set current date as env variable
|
||||
run: echo "NOW=$(date +'%Y-%m-%dT%H-%M-%S')" >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
|
||||
4
.github/workflows/validate-rgas.yml
vendored
4
.github/workflows/validate-rgas.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: RGA schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- name: Setup Submodule
|
||||
run: git submodule update --init
|
||||
- name: Pull engine updates
|
||||
|
||||
4
.github/workflows/validate-rsis.yml
vendored
4
.github/workflows/validate-rsis.yml
vendored
@@ -2,7 +2,7 @@ name: RSI Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Validate RSIs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- name: Setup Submodule
|
||||
run: git submodule update --init
|
||||
- name: Pull engine updates
|
||||
|
||||
4
.github/workflows/validate_mapfiles.yml
vendored
4
.github/workflows/validate_mapfiles.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Map file schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- name: Setup Submodule
|
||||
run: git submodule update --init
|
||||
- name: Pull engine updates
|
||||
|
||||
8
.github/workflows/yaml-linter.yml
vendored
8
.github/workflows/yaml-linter.yml
vendored
@@ -2,7 +2,7 @@ name: YAML Linter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, stable ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v3.6.0
|
||||
- name: Setup submodule
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
@@ -24,9 +24,9 @@ jobs:
|
||||
cd RobustToolbox/
|
||||
git submodule update --init --recursive
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"omnisharp.analyzeOpenDocumentsOnly": true,
|
||||
"dotnet.defaultSolution": "SpaceStation14.sln"
|
||||
}
|
||||
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@@ -10,7 +10,7 @@
|
||||
"args": [
|
||||
"build",
|
||||
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
|
||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -29,9 +29,9 @@
|
||||
"build",
|
||||
"${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'"
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,12 @@ You want to handle the Build, Clean and Rebuild tasks to prevent missing task er
|
||||
If you want to learn more about these kinds of things, check out Microsoft's official documentation about MSBuild:
|
||||
https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
||||
-->
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
||||
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
||||
<TargetFramework>net4.7.2</TargetFramework>
|
||||
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.2</TargetFrameworkMoniker>
|
||||
<RestorePackages>false</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
80
CLA.md
80
CLA.md
@@ -1,80 +0,0 @@
|
||||
# CrystallEdge Contributor License Agreement
|
||||
### Version 1.0
|
||||
##### Thank you for your interest in contributing to CrystallEdge ("We" or "Us").
|
||||
|
||||
##### The purpose of this contributor agreement ("Agreement") is for Your protection as a Contributor in addition to the protection of our community.
|
||||
|
||||
##### If you wish to contact us regarding licensing matters we can be reached at crystalledge14@gmail.com
|
||||
|
||||
## How to use this Contributor Agreement
|
||||
|
||||
##### If You are an employee and have created the Contribution as part of your employment, You need to have Your employer approve this Agreement or sign the Entity version of this document as well.
|
||||
## 1. Definitions
|
||||
- _**"You"**_ means the individual Copyright owner who Submits a Contribution to Us.
|
||||
- _**"Contribution(s)"**_ means any work(s) of authorship, including any original modifications or additions to an existing work of authorships, Submitted by You to Us, where You are the author, holder of copyright, or Licensee under an Approved License specified by Us.
|
||||
- _**"Copyright"**_ means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence.
|
||||
- **_"Material"_** means the software or documentation made available by Us to third parties. When this Agreement covers more than one software project, the Material means the software or documentation to which the Contributions were Submitted. After You Submit the Contributions, theymay be included in the Material.
|
||||
- **_"Submit"_** means any act by which Contributions are transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||
- **_"Documentation"_** means any non-software portion of Contributions.
|
||||
- **_"Approved License"_** means any License specified by CrystallEdge to be Approved or “Compatible” with the Project the Contributions are Submitted to.
|
||||
## 2. Representations
|
||||
|
||||
### 2.1 Representation of legal age
|
||||
|
||||
##### You represent that you are older than 16 years of age, and if required by law, have authorization by a legal guardian to enter this agreement.
|
||||
|
||||
### 2.2 Assurance of legal rights
|
||||
|
||||
##### You represent and assure that You have sufficient rights to your Contribution and are legally entitled to enter this Agreement and grant the licenses specified below or an Approved license if Your Contribution or portions thereof are provided to You under one of the Approved Licenses.
|
||||
|
||||
### 2.3 Third Party Contributions
|
||||
|
||||
##### If You act on behalf of Your employer or other third party You represent that You are authorized and have the right to Submit the Contribution on behalf of Your employer or the mentioned third party.
|
||||
|
||||
### 2.4 Compliance and Non-infringement
|
||||
##### You represent and warrant that each of your Contributions:
|
||||
- Is and will remain an original work of authorship;
|
||||
- to the best of Your knowledge, does not and will not infringe any third party’s copyright, trademark, patent, or other intellectual property rights;
|
||||
- In part or in whole, is licensed under one of the Approved Licenses or is an original work you have the rights to.
|
||||
- includes the complete and correct details of any license, third-party license, patent, trademark, necessary attributions or other restriction associated with all or any part of Your Contribution in a conspicuous location;
|
||||
- complies and will continue to comply with all applicable laws, including export control laws and regulations;
|
||||
|
||||
### 2.5 Mixed license Contributions
|
||||
|
||||
##### Subject to the terms and conditions of this agreement, specifically section 2, if there is a conflict between the grants in section 3, 4 and a Contribution under an Approved License, the terms of the Approved License supersede.
|
||||
## 2.6 Employee or Representative Submissions
|
||||
|
||||
##### If You Submit as a company, You agree that a) Your employees, contractors, and representatives may Submit Contributions on Your behalf; and b) the individual signing this Agreement on Your behalf has the necessary authority including the authority to bind You to the Agreement.
|
||||
|
||||
## 3. License grant
|
||||
### 3.1 Copyright license to Us
|
||||
##### Subject to the terms and conditions of this Agreement, You hereby grant to Us a worldwide, royalty-free, NON-exclusive, perpetual and irrevocable (except as stated in Sections 2.4 and 8.2) license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, under the Copyright covering the Contributions to use the Contributions by all means, including, but not limited to:
|
||||
- publish the Contributions,
|
||||
- modify the Contributions,
|
||||
- prepare derivative works based upon or containing the Contributions and/or to combine the Contributions with other Materials,
|
||||
- reproduce the Contributions in original or modified form,
|
||||
- distribute, to make the Contributions available to the public, display and publicly perform the Contributions in original or modified form.
|
||||
## 3.2 Moral rights
|
||||
##### Moral Rights remain unaffected to the extent they are recognized and not waivable by applicable law. Notwithstanding, You may add your name to the attribution mechanism customary used in the Materials you Contribute to, such as the header of the source code files of Your Contributions, and We will respect this attribution when using Your Contributions.
|
||||
## 4. Patents
|
||||
### 4.1 Patent license
|
||||
##### Subject to the terms and conditions of this Agreement You hereby grant to Us and to recipients of Materials distributed by Us a worldwide, royalty-free, non-exclusive, perpetual and irrevocable (except as stated in Section 3.2) patent license, with the right to transfer an unlimited number of non-exclusive licenses or to grant sublicenses to third parties, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contributions and the Contributions in combination with any Material (and portions of such combination). This license applies to all patents owned or controlled by You, whether already acquired or hereafter acquired, that would be infringed by making, having made, using, selling, offering for sale, importing or otherwise transferring of Your Contribution(s) alone or by combination of Your Contribution(s) with any Material.
|
||||
### 4.2 Revocation of patent license
|
||||
##### You reserve the right to revoke the patent license stated in section 3.1 if We make any infringement claim that is targeted at your Contribution(s) and not asserted for a Defensive Purpose. An assertion of claims of the Patents shall be considered for a "Defensive Purpose" if the claims are asserted against an entity that has filed, maintained, threatened, or voluntarily participated in a patent infringement lawsuit against Us or any of Our licensees.
|
||||
## 5. Disclaimer
|
||||
#### CONTRIBUTIONS ARE PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE MINIMUM PERIOD AND EXTENT PERMITTED BY LAW.
|
||||
## 6. Consequential damage waiver
|
||||
#### TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
|
||||
## 7. Approximation of disclaimer and damage waiver
|
||||
#### IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 4. AND SECTION 5. CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION.
|
||||
## 8. Term
|
||||
##### 8.1 This Agreement shall come into effect upon Your acceptance of the terms and conditions, either by replying to the CLA Bot or by sending a signed copy to crystalledge14@gmail.com with the subject: "\<your name\> CLA"
|
||||
##### 8.2 In the event of a termination of this Agreement Sections 4, 5, 6, 7 and 8 shall survive such termination and shall remain in full force thereafter. For the avoidance of doubt, Approved (sub)licenses that have already been granted for Contributions at the date of the termination shall remain in full force after the termination of this Agreement.
|
||||
## 9. Miscellaneous
|
||||
##### 9.1 This Agreement and all disputes, claims, actions, suits or other proceedings arising out of this agreement or relating in any way to it shall be governed by the laws of Russia excluding its private international law provisions.
|
||||
##### 9.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
|
||||
##### 9.3 In case of Your death, this agreement shall continue with Your heirs. In case of more than one heir, all heirs must exercise their rights through a commonly authorized person.
|
||||
##### 9.4 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and that is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
|
||||
##### 9.5 You agree to notify Us of any facts or circumstances of which you become aware that would make this Agreement inaccurate in any respect.
|
||||
##### 9.6 Any Substantive modifications to this Agreement will result in a new version being created, to continue Contributing you must agree to the latest version of the Agreement, which supersedes any previous versions.
|
||||
##### 9.7 CrystallEdge will provide notification of any new version of this agreement being created, if you do not agree to the new version of the Agreement the previous Agreement remains binding.
|
||||
@@ -9,14 +9,13 @@ using Content.IntegrationTests.Pair;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
@@ -33,6 +32,7 @@ public class ComponentQueryBenchmark
|
||||
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private MapId _mapId = new(10);
|
||||
private EntityQuery<ItemComponent> _itemQuery;
|
||||
private EntityQuery<ClothingComponent> _clothingQuery;
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
@@ -54,10 +54,10 @@ public class ComponentQueryBenchmark
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var map = new ResPath(Map);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
if (!_entMan.System<MapLoaderSystem>().TryLoadMap(map, out _, out _, opts))
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
_items = new EntityUid[_entMan.Count<ItemComponent>()];
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Content.Benchmarks
|
||||
for (var i = 0; i < Aabbs1.Length; i++)
|
||||
{
|
||||
var aabb = Aabbs1[i];
|
||||
_b2Tree.CreateProxy(aabb, uint.MaxValue, i);
|
||||
_b2Tree.CreateProxy(aabb, i);
|
||||
_tree.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,12 @@ using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Maps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
@@ -21,7 +20,7 @@ public class MapLoadBenchmark
|
||||
{
|
||||
private TestPair _pair = default!;
|
||||
private MapLoaderSystem _mapLoader = default!;
|
||||
private SharedMapSystem _mapSys = default!;
|
||||
private IMapManager _mapManager = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
@@ -37,7 +36,7 @@ public class MapLoadBenchmark
|
||||
.ToDictionary(x => x.ID, x => x.MapPath.ToString());
|
||||
|
||||
_mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||
_mapSys = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedMapSystem>();
|
||||
_mapManager = server.ResolveDependency<IMapManager>();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
@@ -47,25 +46,23 @@ public class MapLoadBenchmark
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Convex"};
|
||||
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry", "Oasis" };
|
||||
|
||||
[ParamsSource(nameof(MapsSource))]
|
||||
public string Map;
|
||||
|
||||
public Dictionary<string, string> Paths;
|
||||
private MapId _mapId;
|
||||
|
||||
[Benchmark]
|
||||
public async Task LoadMap()
|
||||
{
|
||||
var mapPath = new ResPath(Paths[Map]);
|
||||
var mapPath = Paths[Map];
|
||||
var server = _pair.Server;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var success = _mapLoader.TryLoadMap(mapPath, out var map, out _);
|
||||
var success = _mapLoader.TryLoad(new MapId(10), mapPath, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_mapId = map.Value.Comp.MapId;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,7 +70,9 @@ public class MapLoadBenchmark
|
||||
public void IterationCleanup()
|
||||
{
|
||||
var server = _pair.Server;
|
||||
server.WaitPost(() => _mapSys.DeleteMap(_mapId))
|
||||
.Wait();
|
||||
server.WaitPost(() =>
|
||||
{
|
||||
_mapManager.DeleteMap(new MapId(10));
|
||||
}).Wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,13 @@ using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Warps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
@@ -36,6 +34,7 @@ public class PvsBenchmark
|
||||
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private MapId _mapId = new(10);
|
||||
private ICommonSession[] _players = default!;
|
||||
private EntityCoordinates[] _spawns = default!;
|
||||
public int _cycleOffset = 0;
|
||||
@@ -66,10 +65,10 @@ public class PvsBenchmark
|
||||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
await _pair.Server.WaitPost(() =>
|
||||
{
|
||||
var path = new ResPath(Map);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
if (!_entMan.System<MapLoaderSystem>().TryLoadMap(path, out _, out _, opts))
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
});
|
||||
|
||||
// Get list of ghost warp positions
|
||||
|
||||
@@ -12,18 +12,11 @@ namespace Content.Client.Access.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AccessLevelControl : GridContainer
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public readonly Dictionary<ProtoId<AccessLevelPrototype>, Button> ButtonsList = new();
|
||||
|
||||
public AccessLevelControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("accesslevelcontrol");
|
||||
}
|
||||
|
||||
public void Populate(List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager)
|
||||
@@ -32,7 +25,7 @@ public sealed partial class AccessLevelControl : GridContainer
|
||||
{
|
||||
if (!prototypeManager.TryIndex(access, out var accessLevel))
|
||||
{
|
||||
_sawmill.Error($"Unable to find accesslevel for {access}");
|
||||
Logger.Error($"Unable to find accesslevel for {access}");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Access.Components.AccessOverriderComponent;
|
||||
|
||||
@@ -24,28 +23,6 @@ namespace Content.Client.Access.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<AccessOverriderWindow>();
|
||||
RefreshAccess();
|
||||
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||
_window.OnSubmit += SubmitData;
|
||||
|
||||
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
||||
}
|
||||
|
||||
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
||||
{
|
||||
base.OnProtoReload(args);
|
||||
if (!args.WasModified<AccessLevelPrototype>())
|
||||
return;
|
||||
|
||||
RefreshAccess();
|
||||
|
||||
if (State != null)
|
||||
_window?.UpdateState(_prototypeManager, (AccessOverriderBoundUserInterfaceState) State);
|
||||
}
|
||||
|
||||
private void RefreshAccess()
|
||||
{
|
||||
List<ProtoId<AccessLevelPrototype>> accessLevels;
|
||||
|
||||
if (EntMan.TryGetComponent<AccessOverriderComponent>(Owner, out var accessOverrider))
|
||||
@@ -53,20 +30,38 @@ namespace Content.Client.Access.UI
|
||||
accessLevels = accessOverrider.AccessLevels;
|
||||
accessLevels.Sort();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
accessLevels = new List<ProtoId<AccessLevelPrototype>>();
|
||||
_accessOverriderSystem.Log.Error($"No AccessOverrider component found for {EntMan.ToPrettyString(Owner)}!");
|
||||
}
|
||||
|
||||
_window?.SetAccessLevels(_prototypeManager, accessLevels);
|
||||
_window = new AccessOverriderWindow(this, _prototypeManager, accessLevels)
|
||||
{
|
||||
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName
|
||||
};
|
||||
|
||||
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
var castState = (AccessOverriderBoundUserInterfaceState) state;
|
||||
_window?.UpdateState(_prototypeManager, castState);
|
||||
_window?.UpdateState(castState);
|
||||
}
|
||||
|
||||
public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)
|
||||
|
||||
@@ -13,24 +13,26 @@ namespace Content.Client.Access.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AccessOverriderWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private readonly AccessOverriderBoundUserInterface _owner;
|
||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||
|
||||
public event Action<List<ProtoId<AccessLevelPrototype>>>? OnSubmit;
|
||||
|
||||
public AccessOverriderWindow()
|
||||
public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototypeManager prototypeManager,
|
||||
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
IoCManager.InjectDependencies(this);
|
||||
var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
|
||||
|
||||
public void SetAccessLevels(IPrototypeManager protoManager, List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||
{
|
||||
_accessButtons.Clear();
|
||||
AccessLevelGrid.DisposeAllChildren();
|
||||
_owner = owner;
|
||||
|
||||
foreach (var access in accessLevels)
|
||||
{
|
||||
if (!protoManager.TryIndex(access, out var accessLevel))
|
||||
if (!prototypeManager.TryIndex(access, out var accessLevel))
|
||||
{
|
||||
logMill.Error($"Unable to find accesslevel for {access}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -42,16 +44,11 @@ namespace Content.Client.Access.UI
|
||||
|
||||
AccessLevelGrid.AddChild(newButton);
|
||||
_accessButtons.Add(accessLevel.ID, newButton);
|
||||
newButton.OnPressed += _ =>
|
||||
{
|
||||
OnSubmit?.Invoke(
|
||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
|
||||
};
|
||||
newButton.OnPressed += _ => SubmitData();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state)
|
||||
public void UpdateState(AccessOverriderBoundUserInterfaceState state)
|
||||
{
|
||||
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
@@ -69,11 +66,11 @@ namespace Content.Client.Access.UI
|
||||
|
||||
if (state.MissingPrivilegesList != null && state.MissingPrivilegesList.Any())
|
||||
{
|
||||
var missingPrivileges = new List<string>();
|
||||
List<string> missingPrivileges = new List<string>();
|
||||
|
||||
foreach (string tag in state.MissingPrivilegesList)
|
||||
{
|
||||
var privilege = Loc.GetString(protoManager.Index<AccessLevelPrototype>(tag)?.Name ?? "generic-unknown");
|
||||
string privilege = Loc.GetString(_prototypeManager.Index<AccessLevelPrototype>(tag)?.Name ?? "generic-unknown");
|
||||
missingPrivileges.Add(privilege);
|
||||
}
|
||||
|
||||
@@ -88,11 +85,18 @@ namespace Content.Client.Access.UI
|
||||
button.Disabled = !interfaceEnabled;
|
||||
if (interfaceEnabled)
|
||||
{
|
||||
// Explicit cast because Rider gives a false error otherwise.
|
||||
button.Pressed = state.TargetAccessReaderIdAccessList?.Contains((ProtoId<AccessLevelPrototype>) accessName) ?? false;
|
||||
button.Disabled = (!state.AllowedModifyAccessList?.Contains((ProtoId<AccessLevelPrototype>) accessName)) ?? true;
|
||||
button.Pressed = state.TargetAccessReaderIdAccessList?.Contains(accessName) ?? false;
|
||||
button.Disabled = (!state.AllowedModifyAccessList?.Contains(accessName)) ?? true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SubmitData()
|
||||
{
|
||||
_owner.SubmitData(
|
||||
|
||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
@@ -21,11 +20,16 @@ namespace Content.Client.Access.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<AgentIDCardWindow>();
|
||||
_window?.Dispose();
|
||||
_window = new AgentIDCardWindow(this);
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnNameChanged += OnNameChanged;
|
||||
_window.OnJobChanged += OnJobChanged;
|
||||
_window.OnJobIconChanged += OnJobIconChanged;
|
||||
}
|
||||
|
||||
private void OnNameChanged(string newName)
|
||||
@@ -38,7 +42,7 @@ namespace Content.Client.Access.UI
|
||||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(ProtoId<JobIconPrototype> newJobIconId)
|
||||
public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
}
|
||||
@@ -55,7 +59,16 @@ namespace Content.Client.Access.UI
|
||||
|
||||
_window.SetCurrentName(cast.CurrentName);
|
||||
_window.SetCurrentJob(cast.CurrentJob);
|
||||
_window.SetAllowedIcons(cast.CurrentJobIconId);
|
||||
_window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
<LineEdit Name="NameLineEdit" />
|
||||
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
|
||||
<LineEdit Name="JobLineEdit" />
|
||||
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
||||
<GridContainer Name="IconGrid" Columns="10">
|
||||
<!-- Job icon buttons are generated in the code -->
|
||||
</GridContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
|
||||
<Control HorizontalExpand="True" MinSize="50 0"/>
|
||||
<GridContainer Name="IconGrid" Columns="10">
|
||||
<!-- Job icon buttons are generated in the code -->
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -8,7 +8,6 @@ using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Access.UI
|
||||
{
|
||||
@@ -18,19 +17,19 @@ namespace Content.Client.Access.UI
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly AgentIDCardBoundUserInterface _bui;
|
||||
|
||||
private const int JobIconColumnCount = 10;
|
||||
|
||||
public event Action<string>? OnNameChanged;
|
||||
public event Action<string>? OnJobChanged;
|
||||
|
||||
public event Action<ProtoId<JobIconPrototype>>? OnJobIconChanged;
|
||||
|
||||
public AgentIDCardWindow()
|
||||
public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
_bui = bui;
|
||||
|
||||
NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text);
|
||||
NameLineEdit.OnFocusExit += e => OnNameChanged?.Invoke(e.Text);
|
||||
@@ -39,16 +38,17 @@ namespace Content.Client.Access.UI
|
||||
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetAllowedIcons(string currentJobIconId)
|
||||
public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
var jobIconButtonGroup = new ButtonGroup();
|
||||
var jobIconGroup = new ButtonGroup();
|
||||
var i = 0;
|
||||
var icons = _prototypeManager.EnumeratePrototypes<JobIconPrototype>().Where(icon => icon.AllowSelection).ToList();
|
||||
icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
|
||||
foreach (var jobIcon in icons)
|
||||
foreach (var jobIconId in icons)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
|
||||
continue;
|
||||
|
||||
String styleBase = StyleBase.ButtonOpenBoth;
|
||||
var modulo = i % JobIconColumnCount;
|
||||
if (modulo == 0)
|
||||
@@ -62,13 +62,12 @@ namespace Content.Client.Access.UI
|
||||
Access = AccessLevel.Public,
|
||||
StyleClasses = { styleBase },
|
||||
MaxSize = new Vector2(42, 28),
|
||||
Group = jobIconButtonGroup,
|
||||
Pressed = currentJobIconId == jobIcon.ID,
|
||||
ToolTip = jobIcon.LocalizedJobName
|
||||
Group = jobIconGroup,
|
||||
Pressed = i == 0,
|
||||
};
|
||||
|
||||
// Generate buttons textures
|
||||
var jobIconTexture = new TextureRect
|
||||
TextureRect jobIconTexture = new TextureRect
|
||||
{
|
||||
Texture = _spriteSystem.Frame0(jobIcon.Icon),
|
||||
TextureScale = new Vector2(2.5f, 2.5f),
|
||||
@@ -76,9 +75,12 @@ namespace Content.Client.Access.UI
|
||||
};
|
||||
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
if (jobIconId.Equals(currentJobIconId))
|
||||
jobIconButton.Pressed = true;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,30 +48,6 @@ namespace Content.Client.Actions
|
||||
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
||||
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentHandleState>(OnEntityWorldTargetHandleState);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
var worldActionQuery = EntityQueryEnumerator<WorldTargetActionComponent>();
|
||||
while (worldActionQuery.MoveNext(out var uid, out var action))
|
||||
{
|
||||
UpdateAction(uid, action);
|
||||
}
|
||||
|
||||
var instantActionQuery = EntityQueryEnumerator<InstantActionComponent>();
|
||||
while (instantActionQuery.MoveNext(out var uid, out var action))
|
||||
{
|
||||
UpdateAction(uid, action);
|
||||
}
|
||||
|
||||
var entityActionQuery = EntityQueryEnumerator<EntityTargetActionComponent>();
|
||||
while (entityActionQuery.MoveNext(out var uid, out var action))
|
||||
{
|
||||
UpdateAction(uid, action);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
|
||||
@@ -88,7 +64,6 @@ namespace Content.Client.Actions
|
||||
return;
|
||||
|
||||
component.Whitelist = state.Whitelist;
|
||||
component.Blacklist = state.Blacklist;
|
||||
component.CanTargetSelf = state.CanTargetSelf;
|
||||
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
|
||||
}
|
||||
@@ -101,26 +76,12 @@ namespace Content.Client.Actions
|
||||
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
|
||||
}
|
||||
|
||||
private void OnEntityWorldTargetHandleState(EntityUid uid,
|
||||
EntityWorldTargetActionComponent component,
|
||||
ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not EntityWorldTargetActionComponentState state)
|
||||
return;
|
||||
|
||||
component.Whitelist = state.Whitelist;
|
||||
component.CanTargetSelf = state.CanTargetSelf;
|
||||
BaseHandleState<EntityWorldTargetActionComponent>(uid, component, state);
|
||||
}
|
||||
|
||||
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
||||
{
|
||||
// TODO ACTIONS use auto comp states
|
||||
component.Icon = state.Icon;
|
||||
component.IconOn = state.IconOn;
|
||||
component.IconColor = state.IconColor;
|
||||
component.OriginalIconColor = state.OriginalIconColor;
|
||||
component.DisabledIconColor = state.DisabledIconColor;
|
||||
component.Keywords.Clear();
|
||||
component.Keywords.UnionWith(state.Keywords);
|
||||
component.Enabled = state.Enabled;
|
||||
@@ -138,7 +99,6 @@ namespace Content.Client.Actions
|
||||
component.Priority = state.Priority;
|
||||
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
||||
component.RaiseOnUser = state.RaiseOnUser;
|
||||
component.RaiseOnAction = state.RaiseOnAction;
|
||||
component.AutoPopulate = state.AutoPopulate;
|
||||
component.Temporary = state.Temporary;
|
||||
component.ItemIconStyle = state.ItemIconStyle;
|
||||
@@ -147,13 +107,11 @@ namespace Content.Client.Actions
|
||||
UpdateAction(uid, component);
|
||||
}
|
||||
|
||||
public override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
|
||||
protected override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
|
||||
{
|
||||
if (!ResolveActionData(actionId, ref action))
|
||||
return;
|
||||
|
||||
action.IconColor = action.Charges < 1 ? action.DisabledIconColor : action.OriginalIconColor;
|
||||
|
||||
base.UpdateAction(actionId, action);
|
||||
if (_playerManager.LocalEntity != action.AttachedEntity)
|
||||
return;
|
||||
@@ -260,13 +218,13 @@ namespace Content.Client.Actions
|
||||
|
||||
public void LinkAllActions(ActionsComponent? actions = null)
|
||||
{
|
||||
if (_playerManager.LocalEntity is not { } user ||
|
||||
!Resolve(user, ref actions, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_playerManager.LocalEntity is not { } user ||
|
||||
!Resolve(user, ref actions, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LinkActions?.Invoke(actions);
|
||||
LinkActions?.Invoke(actions);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -288,6 +246,12 @@ namespace Content.Client.Actions
|
||||
|
||||
if (action.ClientExclusive)
|
||||
{
|
||||
if (instantAction.Event != null)
|
||||
{
|
||||
instantAction.Event.Performer = user;
|
||||
instantAction.Event.Action = actionId;
|
||||
}
|
||||
|
||||
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
|
||||
}
|
||||
else
|
||||
@@ -329,7 +293,7 @@ namespace Content.Client.Actions
|
||||
continue;
|
||||
|
||||
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
|
||||
var actionId = Spawn();
|
||||
var actionId = Spawn(null);
|
||||
AddComp(actionId, action);
|
||||
AddActionDirect(user, actionId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -77,12 +77,9 @@ namespace Content.Client.Actions.UI
|
||||
MaxWidth = TooltipTextMaxWidth,
|
||||
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
|
||||
};
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup))
|
||||
return;
|
||||
|
||||
requiresLabel.SetMessage(markup);
|
||||
|
||||
requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" +
|
||||
requires +
|
||||
"[/color]"));
|
||||
vbox.AddChild(requiresLabel);
|
||||
}
|
||||
}
|
||||
@@ -100,11 +97,8 @@ namespace Content.Client.Actions.UI
|
||||
if (timeLeft > TimeSpan.Zero)
|
||||
{
|
||||
var duration = Cooldown.Value.End - Cooldown.Value.Start;
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup(Loc.GetString("ui-actionslot-duration", ("duration", (int)duration.TotalSeconds), ("timeLeft", (int)timeLeft.TotalSeconds + 1)), out var markup))
|
||||
return;
|
||||
|
||||
_cooldownLabel.SetMessage(markup);
|
||||
_cooldownLabel.SetMessage(FormattedMessage.FromMarkup(
|
||||
$"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]"));
|
||||
_cooldownLabel.Visible = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,218 +1,73 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Administration;
|
||||
|
||||
internal sealed class AdminNameOverlay : Overlay
|
||||
namespace Content.Client.Administration
|
||||
{
|
||||
private readonly AdminSystem _system;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly EntityLookupSystem _entityLookup;
|
||||
private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
private readonly Font _font;
|
||||
private readonly Font _fontBold;
|
||||
private bool _overlayClassic;
|
||||
private bool _overlaySymbols;
|
||||
private bool _overlayPlaytime;
|
||||
private bool _overlayStartingJob;
|
||||
private float _ghostFadeDistance;
|
||||
private float _ghostHideDistance;
|
||||
private int _overlayStackMax;
|
||||
private float _overlayMergeDistance;
|
||||
|
||||
//TODO make this adjustable via GUI
|
||||
private readonly ProtoId<RoleTypePrototype>[] _filter =
|
||||
["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
|
||||
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
|
||||
|
||||
public AdminNameOverlay(
|
||||
AdminSystem system,
|
||||
IEntityManager entityManager,
|
||||
IEyeManager eyeManager,
|
||||
IResourceCache resourceCache,
|
||||
EntityLookupSystem entityLookup,
|
||||
IUserInterfaceManager userInterfaceManager,
|
||||
IConfigurationManager config)
|
||||
internal sealed class AdminNameOverlay : Overlay
|
||||
{
|
||||
_system = system;
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
_entityLookup = entityLookup;
|
||||
_userInterfaceManager = userInterfaceManager;
|
||||
ZIndex = 200;
|
||||
// Setting these to a specific ttf would break the antag symbols
|
||||
_font = resourceCache.NotoStack();
|
||||
_fontBold = resourceCache.NotoStack(variation: "Bold");
|
||||
private readonly AdminSystem _system;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly EntityLookupSystem _entityLookup;
|
||||
private readonly Font _font;
|
||||
|
||||
config.OnValueChanged(CCVars.AdminOverlayClassic, (show) => { _overlayClassic = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlaySymbols, (show) => { _overlaySymbols = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayGhostHideDistance, (f) => { _ghostHideDistance = f; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayGhostFadeDistance, (f) => { _ghostFadeDistance = f; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayStackMax, (i) => { _overlayStackMax = i; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayMergeDistance, (f) => { _overlayMergeDistance = f; }, true);
|
||||
}
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = args.WorldAABB;
|
||||
var colorDisconnected = Color.White;
|
||||
var uiScale = _userInterfaceManager.RootControl.UIScale;
|
||||
var lineoffset = new Vector2(0f, 14f) * uiScale;
|
||||
var drawnOverlays = new List<(Vector2,Vector2)>() ; // A saved list of the overlays already drawn
|
||||
|
||||
// Get all player positions before drawing overlays, so they can be sorted before iteration
|
||||
var sortable = new List<(PlayerInfo, Box2, EntityUid, Vector2)>();
|
||||
foreach (var info in _system.PlayerList)
|
||||
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(info.NetEntity);
|
||||
|
||||
// If entity does not exist or is on a different map, skip
|
||||
if (entity == null
|
||||
|| !_entityManager.EntityExists(entity)
|
||||
|| _entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
|
||||
continue;
|
||||
|
||||
var aabb = _entityLookup.GetWorldAABB(entity.Value);
|
||||
// if not on screen, skip
|
||||
if (!aabb.Intersects(in viewport))
|
||||
continue;
|
||||
|
||||
// Get on-screen coordinates of player
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center).Rounded();
|
||||
|
||||
sortable.Add((info, aabb, entity.Value, screenCoordinates));
|
||||
_system = system;
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
_entityLookup = entityLookup;
|
||||
ZIndex = 200;
|
||||
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
}
|
||||
|
||||
// Draw overlays for visible players, starting from the top of the screen
|
||||
foreach (var info in sortable.OrderBy(s => s.Item4.Y).ToList())
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var playerInfo = info.Item1;
|
||||
var aabb = info.Item2;
|
||||
var entity = info.Item3;
|
||||
var screenCoordinatesCenter = info.Item4;
|
||||
//the center position is kept separately, for simpler position comparison later
|
||||
var centerOffset = new Vector2(28f, -18f) * uiScale;
|
||||
var screenCoordinates = screenCoordinatesCenter + centerOffset;
|
||||
var alpha = 1f;
|
||||
var viewport = args.WorldAABB;
|
||||
|
||||
//TODO make a smarter system where the starting offset can be modified by the predicted position and size of already-drawn overlays/stacks?
|
||||
var currentOffset = Vector2.Zero;
|
||||
|
||||
// Ghosts near the cursor are made transparent/invisible
|
||||
// TODO would be "cheaper" if playerinfo already contained a ghost bool, this gets called every frame for every onscreen player!
|
||||
if (_entityManager.HasComponent<GhostComponent>(entity))
|
||||
foreach (var playerInfo in _system.PlayerList)
|
||||
{
|
||||
// We want the map positions here, so we don't have to worry about resolution and such shenanigans
|
||||
var mobPosition = aabb.Center;
|
||||
var mousePosition = _eyeManager
|
||||
.ScreenToMap(_userInterfaceManager.MousePositionScaled.Position * uiScale)
|
||||
.Position;
|
||||
var dist = Vector2.Distance(mobPosition, mousePosition);
|
||||
if (dist < _ghostHideDistance)
|
||||
continue;
|
||||
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
|
||||
|
||||
alpha = Math.Clamp((dist - _ghostHideDistance) / (_ghostFadeDistance - _ghostHideDistance), 0f, 1f);
|
||||
colorDisconnected.A = alpha;
|
||||
}
|
||||
|
||||
// If the new overlay text block is within merge distance of any previous ones
|
||||
// merge them into a stack so they don't hide each other
|
||||
var stack = drawnOverlays.FindAll(x =>
|
||||
Vector2.Distance(_eyeManager.ScreenToMap(x.Item1).Position, aabb.Center) <= _overlayMergeDistance);
|
||||
if (stack.Count > 0)
|
||||
{
|
||||
screenCoordinates = stack.First().Item1 + centerOffset;
|
||||
// Replacing this overlay's coordinates for the later save with the stack root's coordinates
|
||||
// so that other overlays don't try to stack to these coordinates
|
||||
screenCoordinatesCenter = stack.First().Item1;
|
||||
|
||||
var i = 1;
|
||||
foreach (var s in stack)
|
||||
// Otherwise the entity can not exist yet
|
||||
if (entity == null || !_entityManager.EntityExists(entity))
|
||||
{
|
||||
// additional entries after maximum stack size is reached will be drawn over the last entry
|
||||
if (i <= _overlayStackMax - 1)
|
||||
currentOffset = lineoffset + s.Item2 ;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not on the same map, continue
|
||||
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != _eyeManager.CurrentMap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var aabb = _entityLookup.GetWorldAABB(entity.Value);
|
||||
|
||||
// if not on screen, continue
|
||||
if (!aabb.Intersects(in viewport))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var lineoffset = new Vector2(0f, 11f);
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
|
||||
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
|
||||
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
|
||||
if (playerInfo.Antag)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", Color.OrangeRed);
|
||||
}
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, playerInfo.Connected ? Color.Yellow : Color.White);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
}
|
||||
|
||||
// Character name
|
||||
var color = Color.Aquamarine;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
// Username
|
||||
color = Color.Yellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
// Playtime
|
||||
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && _overlayPlaytime)
|
||||
{
|
||||
color = Color.Orange;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
// Job
|
||||
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && _overlayStartingJob)
|
||||
{
|
||||
color = Color.GreenYellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
// Classic Antag Label
|
||||
if (_overlayClassic && playerInfo.Antag)
|
||||
{
|
||||
var symbol = _overlaySymbols ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
|
||||
var label = _overlaySymbols
|
||||
? Loc.GetString("player-tab-character-name-antag-symbol",
|
||||
("symbol", symbol),
|
||||
("name", _antagLabelClassic))
|
||||
: _antagLabelClassic;
|
||||
color = Color.OrangeRed;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
// Role Type
|
||||
else if (!_overlayClassic && _filter.Contains(playerInfo.RoleProto))
|
||||
{
|
||||
var symbol = _overlaySymbols && playerInfo.Antag ? playerInfo.RoleProto.Symbol : string.Empty;
|
||||
var role = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
|
||||
var label = _overlaySymbols
|
||||
? Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", role))
|
||||
: role;
|
||||
color = playerInfo.RoleProto.Color;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
//Save the coordinates and size of the text block, for stack merge check
|
||||
drawnOverlays.Add((screenCoordinatesCenter, currentOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
@@ -13,8 +11,6 @@ namespace Content.Client.Administration.Systems
|
||||
[Dependency] private readonly IClientAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
private AdminNameOverlay _adminNameOverlay = default!;
|
||||
|
||||
@@ -23,14 +19,7 @@ namespace Content.Client.Administration.Systems
|
||||
|
||||
private void InitializeOverlay()
|
||||
{
|
||||
_adminNameOverlay = new AdminNameOverlay(
|
||||
this,
|
||||
EntityManager,
|
||||
_eyeManager,
|
||||
_resourceCache,
|
||||
_entityLookup,
|
||||
_userInterfaceManager,
|
||||
_configurationManager);
|
||||
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup);
|
||||
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ namespace Content.Client.Administration.Systems
|
||||
OnBwoinkTextMessageRecieved?.Invoke(this, message);
|
||||
}
|
||||
|
||||
public void Send(NetUserId channelId, string text, bool playSound, bool adminOnly)
|
||||
public void Send(NetUserId channelId, string text, bool playSound)
|
||||
{
|
||||
// Reuse the channel ID as the 'true sender'.
|
||||
// Server will ignore this and if someone makes it not ignore this (which is bad, allows impersonation!!!), that will help.
|
||||
RaiseNetworkEvent(new BwoinkTextMessage(channelId, channelId, text, playSound: playSound, adminOnly: adminOnly));
|
||||
RaiseNetworkEvent(new BwoinkTextMessage(channelId, channelId, text, playSound: playSound));
|
||||
SendInputTextUpdated(channelId, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
|
||||
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
|
||||
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab">
|
||||
xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"
|
||||
xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab">
|
||||
<TabContainer Name="MasterTabContainer">
|
||||
<adminTab:AdminTab />
|
||||
<adminbusTab:AdminbusTab />
|
||||
@@ -14,6 +15,7 @@
|
||||
<tabs:RoundTab />
|
||||
<tabs:ServerTab />
|
||||
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
|
||||
<baby:BabyJailTab Name="BabyJailControl" Access="Public" />
|
||||
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
|
||||
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
|
||||
</TabContainer>
|
||||
|
||||
@@ -21,6 +21,10 @@ public sealed partial class AdminMenuWindow : DefaultWindow
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Round, Loc.GetString("admin-menu-round-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.PanicBunker, Loc.GetString("admin-menu-panic-bunker-tab"));
|
||||
/*
|
||||
* TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.BabyJail, Loc.GetString("admin-menu-baby-jail-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Players, Loc.GetString("admin-menu-players-tab"));
|
||||
MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab"));
|
||||
MasterTabContainer.OnTabChanged += OnTabChanged;
|
||||
@@ -48,6 +52,7 @@ public sealed partial class AdminMenuWindow : DefaultWindow
|
||||
Round,
|
||||
Server,
|
||||
PanicBunker,
|
||||
BabyJail,
|
||||
Players,
|
||||
Objects,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -13,7 +13,7 @@ public sealed partial class AdminMessagePopupMessage : Control
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Admin.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString(
|
||||
Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString(
|
||||
"admin-notes-message-admin",
|
||||
("admin", message.AdminName),
|
||||
("date", message.AddedOn.ToLocalTime()))));
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed partial class AdminMessagePopupWindow : Control
|
||||
MessageContainer.AddChild(new AdminMessagePopupMessage(message));
|
||||
}
|
||||
|
||||
Description.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
|
||||
Description.SetMessage(FormattedMessage.FromMarkup(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
|
||||
}
|
||||
|
||||
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace Content.Client.Administration.UI.BanPanel;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
public event Action<string?, (IPAddress, int)?, bool, ImmutableTypedHwid?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string>? PlayerChanged;
|
||||
private string? PlayerUsername { get; set; }
|
||||
private (IPAddress, int)? IpAddress { get; set; }
|
||||
private ImmutableTypedHwid? Hwid { get; set; }
|
||||
private byte[]? Hwid { get; set; }
|
||||
private double TimeEntered { get; set; }
|
||||
private uint Multiplier { get; set; }
|
||||
private bool HasBanFlag { get; set; }
|
||||
@@ -371,8 +371,9 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
private void OnHwidChanged()
|
||||
{
|
||||
var hwidString = HwidLine.Text;
|
||||
ImmutableTypedHwid? hwid = null;
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid))
|
||||
var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
|
||||
Hwid = new byte[length];
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = Color.Red;
|
||||
@@ -389,7 +390,7 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
Hwid = null;
|
||||
return;
|
||||
}
|
||||
Hwid = hwid;
|
||||
Hwid = Convert.FromHexString(hwidString);
|
||||
}
|
||||
|
||||
private void OnTypeChanged()
|
||||
|
||||
@@ -2,26 +2,22 @@
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<SplitContainer Orientation="Vertical" ResizeMode="NotResizable">
|
||||
<SplitContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<cc:PlayerListControl Access="Public" Name="ChannelSelector" HorizontalExpand="True" SizeFlagsStretchRatio="2" />
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="2">
|
||||
<BoxContainer Access="Public" Name="BwoinkArea" VerticalExpand="True" />
|
||||
<SplitContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<cc:PlayerListControl Access="Public" Name="ChannelSelector" HorizontalExpand="True" SizeFlagsStretchRatio="1" />
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="2">
|
||||
<BoxContainer Access="Public" Name="BwoinkArea" VerticalExpand="True" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<CheckBox Visible="True" Name="PlaySound" Access="Public" Text="{Loc 'admin-bwoink-play-sound'}" Pressed="True" />
|
||||
<Control HorizontalExpand="True" MinWidth="5" />
|
||||
<Button Visible="True" Name="PopOut" Access="Public" Text="{Loc 'admin-logs-pop-out'}" StyleClasses="OpenBoth" HorizontalAlignment="Left" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<Button Visible="False" Name="Bans" Text="{Loc 'admin-player-actions-bans'}" StyleClasses="OpenRight" />
|
||||
<Button Visible="False" Name="Notes" Text="{Loc 'admin-player-actions-notes'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Kick" Text="{Loc 'admin-player-actions-kick'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Ban" Text="{Loc 'admin-player-actions-ban'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Respawn" Text="{Loc 'admin-player-actions-respawn'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Follow" Text="{Loc 'admin-player-actions-follow'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</SplitContainer>
|
||||
<BoxContainer Orientation="Horizontal" SetHeight="30" >
|
||||
<CheckBox Name="AdminOnly" Access="Public" Text="{Loc 'admin-ahelp-admin-only'}" ToolTip="{Loc 'admin-ahelp-admin-only-tooltip'}" />
|
||||
<Control HorizontalExpand="True" MinWidth="5" />
|
||||
<CheckBox Name="PlaySound" Access="Public" Text="{Loc 'admin-bwoink-play-sound'}" Pressed="True" />
|
||||
<Control HorizontalExpand="True" MinWidth="5" />
|
||||
<Button Visible="True" Name="PopOut" Access="Public" Text="{Loc 'admin-logs-pop-out'}" StyleClasses="OpenBoth" HorizontalAlignment="Left" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<Button Visible="False" Name="Bans" Text="{Loc 'admin-player-actions-bans'}" StyleClasses="OpenRight" />
|
||||
<Button Visible="False" Name="Notes" Text="{Loc 'admin-player-actions-notes'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Kick" Text="{Loc 'admin-player-actions-kick'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Ban" Text="{Loc 'admin-player-actions-ban'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Respawn" Text="{Loc 'admin-player-actions-respawn'}" StyleClasses="OpenBoth" />
|
||||
<Button Visible="False" Name="Follow" Text="{Loc 'admin-player-actions-follow'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</SplitContainer>
|
||||
</PanelContainer>
|
||||
|
||||
@@ -11,8 +11,9 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.UI.Bwoink
|
||||
{
|
||||
@@ -36,9 +37,6 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var newPlayerThreshold = 0;
|
||||
_cfg.OnValueChanged(CCVars.NewPlayerThreshold, (val) => { newPlayerThreshold = val; }, true);
|
||||
|
||||
var uiController = _ui.GetUIController<AHelpUIController>();
|
||||
if (uiController.UIHelper is not AdminAHelpUIHandler helper)
|
||||
return;
|
||||
@@ -48,8 +46,6 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
_adminManager.AdminStatusUpdated += UpdateButtons;
|
||||
UpdateButtons();
|
||||
|
||||
AdminOnly.OnToggled += args => PlaySound.Disabled = args.Pressed;
|
||||
|
||||
ChannelSelector.OnSelectionChanged += sel =>
|
||||
{
|
||||
_currentPlayer = sel;
|
||||
@@ -62,9 +58,9 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (info.Connected)
|
||||
sb.Append(info.ActiveThisRound ? '⚫' : '◐');
|
||||
sb.Append('●');
|
||||
else
|
||||
sb.Append(info.ActiveThisRound ? '⭘' : '·');
|
||||
sb.Append(info.ActiveThisRound ? '○' : '·');
|
||||
|
||||
sb.Append(' ');
|
||||
if (AHelpHelper.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0)
|
||||
@@ -76,12 +72,10 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
// Mark antagonists with symbol
|
||||
if (info.Antag && info.ActiveThisRound)
|
||||
sb.Append(new Rune(0x1F5E1)); // 🗡
|
||||
|
||||
// Mark new players with symbol
|
||||
if (IsNewPlayer(info))
|
||||
if (info.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)))
|
||||
sb.Append(new Rune(0x23F2)); // ⏲
|
||||
|
||||
sb.AppendFormat("\"{0}\"", text);
|
||||
@@ -89,75 +83,31 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
return sb.ToString();
|
||||
};
|
||||
|
||||
// <summary>
|
||||
// Returns true if the player's overall playtime is under the set threshold
|
||||
// </summary>
|
||||
bool IsNewPlayer(PlayerInfo info)
|
||||
{
|
||||
// Don't show every disconnected player as new, don't show 0-minute players as new if threshold is
|
||||
if (newPlayerThreshold <= 0 || info.OverallPlaytime is null && !info.Connected)
|
||||
return false;
|
||||
|
||||
return (info.OverallPlaytime is null
|
||||
|| info.OverallPlaytime < TimeSpan.FromMinutes(newPlayerThreshold));
|
||||
}
|
||||
|
||||
ChannelSelector.Comparison = (a, b) =>
|
||||
{
|
||||
var ach = AHelpHelper.EnsurePanel(a.SessionId);
|
||||
var bch = AHelpHelper.EnsurePanel(b.SessionId);
|
||||
|
||||
// Pinned players first
|
||||
if (a.IsPinned != b.IsPinned)
|
||||
return a.IsPinned ? -1 : 1;
|
||||
|
||||
// Then, any chat with unread messages.
|
||||
// First, sort by unread. Any chat with unread messages appears first. We just sort based on unread
|
||||
// status, not number of unread messages, so that more recent unread messages take priority.
|
||||
var aUnread = ach.Unread > 0;
|
||||
var bUnread = bch.Unread > 0;
|
||||
if (aUnread != bUnread)
|
||||
return aUnread ? -1 : 1;
|
||||
|
||||
// Then, any chat with recent messages from the current round
|
||||
var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue;
|
||||
var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
|
||||
if (aRecent != bRecent)
|
||||
return aRecent ? -1 : 1;
|
||||
|
||||
// Sort by connection status. Disconnected players will be last.
|
||||
// Next, sort by connection status. Any disconnected players are grouped towards the end.
|
||||
if (a.Connected != b.Connected)
|
||||
return a.Connected ? -1 : 1;
|
||||
|
||||
// Sort connected players by whether they have joined the round, then by New Player status, then by Antag status
|
||||
if (a.Connected && b.Connected)
|
||||
{
|
||||
var aNewPlayer = IsNewPlayer(a);
|
||||
var bNewPlayer = IsNewPlayer(b);
|
||||
|
||||
// Players who have joined the round will be listed before players in the lobby
|
||||
if (a.ActiveThisRound != b.ActiveThisRound)
|
||||
return a.ActiveThisRound ? -1 : 1;
|
||||
|
||||
// Within both the joined group and lobby group, new players will be grouped and listed first
|
||||
if (aNewPlayer != bNewPlayer)
|
||||
return aNewPlayer ? -1 : 1;
|
||||
|
||||
// Within all four previous groups, antagonists will be listed first.
|
||||
if (a.Antag != b.Antag)
|
||||
return a.Antag ? -1 : 1;
|
||||
}
|
||||
|
||||
// Sort disconnected players by participation in the round
|
||||
if (!a.Connected && !b.Connected)
|
||||
{
|
||||
if (a.ActiveThisRound != b.ActiveThisRound)
|
||||
return a.ActiveThisRound ? -1 : 1;
|
||||
}
|
||||
// Next, group by whether or not the players have participated in this round.
|
||||
// The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom.
|
||||
if (a.ActiveThisRound != b.ActiveThisRound)
|
||||
return a.ActiveThisRound ? -1 : 1;
|
||||
|
||||
// Finally, sort by the most recent message.
|
||||
return bch.LastMessage.CompareTo(ach.LastMessage);
|
||||
};
|
||||
|
||||
|
||||
Bans.OnPressed += _ =>
|
||||
{
|
||||
if (_currentPlayer is not null)
|
||||
@@ -303,20 +253,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
|
||||
public void PopulateList()
|
||||
{
|
||||
// Maintain existing pin statuses
|
||||
var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
|
||||
|
||||
ChannelSelector.PopulateList();
|
||||
|
||||
// Restore pin statuses
|
||||
foreach (var player in ChannelSelector.PlayerInfo)
|
||||
{
|
||||
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
|
||||
{
|
||||
player.IsPinned = pinnedPlayer.IsPinned;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
Unread++;
|
||||
|
||||
var formatted = new FormattedMessage(1);
|
||||
formatted.AddMarkupOrThrow($"[color=gray]{message.SentAt.ToShortTimeString()}[/color] {message.Text}");
|
||||
formatted.AddMarkup($"[color=gray]{message.SentAt.ToShortTimeString()}[/color] {message.Text}");
|
||||
TextOutput.AddMessage(formatted);
|
||||
LastMessage = message.SentAt;
|
||||
}
|
||||
|
||||
@@ -22,16 +22,15 @@ namespace Content.Client.Administration.UI.Bwoink
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"{sel.CharacterName} / {sel.Username} | {Loc.GetString("generic-playtime-title")}: ";
|
||||
Title = $"{sel.CharacterName} / {sel.Username}";
|
||||
|
||||
Title += sel.OverallPlaytime != null ? sel.PlaytimeString : Loc.GetString("generic-unknown-title");
|
||||
if (sel.OverallPlaytime != null)
|
||||
{
|
||||
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
|
||||
}
|
||||
};
|
||||
|
||||
OnOpen += () =>
|
||||
{
|
||||
Bwoink.ChannelSelector.StopFiltering();
|
||||
Bwoink.PopulateList();
|
||||
};
|
||||
OnOpen += () => Bwoink.PopulateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,166 +4,154 @@ using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.Verbs.UI;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
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.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.CustomControls;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerListControl : BoxContainer
|
||||
namespace Content.Client.Administration.UI.CustomControls
|
||||
{
|
||||
private readonly AdminSystem _adminSystem;
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IUserInterfaceManager _uiManager;
|
||||
|
||||
private PlayerInfo? _selectedPlayer;
|
||||
|
||||
private List<PlayerInfo> _playerList = new();
|
||||
private List<PlayerInfo> _sortedPlayerList = new();
|
||||
|
||||
public Comparison<PlayerInfo>? Comparison;
|
||||
public Func<PlayerInfo, string, string>? OverrideText;
|
||||
|
||||
public PlayerListControl()
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerListControl : BoxContainer
|
||||
{
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
RobustXamlLoader.Load(this);
|
||||
// Fill the Option data
|
||||
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||
PlayerListContainer.GenerateItem += GenerateButton;
|
||||
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
|
||||
PopulateList(_adminSystem.PlayerList);
|
||||
FilterLineEdit.OnTextChanged += _ => FilterList();
|
||||
_adminSystem.PlayerListChanged += PopulateList;
|
||||
BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
|
||||
}
|
||||
private readonly AdminSystem _adminSystem;
|
||||
|
||||
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
||||
private List<PlayerInfo> _playerList = new();
|
||||
private readonly List<PlayerInfo> _sortedPlayerList = new();
|
||||
|
||||
public event Action<PlayerInfo?>? OnSelectionChanged;
|
||||
public event Action<PlayerInfo?>? OnSelectionChanged;
|
||||
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
|
||||
|
||||
private void PlayerListNoItemSelected()
|
||||
{
|
||||
_selectedPlayer = null;
|
||||
OnSelectionChanged?.Invoke(null);
|
||||
}
|
||||
public Func<PlayerInfo, string, string>? OverrideText;
|
||||
public Comparison<PlayerInfo>? Comparison;
|
||||
|
||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
private IEntityManager _entManager;
|
||||
private IUserInterfaceManager _uiManager;
|
||||
|
||||
if (selectedPlayer == _selectedPlayer)
|
||||
return;
|
||||
private PlayerInfo? _selectedPlayer;
|
||||
|
||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
OnSelectionChanged?.Invoke(selectedPlayer);
|
||||
_selectedPlayer = selectedPlayer;
|
||||
|
||||
// update label text. Only required if there is some override (e.g. unread bwoink count).
|
||||
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
|
||||
label.Text = GetText(selectedPlayer);
|
||||
}
|
||||
|
||||
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
|
||||
return;
|
||||
|
||||
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
public void StopFiltering()
|
||||
{
|
||||
FilterLineEdit.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void FilterList()
|
||||
{
|
||||
_sortedPlayerList.Clear();
|
||||
foreach (var info in _playerList)
|
||||
public PlayerListControl()
|
||||
{
|
||||
var displayName = $"{info.CharacterName} ({info.Username})";
|
||||
if (info.IdentityName != info.CharacterName)
|
||||
displayName += $" [{info.IdentityName}]";
|
||||
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
|
||||
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
|
||||
continue;
|
||||
_sortedPlayerList.Add(info);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
RobustXamlLoader.Load(this);
|
||||
// Fill the Option data
|
||||
PlayerListContainer.ItemPressed += PlayerListItemPressed;
|
||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||
PlayerListContainer.GenerateItem += GenerateButton;
|
||||
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
|
||||
PopulateList(_adminSystem.PlayerList);
|
||||
FilterLineEdit.OnTextChanged += _ => FilterList();
|
||||
_adminSystem.PlayerListChanged += PopulateList;
|
||||
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
|
||||
}
|
||||
|
||||
if (Comparison != null)
|
||||
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
|
||||
|
||||
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
|
||||
if (_selectedPlayer != null)
|
||||
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
|
||||
}
|
||||
|
||||
|
||||
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
|
||||
{
|
||||
// Maintain existing pin statuses
|
||||
var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
|
||||
|
||||
players ??= _adminSystem.PlayerList;
|
||||
|
||||
_playerList = players.ToList();
|
||||
|
||||
// Restore pin statuses
|
||||
foreach (var player in _playerList)
|
||||
private void PlayerListNoItemSelected()
|
||||
{
|
||||
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
|
||||
{
|
||||
player.IsPinned = pinnedPlayer.IsPinned;
|
||||
}
|
||||
}
|
||||
|
||||
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
|
||||
_selectedPlayer = null;
|
||||
OnSelectionChanged?.Invoke(null);
|
||||
}
|
||||
|
||||
FilterList();
|
||||
}
|
||||
|
||||
|
||||
private string GetText(PlayerInfo info)
|
||||
{
|
||||
var text = $"{info.CharacterName} ({info.Username})";
|
||||
if (OverrideText != null)
|
||||
text = OverrideText.Invoke(info, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var info })
|
||||
return;
|
||||
|
||||
var entry = new PlayerListEntry();
|
||||
entry.Setup(info, OverrideText);
|
||||
entry.OnPinStatusChanged += _ =>
|
||||
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
|
||||
return;
|
||||
|
||||
if (selectedPlayer == _selectedPlayer)
|
||||
return;
|
||||
|
||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
OnSelectionChanged?.Invoke(selectedPlayer);
|
||||
_selectedPlayer = selectedPlayer;
|
||||
|
||||
// update label text. Only required if there is some override (e.g. unread bwoink count).
|
||||
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
|
||||
label.Text = GetText(selectedPlayer);
|
||||
}
|
||||
|
||||
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
|
||||
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
|
||||
return;
|
||||
|
||||
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
public void StopFiltering()
|
||||
{
|
||||
FilterLineEdit.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void FilterList()
|
||||
{
|
||||
_sortedPlayerList.Clear();
|
||||
foreach (var info in _playerList)
|
||||
{
|
||||
var displayName = $"{info.CharacterName} ({info.Username})";
|
||||
if (info.IdentityName != info.CharacterName)
|
||||
displayName += $" [{info.IdentityName}]";
|
||||
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
|
||||
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
|
||||
continue;
|
||||
_sortedPlayerList.Add(info);
|
||||
}
|
||||
|
||||
if (Comparison != null)
|
||||
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
|
||||
|
||||
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
|
||||
if (_selectedPlayer != null)
|
||||
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
|
||||
}
|
||||
|
||||
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
|
||||
{
|
||||
players ??= _adminSystem.PlayerList;
|
||||
|
||||
_playerList = players.ToList();
|
||||
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
|
||||
_selectedPlayer = null;
|
||||
|
||||
FilterList();
|
||||
};
|
||||
}
|
||||
|
||||
button.AddChild(entry);
|
||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||
private string GetText(PlayerInfo info)
|
||||
{
|
||||
var text = $"{info.CharacterName} ({info.Username})";
|
||||
if (OverrideText != null)
|
||||
text = OverrideText.Invoke(info, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var info })
|
||||
return;
|
||||
|
||||
button.AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
ClipText = true,
|
||||
Text = GetText(info)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record PlayerListData(PlayerInfo Info) : ListData;
|
||||
public record PlayerListData(PlayerInfo Info) : ListData;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
Orientation="Horizontal" HorizontalExpand="true">
|
||||
<Label Name="PlayerEntryLabel" Text="" ClipText="True" HorizontalExpand="True" />
|
||||
<TextureButton Name="PlayerEntryPinButton"
|
||||
HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
@@ -1,58 +0,0 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.CustomControls;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerListEntry : BoxContainer
|
||||
{
|
||||
public PlayerListEntry()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public event Action<PlayerInfo>? OnPinStatusChanged;
|
||||
|
||||
public void Setup(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
|
||||
{
|
||||
Update(info, overrideText);
|
||||
PlayerEntryPinButton.OnPressed += HandlePinButtonPressed(info);
|
||||
}
|
||||
|
||||
private Action<BaseButton.ButtonEventArgs> HandlePinButtonPressed(PlayerInfo info)
|
||||
{
|
||||
return args =>
|
||||
{
|
||||
info.IsPinned = !info.IsPinned;
|
||||
UpdatePinButtonTexture(info.IsPinned);
|
||||
OnPinStatusChanged?.Invoke(info);
|
||||
};
|
||||
}
|
||||
|
||||
private void Update(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
|
||||
{
|
||||
PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
|
||||
$"{info.CharacterName} ({info.Username})";
|
||||
|
||||
UpdatePinButtonTexture(info.IsPinned);
|
||||
}
|
||||
|
||||
private void UpdatePinButtonTexture(bool isPinned)
|
||||
{
|
||||
if (isPinned)
|
||||
{
|
||||
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
|
||||
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
|
||||
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BorderThickness="2" BorderColor="#18181B" BackgroundColor="#25252a"/>
|
||||
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#18181B"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical" Margin="4 4 4 4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="PlayerNameLabel"/>
|
||||
<Label Name="IdLabel"/>
|
||||
<Label Name="TypeLabel"/>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
|
||||
Visible="False" HorizontalExpand="True" />
|
||||
<OptionButton Name="ExpiryLengthDropdown" Visible="False" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<OptionButton Name="TypeOption" HorizontalAlignment="Center" />
|
||||
|
||||
@@ -17,17 +17,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
|
||||
private enum Multipliers
|
||||
{
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
Weeks,
|
||||
Months,
|
||||
Years,
|
||||
Centuries
|
||||
}
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
|
||||
|
||||
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
|
||||
@@ -42,20 +31,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
// It's weird to use minutes as the IDs, but it works and makes sense kind of :)
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-minutes"), (int) Multipliers.Minutes);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-hours"), (int) Multipliers.Hours);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-days"), (int) Multipliers.Days);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-weeks"), (int) Multipliers.Weeks);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-months"), (int) Multipliers.Months);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-years"), (int) Multipliers.Years);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-centuries"), (int) Multipliers.Centuries);
|
||||
ExpiryLengthDropdown.OnItemSelected += OnLengthChanged;
|
||||
|
||||
ExpiryLengthDropdown.SelectId((int) Multipliers.Weeks);
|
||||
|
||||
ExpiryLineEdit.OnTextChanged += OnTextChanged;
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
|
||||
@@ -159,7 +134,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = false;
|
||||
PermanentCheckBox.Pressed = true;
|
||||
SubmitButton.Disabled = true;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Message: // Message: these are shown to the player when they log on
|
||||
@@ -198,9 +172,8 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
{
|
||||
ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLengthDropdown.Visible = !PermanentCheckBox.Pressed;
|
||||
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? 1.ToString() : string.Empty;
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
|
||||
}
|
||||
|
||||
private void OnSecretPressed(BaseButton.ButtonEventArgs _)
|
||||
@@ -214,16 +187,6 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SeverityOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnLengthChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
ExpiryLengthDropdown.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnTextChanged(HistoryLineEdit.LineEditEventArgs args)
|
||||
{
|
||||
ParseExpiryTime();
|
||||
}
|
||||
|
||||
private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (!ParseExpiryTime())
|
||||
@@ -300,24 +263,13 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !uint.TryParse(ExpiryLineEdit.Text, out var inputInt))
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !DateTime.TryParse(ExpiryLineEdit.Text, out var result) || DateTime.UtcNow > result)
|
||||
{
|
||||
ExpiryLineEdit.ModulateSelfOverride = Color.Red;
|
||||
return false;
|
||||
}
|
||||
|
||||
var mult = ExpiryLengthDropdown.SelectedId switch
|
||||
{
|
||||
(int) Multipliers.Minutes => TimeSpan.FromMinutes(1).TotalMinutes,
|
||||
(int) Multipliers.Hours => TimeSpan.FromHours(1).TotalMinutes,
|
||||
(int) Multipliers.Days => TimeSpan.FromDays(1).TotalMinutes,
|
||||
(int) Multipliers.Weeks => TimeSpan.FromDays(7).TotalMinutes,
|
||||
(int) Multipliers.Months => TimeSpan.FromDays(30).TotalMinutes,
|
||||
(int) Multipliers.Years => TimeSpan.FromDays(365).TotalMinutes,
|
||||
(int) Multipliers.Centuries => TimeSpan.FromDays(36525).TotalMinutes,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(ExpiryLengthDropdown.SelectedId), "Multiplier out of range :(")
|
||||
};
|
||||
ExpiryTime = DateTime.UtcNow.AddMinutes(inputInt * mult);
|
||||
ExpiryTime = result.ToUniversalTime();
|
||||
ExpiryLineEdit.ModulateSelfOverride = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,6 @@ namespace Content.Client.Administration.UI
|
||||
}
|
||||
|
||||
var title = string.IsNullOrWhiteSpace(popup.TitleEdit.Text) ? null : popup.TitleEdit.Text;
|
||||
var suspended = popup.SuspendedCheckbox.Pressed;
|
||||
|
||||
if (popup.SourceData is { } src)
|
||||
{
|
||||
@@ -140,8 +139,7 @@ namespace Content.Client.Administration.UI
|
||||
Title = title,
|
||||
PosFlags = pos,
|
||||
NegFlags = neg,
|
||||
RankId = rank,
|
||||
Suspended = suspended,
|
||||
RankId = rank
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -154,8 +152,7 @@ namespace Content.Client.Administration.UI
|
||||
Title = title,
|
||||
PosFlags = pos,
|
||||
NegFlags = neg,
|
||||
RankId = rank,
|
||||
Suspended = suspended,
|
||||
RankId = rank
|
||||
});
|
||||
}
|
||||
|
||||
@@ -174,7 +171,7 @@ namespace Content.Client.Administration.UI
|
||||
{
|
||||
Id = src,
|
||||
Flags = flags,
|
||||
Name = name,
|
||||
Name = name
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -354,7 +351,6 @@ namespace Content.Client.Administration.UI
|
||||
public readonly OptionButton RankButton;
|
||||
public readonly Button SaveButton;
|
||||
public readonly Button? RemoveButton;
|
||||
public readonly CheckBox SuspendedCheckbox;
|
||||
|
||||
public readonly Dictionary<AdminFlags, (Button inherit, Button sub, Button plus)> FlagButtons
|
||||
= new();
|
||||
@@ -385,12 +381,6 @@ namespace Content.Client.Administration.UI
|
||||
RankButton = new OptionButton();
|
||||
SaveButton = new Button { Text = Loc.GetString("permissions-eui-edit-admin-window-save-button"), HorizontalAlignment = HAlignment.Right };
|
||||
|
||||
SuspendedCheckbox = new CheckBox
|
||||
{
|
||||
Text = Loc.GetString("permissions-eui-edit-admin-window-suspended"),
|
||||
Pressed = data?.Suspended ?? false,
|
||||
};
|
||||
|
||||
RankButton.AddItem(Loc.GetString("permissions-eui-edit-admin-window-no-rank-button"), NoRank);
|
||||
foreach (var (rId, rank) in ui._ranks)
|
||||
{
|
||||
@@ -498,8 +488,7 @@ namespace Content.Client.Administration.UI
|
||||
{
|
||||
nameControl,
|
||||
TitleEdit,
|
||||
RankButton,
|
||||
SuspendedCheckbox,
|
||||
RankButton
|
||||
}
|
||||
},
|
||||
permGrid
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<ui:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc ban-panel-title}" MinSize="300 300">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="PlayerName"/>
|
||||
<Button Name="UsernameCopyButton" Text="{Loc player-panel-copy-username}"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Whitelisted"/>
|
||||
<controls:ConfirmButton Name="WhitelistToggle" Text="{Loc 'player-panel-false'}" Visible="False"></controls:ConfirmButton>
|
||||
</BoxContainer>
|
||||
<Label Name="Playtime"/>
|
||||
<Label Name="Notes"/>
|
||||
<Label Name="Bans"/>
|
||||
<Label Name="RoleBans"/>
|
||||
<Label Name="SharedConnections"/>
|
||||
|
||||
<BoxContainer Align="Center">
|
||||
<GridContainer Rows="5">
|
||||
<Button Name="NotesButton" Text="{Loc player-panel-show-notes}" SetWidth="136" Disabled="True"/>
|
||||
<Button Name="AhelpButton" Text="{Loc player-panel-help}" Disabled="True"/>
|
||||
<Button Name="FreezeButton" Text = "{Loc player-panel-freeze}" Disabled="True"/>
|
||||
<controls:ConfirmButton Name="KickButton" Text="{Loc player-panel-kick}" Disabled="True"/>
|
||||
<controls:ConfirmButton Name="DeleteButton" Text="{Loc player-panel-delete}" Disabled="True"/>
|
||||
<Button Name="ShowBansButton" Text="{Loc player-panel-show-bans}" SetWidth="136" Disabled="True"/>
|
||||
<Button Name="LogsButton" Text="{Loc player-panel-logs}" Disabled="True"/>
|
||||
<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"/>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ui:FancyWindow>
|
||||
@@ -1,132 +0,0 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.PlayerPanel;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerPanel : FancyWindow
|
||||
{
|
||||
private readonly IClientAdminManager _adminManager;
|
||||
|
||||
public event Action<string>? OnUsernameCopy;
|
||||
public event Action<NetUserId?>? OnOpenNotes;
|
||||
public event Action<NetUserId?>? OnOpenBans;
|
||||
public event Action<NetUserId?>? OnAhelp;
|
||||
public event Action<string?>? OnKick;
|
||||
public event Action<NetUserId?>? OnOpenBanPanel;
|
||||
public event Action<NetUserId?, bool>? OnWhitelistToggle;
|
||||
public event Action? OnFreezeAndMuteToggle;
|
||||
public event Action? OnFreeze;
|
||||
public event Action? OnLogs;
|
||||
public event Action? OnDelete;
|
||||
public event Action? OnRejuvenate;
|
||||
|
||||
public NetUserId? TargetPlayer;
|
||||
public string? TargetUsername;
|
||||
private bool _isWhitelisted;
|
||||
|
||||
public PlayerPanel(IClientAdminManager adminManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_adminManager = adminManager;
|
||||
|
||||
UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
|
||||
BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
|
||||
KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
|
||||
NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
|
||||
ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
|
||||
AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
|
||||
WhitelistToggle.OnPressed += _ =>
|
||||
{
|
||||
OnWhitelistToggle?.Invoke(TargetPlayer, _isWhitelisted);
|
||||
SetWhitelisted(!_isWhitelisted);
|
||||
};
|
||||
FreezeButton.OnPressed += _ => OnFreeze?.Invoke();
|
||||
FreezeAndMuteToggleButton.OnPressed += _ => OnFreezeAndMuteToggle?.Invoke();
|
||||
LogsButton.OnPressed += _ => OnLogs?.Invoke();
|
||||
DeleteButton.OnPressed += _ => OnDelete?.Invoke();
|
||||
RejuvenateButton.OnPressed += _ => OnRejuvenate?.Invoke();
|
||||
}
|
||||
|
||||
public void SetUsername(string player)
|
||||
{
|
||||
Title = Loc.GetString("player-panel-title", ("player", player));
|
||||
PlayerName.Text = Loc.GetString("player-panel-username", ("player", player));
|
||||
}
|
||||
|
||||
public void SetWhitelisted(bool? whitelisted)
|
||||
{
|
||||
if (whitelisted == null)
|
||||
{
|
||||
Whitelisted.Text = null;
|
||||
WhitelistToggle.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Whitelisted.Text = Loc.GetString("player-panel-whitelisted");
|
||||
WhitelistToggle.Text = whitelisted.Value ? Loc.GetString("player-panel-true") : Loc.GetString("player-panel-false");
|
||||
WhitelistToggle.Visible = true;
|
||||
_isWhitelisted = whitelisted.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBans(int? totalBans, int? totalRoleBans)
|
||||
{
|
||||
// If one value exists then so should the other.
|
||||
DebugTools.Assert(totalBans.HasValue && totalRoleBans.HasValue || totalBans == null && totalRoleBans == null);
|
||||
|
||||
Bans.Text = totalBans != null ? Loc.GetString("player-panel-bans", ("totalBans", totalBans)) : null;
|
||||
|
||||
RoleBans.Text = totalRoleBans != null ? Loc.GetString("player-panel-rolebans", ("totalRoleBans", totalRoleBans)) : null;
|
||||
}
|
||||
|
||||
public void SetNotes(int? totalNotes)
|
||||
{
|
||||
Notes.Text = totalNotes != null ? Loc.GetString("player-panel-notes", ("totalNotes", totalNotes)) : null;
|
||||
}
|
||||
|
||||
public void SetSharedConnections(int sharedConnections)
|
||||
{
|
||||
SharedConnections.Text = Loc.GetString("player-panel-shared-connections", ("sharedConnections", sharedConnections));
|
||||
}
|
||||
|
||||
public void SetPlaytime(TimeSpan playtime)
|
||||
{
|
||||
Playtime.Text = Loc.GetString("player-panel-playtime",
|
||||
("days", playtime.Days),
|
||||
("hours", playtime.Hours % 24),
|
||||
("minutes", playtime.Minutes % (24 * 60)));
|
||||
}
|
||||
|
||||
public void SetFrozen(bool canFreeze, bool frozen)
|
||||
{
|
||||
FreezeAndMuteToggleButton.Disabled = !canFreeze;
|
||||
FreezeButton.Disabled = !canFreeze || frozen;
|
||||
|
||||
FreezeAndMuteToggleButton.Text = Loc.GetString(!frozen ? "player-panel-freeze-and-mute" : "player-panel-unfreeze");
|
||||
}
|
||||
|
||||
public void SetAhelp(bool canAhelp)
|
||||
{
|
||||
AhelpButton.Disabled = !canAhelp;
|
||||
}
|
||||
|
||||
public void SetButtons()
|
||||
{
|
||||
BanButton.Disabled = !_adminManager.CanCommand("banpanel");
|
||||
KickButton.Disabled = !_adminManager.CanCommand("kick");
|
||||
NotesButton.Disabled = !_adminManager.CanCommand("adminnotes");
|
||||
ShowBansButton.Disabled = !_adminManager.CanCommand("banlist");
|
||||
WhitelistToggle.Disabled =
|
||||
!(_adminManager.CanCommand("whitelistadd") && _adminManager.CanCommand("whitelistremove"));
|
||||
LogsButton.Disabled = !_adminManager.CanCommand("adminlogs");
|
||||
RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
|
||||
DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Administration.UI.PlayerPanel;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class PlayerPanelEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IClipboardManager _clipboard = default!;
|
||||
|
||||
private PlayerPanel PlayerPanel { get; }
|
||||
|
||||
public PlayerPanelEui()
|
||||
{
|
||||
PlayerPanel = new PlayerPanel(_admin);
|
||||
|
||||
PlayerPanel.OnUsernameCopy += username => _clipboard.SetText(username);
|
||||
PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
|
||||
// Kick command does not support GUIDs
|
||||
PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
|
||||
PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
|
||||
PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
|
||||
PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
|
||||
PlayerPanel.OnWhitelistToggle += (id, whitelisted) =>
|
||||
{
|
||||
_console.ExecuteCommand(whitelisted ? $"whitelistremove \"{id}\"" : $"whitelistadd \"{id}\"");
|
||||
};
|
||||
|
||||
PlayerPanel.OnFreezeAndMuteToggle += () => SendMessage(new PlayerPanelFreezeMessage(true));
|
||||
PlayerPanel.OnFreeze += () => SendMessage(new PlayerPanelFreezeMessage());
|
||||
PlayerPanel.OnLogs += () => SendMessage(new PlayerPanelLogsMessage());
|
||||
PlayerPanel.OnRejuvenate += () => SendMessage(new PlayerPanelRejuvenationMessage());
|
||||
PlayerPanel.OnDelete+= () => SendMessage(new PlayerPanelDeleteMessage());
|
||||
|
||||
PlayerPanel.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
PlayerPanel.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
PlayerPanel.Close();
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if (state is not PlayerPanelEuiState s)
|
||||
return;
|
||||
|
||||
PlayerPanel.TargetPlayer = s.Guid;
|
||||
PlayerPanel.TargetUsername = s.Username;
|
||||
PlayerPanel.SetUsername(s.Username);
|
||||
PlayerPanel.SetPlaytime(s.Playtime);
|
||||
PlayerPanel.SetBans(s.TotalBans, s.TotalRoleBans);
|
||||
PlayerPanel.SetNotes(s.TotalNotes);
|
||||
PlayerPanel.SetWhitelisted(s.Whitelisted);
|
||||
PlayerPanel.SetSharedConnections(s.SharedConnections);
|
||||
PlayerPanel.SetFrozen(s.CanFreeze, s.Frozen);
|
||||
PlayerPanel.SetAhelp(s.CanAhelp);
|
||||
PlayerPanel.SetButtons();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Administration.UI.SetOutfit
|
||||
@@ -64,18 +64,9 @@ namespace Content.Client.Administration.UI.SetOutfit
|
||||
PopulateByFilter(SearchBar.Text);
|
||||
}
|
||||
|
||||
private IEnumerable<StartingGearPrototype> GetPrototypes()
|
||||
{
|
||||
// Filter out any StartingGearPrototypes that belong to loadouts
|
||||
var loadouts = _prototypeManager.EnumeratePrototypes<LoadoutPrototype>();
|
||||
var loadoutGears = loadouts.Select(l => l.StartingGear);
|
||||
return _prototypeManager.EnumeratePrototypes<StartingGearPrototype>()
|
||||
.Where(p => !loadoutGears.Contains(p.ID));
|
||||
}
|
||||
|
||||
private void PopulateList()
|
||||
{
|
||||
foreach (var gear in GetPrototypes())
|
||||
foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
|
||||
{
|
||||
OutfitList.Add(GetItem(gear, OutfitList));
|
||||
}
|
||||
@@ -84,7 +75,7 @@ namespace Content.Client.Administration.UI.SetOutfit
|
||||
private void PopulateByFilter(string filter)
|
||||
{
|
||||
OutfitList.Clear();
|
||||
foreach (var gear in GetPrototypes())
|
||||
foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(filter) &&
|
||||
gear.ID.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant()))
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<controls:BabyJailStatusWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
Title="{Loc admin-ui-baby-jail-window-title}">
|
||||
<RichTextLabel Name="MessageLabel" Access="Public" />
|
||||
</controls:BabyJailStatusWindow>
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailStatusWindow : FancyWindow
|
||||
{
|
||||
public BabyJailStatusWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
MessageLabel.SetMarkup(Loc.GetString("admin-ui-baby-jail-is-enabled"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<controls:BabyJailTab
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<cc:CommandButton Name="EnabledButton" Command="babyjail" ToggleMode="True"
|
||||
Text="{Loc admin-ui-baby-jail-disabled}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-tooltip}" />
|
||||
<cc:CommandButton Name="ShowReasonButton" Command="babyjail_show_reason"
|
||||
ToggleMode="True" Text="{Loc admin-ui-baby-jail-show-reason}"
|
||||
ToolTip="{Loc admin-ui-baby-jail-show-reason-tooltip}" />
|
||||
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-account-age}" MinWidth="175" />
|
||||
<LineEdit Name="MaxAccountAge" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||
<Label Text="{Loc admin-ui-baby-jail-max-overall-minutes}" MinWidth="175" />
|
||||
<LineEdit Name="MaxOverallMinutes" MinWidth="50" Margin="0 0 5 0" />
|
||||
<Label Text="{Loc generic-minutes}" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:BabyJailTab>
|
||||
@@ -0,0 +1,75 @@
|
||||
using Content.Shared.Administration.Events;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
/*
|
||||
* TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future.
|
||||
*/
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.BabyJailTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BabyJailTab : Control
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
|
||||
private string _maxAccountAge;
|
||||
private string _maxOverallMinutes;
|
||||
|
||||
public BabyJailTab()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
MaxAccountAge.OnTextEntered += args => SendMaxAccountAge(args.Text);
|
||||
MaxAccountAge.OnFocusExit += args => SendMaxAccountAge(args.Text);
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.OnTextEntered += args => SendMaxOverallMinutes(args.Text);
|
||||
MaxOverallMinutes.OnFocusExit += args => SendMaxOverallMinutes(args.Text);
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
|
||||
private void SendMaxAccountAge(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxAccountAge ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_account_age {minutes}");
|
||||
}
|
||||
|
||||
private void SendMaxOverallMinutes(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text) ||
|
||||
text == _maxOverallMinutes ||
|
||||
!int.TryParse(text, out var minutes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ExecuteCommand($"babyjail_max_overall_minutes {minutes}");
|
||||
}
|
||||
|
||||
public void UpdateStatus(BabyJailStatus status)
|
||||
{
|
||||
EnabledButton.Pressed = status.Enabled;
|
||||
EnabledButton.Text = Loc.GetString(status.Enabled
|
||||
? "admin-ui-baby-jail-enabled"
|
||||
: "admin-ui-baby-jail-disabled"
|
||||
);
|
||||
EnabledButton.ModulateSelfOverride = status.Enabled ? Color.Red : null;
|
||||
ShowReasonButton.Pressed = status.ShowReason;
|
||||
|
||||
MaxAccountAge.Text = status.MaxAccountAgeMinutes.ToString();
|
||||
_maxAccountAge = MaxAccountAge.Text;
|
||||
|
||||
MaxOverallMinutes.Text = status.MaxOverallMinutes.ToString();
|
||||
_maxOverallMinutes = MaxOverallMinutes.Text;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Station;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ObjectsTab : Control
|
||||
{
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||
@@ -51,20 +50,10 @@ public sealed partial class ObjectsTab : Control
|
||||
RefreshListButton.OnPressed += _ => RefreshObjectList();
|
||||
|
||||
var defaultSelection = ObjectsTabSelection.Grids;
|
||||
ObjectTypeOptions.SelectId((int)defaultSelection);
|
||||
ObjectTypeOptions.SelectId((int) defaultSelection);
|
||||
RefreshObjectList(defaultSelection);
|
||||
}
|
||||
|
||||
private void TeleportTo(NetEntity nent)
|
||||
{
|
||||
_console.ExecuteCommand($"tpto {nent}");
|
||||
}
|
||||
|
||||
private void Delete(NetEntity nent)
|
||||
{
|
||||
_console.ExecuteCommand($"delete {nent}");
|
||||
}
|
||||
|
||||
public void RefreshObjectList()
|
||||
{
|
||||
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
|
||||
@@ -128,9 +117,9 @@ public sealed partial class ObjectsTab : Control
|
||||
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
|
||||
return;
|
||||
|
||||
var entry = new ObjectsTabEntry(_admin, info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
|
||||
entry.OnTeleport += TeleportTo;
|
||||
entry.OnDelete += Delete;
|
||||
var entry = new ObjectsTabEntry(info.Name,
|
||||
info.Entity,
|
||||
new StyleBoxFlat { BackgroundColor = backgroundColor });
|
||||
button.ToolTip = $"{info.Name}, {info.Entity}";
|
||||
|
||||
button.AddChild(entry);
|
||||
|
||||
@@ -5,25 +5,13 @@
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="NameLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="EIDLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Button Name="TeleportButton"
|
||||
Text="{Loc object-tab-entity-teleport}"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Button Name="DeleteButton"
|
||||
Text="{Loc object-tab-entity-delete}"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<Label Name="EIDLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -11,30 +10,12 @@ public sealed partial class ObjectsTabEntry : PanelContainer
|
||||
{
|
||||
public NetEntity AssocEntity;
|
||||
|
||||
public Action<NetEntity>? OnTeleport;
|
||||
public Action<NetEntity>? OnDelete;
|
||||
private readonly Dictionary<Button, ConfirmationData> _confirmations = new();
|
||||
|
||||
public ObjectsTabEntry(IClientAdminManager manager, string name, NetEntity nent, StyleBox styleBox)
|
||||
public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
AssocEntity = nent;
|
||||
EIDLabel.Text = nent.ToString();
|
||||
NameLabel.Text = name;
|
||||
BackgroundColorPanel.PanelOverride = styleBox;
|
||||
|
||||
TeleportButton.Disabled = !manager.CanCommand("tpto");
|
||||
DeleteButton.Disabled = !manager.CanCommand("delete");
|
||||
|
||||
TeleportButton.OnPressed += _ => OnTeleport?.Invoke(nent);
|
||||
DeleteButton.OnPressed += _ =>
|
||||
{
|
||||
if (!AdminUIHelpers.TryConfirm(DeleteButton, _confirmations))
|
||||
{
|
||||
return;
|
||||
}
|
||||
OnDelete?.Invoke(nent);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,17 @@
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="4">
|
||||
<Label Name="ObjectNameLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-object-name}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="EntityIDLabel"
|
||||
SizeFlagsStretchRatio="5"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc object-tab-entity-id}"
|
||||
MouseFilter="Pass"/>
|
||||
<Label Name="EntityTeleportLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"/>
|
||||
<Label Name="EntityDeleteLabel"
|
||||
SizeFlagsStretchRatio="3"
|
||||
HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -2,239 +2,228 @@ using System.Linq;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerTab : Control
|
||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
|
||||
private const string ArrowUp = "↑";
|
||||
private const string ArrowDown = "↓";
|
||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||
private readonly AdminSystem _adminSystem;
|
||||
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
|
||||
|
||||
private Header _headerClicked = Header.Username;
|
||||
private bool _ascending = true;
|
||||
private bool _showDisconnected;
|
||||
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
|
||||
public PlayerTab()
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlayerTab : Control
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
||||
_adminSystem.OverlayEnabled += OverlayEnabled;
|
||||
_adminSystem.OverlayDisabled += OverlayDisabled;
|
||||
private const string ArrowUp = "↑";
|
||||
private const string ArrowDown = "↓";
|
||||
private readonly Color _altColor = Color.FromHex("#292B38");
|
||||
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
|
||||
private readonly AdminSystem _adminSystem;
|
||||
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
|
||||
|
||||
_config.OnValueChanged(CCVars.AdminPlayerlistSeparateSymbols, PlayerListSettingsChanged);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerListSettingsChanged);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerlistRoleTypeColor, PlayerListSettingsChanged);
|
||||
private Header _headerClicked = Header.Username;
|
||||
private bool _ascending = true;
|
||||
private bool _showDisconnected;
|
||||
|
||||
OverlayButton.OnPressed += OverlayButtonPressed;
|
||||
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
|
||||
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||
|
||||
SearchList.SearchBar = SearchLineEdit;
|
||||
SearchList.GenerateItem += GenerateButton;
|
||||
SearchList.DataFilterCondition += DataFilterCondition;
|
||||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
|
||||
}
|
||||
|
||||
#region Antag Overlay
|
||||
|
||||
private void OverlayEnabled()
|
||||
{
|
||||
OverlayButton.Pressed = true;
|
||||
}
|
||||
|
||||
private void OverlayDisabled()
|
||||
{
|
||||
OverlayButton.Pressed = false;
|
||||
}
|
||||
|
||||
private void OverlayButtonPressed(ButtonEventArgs args)
|
||||
{
|
||||
if (args.Button.Pressed)
|
||||
public PlayerTab()
|
||||
{
|
||||
_adminSystem.AdminOverlayOn();
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
_adminSystem.PlayerListChanged += RefreshPlayerList;
|
||||
_adminSystem.OverlayEnabled += OverlayEnabled;
|
||||
_adminSystem.OverlayDisabled += OverlayDisabled;
|
||||
|
||||
OverlayButton.OnPressed += OverlayButtonPressed;
|
||||
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
||||
|
||||
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
|
||||
ListHeader.OnHeaderClicked += HeaderClicked;
|
||||
|
||||
SearchList.SearchBar = SearchLineEdit;
|
||||
SearchList.GenerateItem += GenerateButton;
|
||||
SearchList.DataFilterCondition += DataFilterCondition;
|
||||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
|
||||
}
|
||||
else
|
||||
|
||||
#region Antag Overlay
|
||||
|
||||
private void OverlayEnabled()
|
||||
{
|
||||
_adminSystem.AdminOverlayOff();
|
||||
OverlayButton.Pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
||||
{
|
||||
_showDisconnected = args.Button.Pressed;
|
||||
RefreshPlayerList(_players);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
private void OverlayDisabled()
|
||||
{
|
||||
_adminSystem.PlayerListChanged -= RefreshPlayerList;
|
||||
_adminSystem.OverlayEnabled -= OverlayEnabled;
|
||||
_adminSystem.OverlayDisabled -= OverlayDisabled;
|
||||
|
||||
OverlayButton.OnPressed -= OverlayButtonPressed;
|
||||
|
||||
ListHeader.OnHeaderClicked -= HeaderClicked;
|
||||
OverlayButton.Pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
#region ListContainer
|
||||
|
||||
private void PlayerListSettingsChanged(bool _)
|
||||
{
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
}
|
||||
|
||||
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
||||
{
|
||||
_players = players;
|
||||
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
|
||||
|
||||
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
|
||||
|
||||
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
|
||||
sortedPlayers.Sort(Compare);
|
||||
|
||||
UpdateHeaderSymbols();
|
||||
|
||||
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
|
||||
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var player})
|
||||
return;
|
||||
|
||||
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
|
||||
button.AddChild(entry);
|
||||
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
|
||||
/// If all characters are lowercase, the comparison ignores case.
|
||||
/// If there is an uppercase character, the comparison is case sensitive.
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="listData"></param>
|
||||
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
|
||||
private bool DataFilterCondition(string filter, ListData listData)
|
||||
{
|
||||
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
|
||||
return false;
|
||||
|
||||
if (!_showDisconnected && !info.Connected)
|
||||
return false;
|
||||
|
||||
if (IsAllLower(filter))
|
||||
private void OverlayButtonPressed(ButtonEventArgs args)
|
||||
{
|
||||
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
|
||||
if (args.Button.Pressed)
|
||||
{
|
||||
_adminSystem.AdminOverlayOn();
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminSystem.AdminOverlayOff();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ShowDisconnectedPressed(ButtonEventArgs args)
|
||||
{
|
||||
_showDisconnected = args.Button.Pressed;
|
||||
RefreshPlayerList(_players);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_adminSystem.PlayerListChanged -= RefreshPlayerList;
|
||||
_adminSystem.OverlayEnabled -= OverlayEnabled;
|
||||
_adminSystem.OverlayDisabled -= OverlayDisabled;
|
||||
|
||||
OverlayButton.OnPressed -= OverlayButtonPressed;
|
||||
|
||||
ListHeader.OnHeaderClicked -= HeaderClicked;
|
||||
}
|
||||
}
|
||||
|
||||
#region ListContainer
|
||||
|
||||
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
||||
{
|
||||
_players = players;
|
||||
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
|
||||
|
||||
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
|
||||
|
||||
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
|
||||
sortedPlayers.Sort(Compare);
|
||||
|
||||
UpdateHeaderSymbols();
|
||||
|
||||
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
|
||||
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var player})
|
||||
return;
|
||||
|
||||
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
|
||||
button.AddChild(entry);
|
||||
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
|
||||
/// If all characters are lowercase, the comparison ignores case.
|
||||
/// If there is an uppercase character, the comparison is case sensitive.
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="listData"></param>
|
||||
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
|
||||
private bool DataFilterCondition(string filter, ListData listData)
|
||||
{
|
||||
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!playerString.Contains(filter))
|
||||
|
||||
if (!_showDisconnected && !info.Connected)
|
||||
return false;
|
||||
|
||||
if (IsAllLower(filter))
|
||||
{
|
||||
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!playerString.Contains(filter))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsAllLower(string input)
|
||||
{
|
||||
foreach (var c in input)
|
||||
private bool IsAllLower(string input)
|
||||
{
|
||||
if (char.IsLetter(c) && !char.IsLower(c))
|
||||
return false;
|
||||
foreach (var c in input)
|
||||
{
|
||||
if (char.IsLetter(c) && !char.IsLower(c))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Header
|
||||
|
||||
#region Header
|
||||
|
||||
private void UpdateHeaderSymbols()
|
||||
{
|
||||
ListHeader.ResetHeaderText();
|
||||
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
|
||||
}
|
||||
|
||||
private int Compare(PlayerInfo x, PlayerInfo y)
|
||||
{
|
||||
if (!_ascending)
|
||||
private void UpdateHeaderSymbols()
|
||||
{
|
||||
(x, y) = (y, x);
|
||||
ListHeader.ResetHeaderText();
|
||||
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
|
||||
}
|
||||
|
||||
return _headerClicked switch
|
||||
private int Compare(PlayerInfo x, PlayerInfo y)
|
||||
{
|
||||
Header.Username => Compare(x.Username, y.Username),
|
||||
Header.Character => Compare(x.CharacterName, y.CharacterName),
|
||||
Header.Job => Compare(x.StartingJob, y.StartingJob),
|
||||
Header.RoleType => y.SortWeight - x.SortWeight,
|
||||
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
if (!_ascending)
|
||||
{
|
||||
(x, y) = (y, x);
|
||||
}
|
||||
|
||||
private int Compare(string x, string y)
|
||||
{
|
||||
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void HeaderClicked(Header header)
|
||||
{
|
||||
if (_headerClicked == header)
|
||||
{
|
||||
_ascending = !_ascending;
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerClicked = header;
|
||||
_ascending = true;
|
||||
return _headerClicked switch
|
||||
{
|
||||
Header.Username => Compare(x.Username, y.Username),
|
||||
Header.Character => Compare(x.CharacterName, y.CharacterName),
|
||||
Header.Job => Compare(x.StartingJob, y.StartingJob),
|
||||
Header.Antagonist => x.Antag.CompareTo(y.Antag),
|
||||
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
private int Compare(string x, string y)
|
||||
{
|
||||
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void HeaderClicked(Header header)
|
||||
{
|
||||
if (_headerClicked == header)
|
||||
{
|
||||
_ascending = !_ascending;
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerClicked = header;
|
||||
_ascending = true;
|
||||
}
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
|
||||
}
|
||||
|
||||
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="RoleTypeLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
<Label Name="AntagonistLabel"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||
|
||||
@@ -16,25 +14,15 @@ public sealed partial class PlayerTabEntry : PanelContainer
|
||||
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
var config = IoCManager.Resolve<IConfigurationManager>();
|
||||
|
||||
UsernameLabel.Text = player.Username;
|
||||
if (!player.Connected)
|
||||
UsernameLabel.StyleClasses.Add("Disabled");
|
||||
JobLabel.Text = player.StartingJob;
|
||||
var separateAntagSymbols = config.GetCVar(CCVars.AdminPlayerlistSeparateSymbols);
|
||||
var genericAntagSymbol = player.Antag ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
|
||||
var roleSymbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
|
||||
var symbol = separateAntagSymbols ? roleSymbol : genericAntagSymbol;
|
||||
CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
|
||||
|
||||
if (player.Antag && config.GetCVar(CCVars.AdminPlayerlistHighlightedCharacterColor))
|
||||
CharacterLabel.FontColorOverride = player.RoleProto.Color;
|
||||
CharacterLabel.Text = player.CharacterName;
|
||||
if (player.IdentityName != player.CharacterName)
|
||||
CharacterLabel.Text += $" [{player.IdentityName}]";
|
||||
RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
|
||||
if (config.GetCVar(CCVars.AdminPlayerlistRoleTypeColor))
|
||||
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
|
||||
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
|
||||
BackgroundColorPanel.PanelOverride = styleBoxFlat;
|
||||
OverallPlaytimeLabel.Text = player.PlaytimeString;
|
||||
PlayerEntity = player.NetEntity;
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
Text="{Loc player-tab-job}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="RoleTypeLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
<Label Name="AntagonistLabel"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc player-tab-roletype}"
|
||||
Text="{Loc player-tab-antagonist}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="PlaytimeLabel"
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class PlayerTabHeader : Control
|
||||
UsernameLabel.OnKeyBindDown += UsernameClicked;
|
||||
CharacterLabel.OnKeyBindDown += CharacterClicked;
|
||||
JobLabel.OnKeyBindDown += JobClicked;
|
||||
RoleTypeLabel.OnKeyBindDown += RoleTypeClicked;
|
||||
AntagonistLabel.OnKeyBindDown += AntagonistClicked;
|
||||
PlaytimeLabel.OnKeyBindDown += PlaytimeClicked;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public sealed partial class PlayerTabHeader : Control
|
||||
Header.Username => UsernameLabel,
|
||||
Header.Character => CharacterLabel,
|
||||
Header.Job => JobLabel,
|
||||
Header.RoleType => RoleTypeLabel,
|
||||
Header.Antagonist => AntagonistLabel,
|
||||
Header.Playtime => PlaytimeLabel,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
|
||||
};
|
||||
@@ -40,7 +40,7 @@ public sealed partial class PlayerTabHeader : Control
|
||||
UsernameLabel.Text = Loc.GetString("player-tab-username");
|
||||
CharacterLabel.Text = Loc.GetString("player-tab-character");
|
||||
JobLabel.Text = Loc.GetString("player-tab-job");
|
||||
RoleTypeLabel.Text = Loc.GetString("player-tab-roletype");
|
||||
AntagonistLabel.Text = Loc.GetString("player-tab-antagonist");
|
||||
PlaytimeLabel.Text = Loc.GetString("player-tab-playtime");
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ public sealed partial class PlayerTabHeader : Control
|
||||
HeaderClicked(args, Header.Job);
|
||||
}
|
||||
|
||||
private void RoleTypeClicked(GUIBoundKeyEventArgs args)
|
||||
private void AntagonistClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.RoleType);
|
||||
HeaderClicked(args, Header.Antagonist);
|
||||
}
|
||||
|
||||
private void PlaytimeClicked(GUIBoundKeyEventArgs args)
|
||||
@@ -89,7 +89,7 @@ public sealed partial class PlayerTabHeader : Control
|
||||
UsernameLabel.OnKeyBindDown -= UsernameClicked;
|
||||
CharacterLabel.OnKeyBindDown -= CharacterClicked;
|
||||
JobLabel.OnKeyBindDown -= JobClicked;
|
||||
RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked;
|
||||
AntagonistLabel.OnKeyBindDown -= AntagonistClicked;
|
||||
PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public sealed partial class PlayerTabHeader : Control
|
||||
Username,
|
||||
Character,
|
||||
Job,
|
||||
RoleType,
|
||||
Antagonist,
|
||||
Playtime
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.Advertise.Systems;
|
||||
|
||||
namespace Content.Client.Advertise.Systems;
|
||||
|
||||
public sealed class SpeakOnUIClosedSystem : SharedSpeakOnUIClosedSystem;
|
||||
@@ -2,7 +2,6 @@ using System.Linq;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -25,7 +24,8 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<AlertsComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentHandleState>(OnHandleState);
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, AfterAutoHandleStateEvent>(ClientAlertsHandleState);
|
||||
}
|
||||
protected override void LoadPrototypes()
|
||||
{
|
||||
@@ -47,16 +47,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<AlertsComponent> alerts, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not AlertComponentState cast)
|
||||
return;
|
||||
|
||||
alerts.Comp.Alerts = new(cast.Alerts);
|
||||
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
protected override void AfterShowAlert(Entity<AlertsComponent> alerts)
|
||||
{
|
||||
UpdateHud(alerts);
|
||||
@@ -67,6 +57,11 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
private void ClientAlertsHandleState(Entity<AlertsComponent> alerts, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
private void UpdateHud(Entity<AlertsComponent> entity)
|
||||
{
|
||||
if (_playerManager.LocalEntity == entity.Owner)
|
||||
@@ -98,6 +93,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
|
||||
public void AlertClicked(ProtoId<AlertPrototype> alertType)
|
||||
{
|
||||
RaisePredictiveEvent(new ClickAlertEvent(alertType));
|
||||
RaiseNetworkEvent(new ClickAlertEvent(alertType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Ame.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Ame.UI
|
||||
{
|
||||
@@ -17,8 +16,9 @@ namespace Content.Client.Ame.UI
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<AmeWindow>();
|
||||
_window.OnAmeButton += ButtonPressed;
|
||||
_window = new AmeWindow(this);
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,5 +40,15 @@ namespace Content.Client.Ame.UI
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Ame.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -10,17 +9,15 @@ namespace Content.Client.Ame.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AmeWindow : DefaultWindow
|
||||
{
|
||||
public event Action<UiButton>? OnAmeButton;
|
||||
|
||||
public AmeWindow()
|
||||
public AmeWindow(AmeControllerBoundUserInterface ui)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
EjectButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.Eject);
|
||||
ToggleInjection.OnPressed += _ => OnAmeButton?.Invoke(UiButton.ToggleInjection);
|
||||
IncreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.IncreaseFuel);
|
||||
DecreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.DecreaseFuel);
|
||||
EjectButton.OnPressed += _ => ui.ButtonPressed(UiButton.Eject);
|
||||
ToggleInjection.OnPressed += _ => ui.ButtonPressed(UiButton.ToggleInjection);
|
||||
IncreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.IncreaseFuel);
|
||||
DecreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.DecreaseFuel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,7 +29,7 @@ namespace Content.Client.Ame.UI
|
||||
var castState = (AmeControllerBoundUserInterfaceState) state;
|
||||
|
||||
// Disable all buttons if not powered
|
||||
if (Contents.Children.Any())
|
||||
if (Contents.Children != null)
|
||||
{
|
||||
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
|
||||
EjectButton.Disabled = false;
|
||||
@@ -68,8 +65,8 @@ namespace Content.Client.Ame.UI
|
||||
CoreCount.Text = $"{castState.CoreCount}";
|
||||
InjectionAmount.Text = $"{castState.InjectionAmount}";
|
||||
// format power statistics to pretty numbers
|
||||
CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply:N1}";
|
||||
TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply:N1}";
|
||||
CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply.ToString("N1")}";
|
||||
TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply.ToString("N1")}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,8 @@ public sealed class AnomalySystem : SharedAnomalySystem
|
||||
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
||||
SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete);
|
||||
|
||||
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
|
||||
{
|
||||
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
|
||||
@@ -76,13 +75,4 @@ public sealed class AnomalySystem : SharedAnomalySystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<AnomalySupercriticalComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Scale = Vector2.One;
|
||||
sprite.Color = sprite.Color.WithAlpha(1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Content.Shared.Anomaly.Effects;
|
||||
using Content.Shared.Body.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Anomaly.Effects;
|
||||
|
||||
public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<InnerBodyAnomalyComponent, AfterAutoHandleStateEvent>(OnAfterHandleState);
|
||||
SubscribeLocalEvent<InnerBodyAnomalyComponent, ComponentShutdown>(OnCompShutdown);
|
||||
}
|
||||
|
||||
private void OnAfterHandleState(Entity<InnerBodyAnomalyComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
|
||||
if (ent.Comp.FallbackSprite is null)
|
||||
return;
|
||||
|
||||
if (!sprite.LayerMapTryGet(ent.Comp.LayerMap, out var index))
|
||||
index = sprite.LayerMapReserveBlank(ent.Comp.LayerMap);
|
||||
|
||||
if (TryComp<BodyComponent>(ent, out var body) &&
|
||||
body.Prototype is not null &&
|
||||
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
|
||||
{
|
||||
sprite.LayerSetSprite(index, speciesSprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetSprite(index, ent.Comp.FallbackSprite);
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(index, true);
|
||||
sprite.LayerSetShader(index, "unshaded");
|
||||
}
|
||||
|
||||
private void OnCompShutdown(Entity<InnerBodyAnomalyComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
|
||||
var index = sprite.LayerMapGet(ent.Comp.LayerMap);
|
||||
sprite.LayerSetVisible(index, false);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Anomaly;
|
||||
using Content.Shared.Gravity;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Anomaly.Ui;
|
||||
|
||||
@@ -17,8 +18,10 @@ public sealed class AnomalyGeneratorBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<AnomalyGeneratorWindow>();
|
||||
_window.SetEntity(Owner);
|
||||
_window = new(Owner);
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OnGenerateButtonPressed += () =>
|
||||
{
|
||||
@@ -34,5 +37,18 @@ public sealed class AnomalyGeneratorBoundUserInterface : BoundUserInterface
|
||||
return;
|
||||
_window?.UpdateState(msg);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
|
||||
public void SetPowerSwitch(bool on)
|
||||
{
|
||||
SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,21 +18,17 @@ public sealed partial class AnomalyGeneratorWindow : FancyWindow
|
||||
|
||||
public Action? OnGenerateButtonPressed;
|
||||
|
||||
public AnomalyGeneratorWindow()
|
||||
public AnomalyGeneratorWindow(EntityUid gen)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
EntityView.SetEntity(gen);
|
||||
EntityView.SpriteOffset = false;
|
||||
|
||||
GenerateButton.OnPressed += _ => OnGenerateButtonPressed?.Invoke();
|
||||
}
|
||||
|
||||
public void SetEntity(EntityUid uid)
|
||||
{
|
||||
EntityView.SetEntity(uid);
|
||||
}
|
||||
|
||||
public void UpdateState(AnomalyGeneratorUserInterfaceState state)
|
||||
{
|
||||
_cooldownEnd = state.CooldownEndTime;
|
||||
|
||||
@@ -31,7 +31,7 @@ public sealed partial class AnomalyScannerMenu : FancyWindow
|
||||
msg.PushNewline();
|
||||
var time = NextPulseTime.Value - _timing.CurTime;
|
||||
var timestring = $"{time.Minutes:00}:{time.Seconds:00}";
|
||||
msg.AddMarkupOrThrow(Loc.GetString("anomaly-scanner-pulse-timer", ("time", timestring)));
|
||||
msg.AddMarkup(Loc.GetString("anomaly-scanner-pulse-timer", ("time", timestring)));
|
||||
}
|
||||
|
||||
TextDisplay.SetMarkup(msg.ToMarkup());
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace Content.Client.Arcade
|
||||
|
||||
private static readonly Vector2 BlockSize = new(15, 15);
|
||||
|
||||
private readonly BlockGameBoundUserInterface _owner;
|
||||
|
||||
private readonly PanelContainer _mainPanel;
|
||||
|
||||
private readonly BoxContainer _gameRootContainer;
|
||||
@@ -56,11 +58,10 @@ namespace Content.Client.Arcade
|
||||
private bool _isPlayer = false;
|
||||
private bool _gameOver = false;
|
||||
|
||||
public event Action<BlockGamePlayerAction>? OnAction;
|
||||
|
||||
public BlockGameMenu()
|
||||
public BlockGameMenu(BlockGameBoundUserInterface owner)
|
||||
{
|
||||
Title = Loc.GetString("blockgame-menu-title");
|
||||
_owner = owner;
|
||||
|
||||
MinSize = SetSize = new Vector2(410, 490);
|
||||
|
||||
@@ -175,7 +176,7 @@ namespace Content.Client.Arcade
|
||||
};
|
||||
_newGameButton.OnPressed += (e) =>
|
||||
{
|
||||
OnAction?.Invoke(BlockGamePlayerAction.NewGame);
|
||||
_owner.SendAction(BlockGamePlayerAction.NewGame);
|
||||
};
|
||||
pauseMenuContainer.AddChild(_newGameButton);
|
||||
pauseMenuContainer.AddChild(new Control { MinSize = new Vector2(1, 10) });
|
||||
@@ -185,10 +186,7 @@ namespace Content.Client.Arcade
|
||||
Text = Loc.GetString("blockgame-menu-button-scoreboard"),
|
||||
TextAlign = Label.AlignMode.Center
|
||||
};
|
||||
_scoreBoardButton.OnPressed += (e) =>
|
||||
{
|
||||
OnAction?.Invoke(BlockGamePlayerAction.ShowHighscores);
|
||||
};
|
||||
_scoreBoardButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.ShowHighscores);
|
||||
pauseMenuContainer.AddChild(_scoreBoardButton);
|
||||
_unpauseButtonMargin = new Control { MinSize = new Vector2(1, 10), Visible = false };
|
||||
pauseMenuContainer.AddChild(_unpauseButtonMargin);
|
||||
@@ -201,7 +199,7 @@ namespace Content.Client.Arcade
|
||||
};
|
||||
_unpauseButton.OnPressed += (e) =>
|
||||
{
|
||||
OnAction?.Invoke(BlockGamePlayerAction.Unpause);
|
||||
_owner.SendAction(BlockGamePlayerAction.Unpause);
|
||||
};
|
||||
pauseMenuContainer.AddChild(_unpauseButton);
|
||||
|
||||
@@ -259,7 +257,7 @@ namespace Content.Client.Arcade
|
||||
};
|
||||
_finalNewGameButton.OnPressed += (e) =>
|
||||
{
|
||||
OnAction?.Invoke(BlockGamePlayerAction.NewGame);
|
||||
_owner.SendAction(BlockGamePlayerAction.NewGame);
|
||||
};
|
||||
gameOverMenuContainer.AddChild(_finalNewGameButton);
|
||||
|
||||
@@ -329,10 +327,7 @@ namespace Content.Client.Arcade
|
||||
Text = Loc.GetString("blockgame-menu-button-back"),
|
||||
TextAlign = Label.AlignMode.Center
|
||||
};
|
||||
_highscoreBackButton.OnPressed += (e) =>
|
||||
{
|
||||
OnAction?.Invoke(BlockGamePlayerAction.Pause);
|
||||
};
|
||||
_highscoreBackButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.Pause);
|
||||
menuContainer.AddChild(_highscoreBackButton);
|
||||
|
||||
menuInnerPanel.AddChild(menuContainer);
|
||||
@@ -380,7 +375,7 @@ namespace Content.Client.Arcade
|
||||
{
|
||||
PanelOverride = back,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 34.25f
|
||||
SizeFlagsStretchRatio = 60
|
||||
};
|
||||
var backgroundPanel = new PanelContainer
|
||||
{
|
||||
@@ -478,7 +473,7 @@ namespace Content.Client.Arcade
|
||||
|
||||
private void TryPause()
|
||||
{
|
||||
OnAction?.Invoke(BlockGamePlayerAction.Pause);
|
||||
_owner.SendAction(BlockGamePlayerAction.Pause);
|
||||
}
|
||||
|
||||
public void SetStarted()
|
||||
@@ -581,19 +576,19 @@ namespace Content.Client.Arcade
|
||||
return;
|
||||
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeLeft)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.StartLeft);
|
||||
_owner.SendAction(BlockGamePlayerAction.StartLeft);
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeRight)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.StartRight);
|
||||
_owner.SendAction(BlockGamePlayerAction.StartRight);
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeUp)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.Rotate);
|
||||
_owner.SendAction(BlockGamePlayerAction.Rotate);
|
||||
else if (args.Function == ContentKeyFunctions.Arcade3)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.CounterRotate);
|
||||
_owner.SendAction(BlockGamePlayerAction.CounterRotate);
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeDown)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.SoftdropStart);
|
||||
_owner.SendAction(BlockGamePlayerAction.SoftdropStart);
|
||||
else if (args.Function == ContentKeyFunctions.Arcade2)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.Hold);
|
||||
_owner.SendAction(BlockGamePlayerAction.Hold);
|
||||
else if (args.Function == ContentKeyFunctions.Arcade1)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.Harddrop);
|
||||
_owner.SendAction(BlockGamePlayerAction.Harddrop);
|
||||
}
|
||||
|
||||
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
@@ -604,11 +599,11 @@ namespace Content.Client.Arcade
|
||||
return;
|
||||
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeLeft)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.EndLeft);
|
||||
_owner.SendAction(BlockGamePlayerAction.EndLeft);
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeRight)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.EndRight);
|
||||
_owner.SendAction(BlockGamePlayerAction.EndRight);
|
||||
else if (args.Function == ContentKeyFunctions.ArcadeDown)
|
||||
OnAction?.Invoke(BlockGamePlayerAction.SoftdropEnd);
|
||||
_owner.SendAction(BlockGamePlayerAction.SoftdropEnd);
|
||||
}
|
||||
|
||||
public void UpdateNextBlock(BlockGameBlock[] blocks)
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.Arcade
|
||||
{
|
||||
public sealed class SpaceVillainArcadeMenu : DefaultWindow
|
||||
{
|
||||
public SpaceVillainArcadeBoundUserInterface Owner { get; set; }
|
||||
|
||||
private readonly Label _enemyNameLabel;
|
||||
private readonly Label _playerInfoLabel;
|
||||
private readonly Label _enemyInfoLabel;
|
||||
@@ -15,13 +17,11 @@ namespace Content.Client.Arcade
|
||||
private readonly Label _enemyActionLabel;
|
||||
|
||||
private readonly Button[] _gameButtons = new Button[3]; //used to disable/enable all game buttons
|
||||
|
||||
public event Action<SharedSpaceVillainArcadeComponent.PlayerAction>? OnPlayerAction;
|
||||
|
||||
public SpaceVillainArcadeMenu()
|
||||
public SpaceVillainArcadeMenu(SpaceVillainArcadeBoundUserInterface owner)
|
||||
{
|
||||
MinSize = SetSize = new Vector2(300, 225);
|
||||
Title = Loc.GetString("spacevillain-menu-title");
|
||||
Owner = owner;
|
||||
|
||||
var grid = new GridContainer { Columns = 1 };
|
||||
|
||||
@@ -47,43 +47,32 @@ namespace Content.Client.Arcade
|
||||
grid.AddChild(_enemyActionLabel);
|
||||
|
||||
var buttonGrid = new GridContainer { Columns = 3 };
|
||||
_gameButtons[0] = new Button()
|
||||
_gameButtons[0] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Attack)
|
||||
{
|
||||
Text = Loc.GetString("spacevillain-menu-button-attack")
|
||||
};
|
||||
|
||||
_gameButtons[0].OnPressed +=
|
||||
_ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Attack);
|
||||
buttonGrid.AddChild(_gameButtons[0]);
|
||||
|
||||
_gameButtons[1] = new Button()
|
||||
_gameButtons[1] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Heal)
|
||||
{
|
||||
Text = Loc.GetString("spacevillain-menu-button-heal")
|
||||
};
|
||||
|
||||
_gameButtons[1].OnPressed +=
|
||||
_ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Heal);
|
||||
buttonGrid.AddChild(_gameButtons[1]);
|
||||
|
||||
_gameButtons[2] = new Button()
|
||||
_gameButtons[2] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Recharge)
|
||||
{
|
||||
Text = Loc.GetString("spacevillain-menu-button-recharge")
|
||||
};
|
||||
|
||||
_gameButtons[2].OnPressed +=
|
||||
_ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Recharge);
|
||||
buttonGrid.AddChild(_gameButtons[2]);
|
||||
|
||||
centerContainer = new CenterContainer();
|
||||
centerContainer.AddChild(buttonGrid);
|
||||
grid.AddChild(centerContainer);
|
||||
|
||||
var newGame = new Button()
|
||||
var newGame = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.NewGame)
|
||||
{
|
||||
Text = Loc.GetString("spacevillain-menu-button-new-game")
|
||||
};
|
||||
|
||||
newGame.OnPressed += _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.NewGame);
|
||||
grid.AddChild(newGame);
|
||||
|
||||
Contents.AddChild(grid);
|
||||
@@ -110,5 +99,23 @@ namespace Content.Client.Arcade
|
||||
_playerActionLabel.Text = message.PlayerActionMessage;
|
||||
_enemyActionLabel.Text = message.EnemyActionMessage;
|
||||
}
|
||||
|
||||
private sealed class ActionButton : Button
|
||||
{
|
||||
private readonly SpaceVillainArcadeBoundUserInterface _owner;
|
||||
private readonly SharedSpaceVillainArcadeComponent.PlayerAction _playerAction;
|
||||
|
||||
public ActionButton(SpaceVillainArcadeBoundUserInterface owner, SharedSpaceVillainArcadeComponent.PlayerAction playerAction)
|
||||
{
|
||||
_owner = owner;
|
||||
_playerAction = playerAction;
|
||||
OnPressed += Clicked;
|
||||
}
|
||||
|
||||
private void Clicked(ButtonEventArgs e)
|
||||
{
|
||||
_owner.SendAction(_playerAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Arcade;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Arcade.UI;
|
||||
|
||||
@@ -16,8 +15,9 @@ public sealed class BlockGameBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<BlockGameMenu>();
|
||||
_menu.OnAction += SendAction;
|
||||
_menu = new BlockGameMenu(this);
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent;
|
||||
@@ -10,6 +9,8 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables] private SpaceVillainArcadeMenu? _menu;
|
||||
|
||||
//public SharedSpaceVillainArcadeComponent SpaceVillainArcade;
|
||||
|
||||
public SpaceVillainArcadeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
SendAction(PlayerAction.RequestData);
|
||||
@@ -24,8 +25,10 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<SpaceVillainArcadeMenu>();
|
||||
_menu.OnPlayerAction += SendAction;
|
||||
_menu = new SpaceVillainArcadeMenu(this);
|
||||
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
@@ -33,4 +36,12 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
|
||||
if (message is SpaceVillainArcadeDataUpdateMessage msg)
|
||||
_menu?.UpdateInfo(msg);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Atmos.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PipeColorVisualsComponent : Component;
|
||||
public sealed partial class PipeColorVisualsComponent : Component
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
|
||||
|
||||
<!-- Device selection button -->
|
||||
<Button Name="FocusButton" HorizontalExpand="True" SetHeight="32" Margin="12 0 0 0" StyleClasses="OpenBoth" Access="Public">
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
|
||||
|
||||
<!-- Alarm state -->
|
||||
<TextureRect Stretch="Keep" HorizontalAlignment="Left" Margin="-20 -2 0 0" ModulateSelfOverride="#25252a" TexturePath="/Textures/Interface/AtmosMonitoring/status_bg.png">
|
||||
<BoxContainer VerticalExpand="True" HorizontalExpand="True" Orientation="Horizontal" Margin="8 0">
|
||||
<TextureRect Name="ArrowTexture" VerticalAlignment="Center" SetSize="12 12" Stretch="KeepAspectCentered" Margin="3 0" TexturePath="/Textures/Interface/Nano/triangle_right.png"></TextureRect>
|
||||
<Label Name="AlarmStateLabel" HorizontalExpand="True" HorizontalAlignment="Center" FontColorOverride="#5A5A5A" Text="{Loc 'atmos-alerts-window-invalid-state'}"></Label>
|
||||
</BoxContainer>
|
||||
</TextureRect>
|
||||
|
||||
<!-- Alarm name -->
|
||||
<Label Name="AlarmNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Center" Margin="5 0"></Label>
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
|
||||
<!-- Panel that appears on selecting the device -->
|
||||
<PanelContainer Name="FocusContainer" HorizontalExpand="True" Margin="1 -1 1 0" ReservesSpace="False" Visible="False" Access="Public">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
|
||||
<!-- Atmosphere status -->
|
||||
<Control>
|
||||
|
||||
<!-- Main container for displaying atmospheric data -->
|
||||
<BoxContainer Name="MainDataContainer" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical" ReservesSpace="False" Visible="False">
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="TemperatureHeaderLabel" Text="{Loc 'atmos-alerts-window-temperature-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="PressureHeaderLabel" Text="{Loc 'atmos-alerts-window-pressure-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="OxygenationHeaderLabel" Text="{Loc 'atmos-alerts-window-oxygenation-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
<PanelContainer HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="TemperatureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="PressureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="OxygenationLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="GasesHeaderLabel" Text="{Loc 'atmos-alerts-window-other-gases-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 4 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
<PanelContainer HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
|
||||
<!-- Gas entries added via C# code -->
|
||||
<GridContainer Name="GasGridContainer" HorizontalExpand="True" Columns = "4"></GridContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- If the alarm is inactive, this is label is displayed instead -->
|
||||
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label>
|
||||
|
||||
<!-- Silencing progress bar -->
|
||||
<controls:StripeBack Name="SilenceAlarmProgressBar" ReservesSpace="False" Visible="False" Access="Public">
|
||||
<PanelContainer>
|
||||
<Label Text="{Loc 'atmos-alerts-window-alerts-being-silenced'}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5 5 5 5"/>
|
||||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
</Control>
|
||||
|
||||
<!-- Check box for silencing this alarm -->
|
||||
<CheckBox Name="SilenceCheckBox" Text="{Loc 'atmos-alerts-window-silence-alerts'}" HorizontalAlignment="Left" Margin="5 5 5 5" Access="Public"></CheckBox>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
</BoxContainer>
|
||||
@@ -1,200 +0,0 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||
{
|
||||
public NetEntity NetEntity;
|
||||
public EntityCoordinates? Coordinates;
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IResourceCache _cache;
|
||||
|
||||
private Dictionary<AtmosAlarmType, string> _alarmStrings = new Dictionary<AtmosAlarmType, string>()
|
||||
{
|
||||
[AtmosAlarmType.Invalid] = "atmos-alerts-window-invalid-state",
|
||||
[AtmosAlarmType.Normal] = "atmos-alerts-window-normal-state",
|
||||
[AtmosAlarmType.Warning] = "atmos-alerts-window-warning-state",
|
||||
[AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state",
|
||||
};
|
||||
|
||||
public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_cache = IoCManager.Resolve<IResourceCache>();
|
||||
|
||||
NetEntity = uid;
|
||||
Coordinates = coordinates;
|
||||
|
||||
// Load fonts
|
||||
var headerFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), 11);
|
||||
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||
var smallFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
|
||||
// Set fonts
|
||||
TemperatureHeaderLabel.FontOverride = headerFont;
|
||||
PressureHeaderLabel.FontOverride = headerFont;
|
||||
OxygenationHeaderLabel.FontOverride = headerFont;
|
||||
GasesHeaderLabel.FontOverride = headerFont;
|
||||
|
||||
TemperatureLabel.FontOverride = normalFont;
|
||||
PressureLabel.FontOverride = normalFont;
|
||||
OxygenationLabel.FontOverride = normalFont;
|
||||
|
||||
NoDataLabel.FontOverride = headerFont;
|
||||
|
||||
SilenceCheckBox.Label.FontOverride = smallFont;
|
||||
SilenceCheckBox.Label.FontColorOverride = Color.DarkGray;
|
||||
}
|
||||
|
||||
public void UpdateEntry(AtmosAlertsComputerEntry entry, bool isFocus, AtmosAlertsFocusDeviceData? focusData = null)
|
||||
{
|
||||
NetEntity = entry.NetEntity;
|
||||
Coordinates = _entManager.GetCoordinates(entry.Coordinates);
|
||||
|
||||
// Load fonts
|
||||
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||
|
||||
// Update alarm state
|
||||
if (!_alarmStrings.TryGetValue(entry.AlarmState, out var alarmString))
|
||||
alarmString = "atmos-alerts-window-invalid-state";
|
||||
|
||||
AlarmStateLabel.Text = Loc.GetString(alarmString);
|
||||
AlarmStateLabel.FontColorOverride = GetAlarmStateColor(entry.AlarmState);
|
||||
|
||||
// Update alarm name
|
||||
AlarmNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", entry.EntityName), ("address", entry.Address));
|
||||
|
||||
// Focus updates
|
||||
FocusContainer.Visible = isFocus;
|
||||
|
||||
if (isFocus)
|
||||
SetAsFocus();
|
||||
else
|
||||
RemoveAsFocus();
|
||||
|
||||
if (isFocus && entry.Group == AtmosAlertsComputerGroup.AirAlarm)
|
||||
{
|
||||
MainDataContainer.Visible = (entry.AlarmState != AtmosAlarmType.Invalid);
|
||||
NoDataLabel.Visible = (entry.AlarmState == AtmosAlarmType.Invalid);
|
||||
|
||||
if (focusData != null)
|
||||
{
|
||||
// Update temperature
|
||||
var tempK = (FixedPoint2)focusData.Value.TemperatureData.Item1;
|
||||
var tempC = (FixedPoint2)TemperatureHelpers.KelvinToCelsius(tempK.Float());
|
||||
|
||||
TemperatureLabel.Text = Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK));
|
||||
TemperatureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.TemperatureData.Item2);
|
||||
|
||||
// Update pressure
|
||||
PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2)focusData.Value.PressureData.Item1));
|
||||
PressureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.PressureData.Item2);
|
||||
|
||||
// Update oxygenation
|
||||
var oxygenPercent = (FixedPoint2)0f;
|
||||
var oxygenAlert = AtmosAlarmType.Invalid;
|
||||
|
||||
if (focusData.Value.GasData.TryGetValue(Gas.Oxygen, out var oxygenData))
|
||||
{
|
||||
oxygenPercent = oxygenData.Item2 * 100f;
|
||||
oxygenAlert = oxygenData.Item3;
|
||||
}
|
||||
|
||||
OxygenationLabel.Text = Loc.GetString("atmos-alerts-window-oxygenation-value", ("value", oxygenPercent));
|
||||
OxygenationLabel.FontColorOverride = GetAlarmStateColor(oxygenAlert);
|
||||
|
||||
// Update other present gases
|
||||
GasGridContainer.RemoveAllChildren();
|
||||
|
||||
var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen);
|
||||
var keyValuePairs = gasData.ToList();
|
||||
|
||||
if (keyValuePairs.Count == 0)
|
||||
{
|
||||
// No other gases
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value-nil"),
|
||||
FontOverride = normalFont,
|
||||
FontColorOverride = StyleNano.DisabledFore,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
SetHeight = 24f,
|
||||
};
|
||||
|
||||
GasGridContainer.AddChild(gasLabel);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Add an entry for each gas
|
||||
foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs)
|
||||
{
|
||||
FixedPoint2 gasPercent = percent * 100f;
|
||||
var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
|
||||
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasAbbreviation), ("value", gasPercent)),
|
||||
FontOverride = normalFont,
|
||||
FontColorOverride = GetAlarmStateColor(alert),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
SetHeight = 24f,
|
||||
};
|
||||
|
||||
GasGridContainer.AddChild(gasLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAsFocus()
|
||||
{
|
||||
FocusButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||
ArrowTexture.TexturePath = "/Textures/Interface/Nano/inverted_triangle.svg.png";
|
||||
}
|
||||
|
||||
public void RemoveAsFocus()
|
||||
{
|
||||
FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||
ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png";
|
||||
FocusContainer.Visible = false;
|
||||
}
|
||||
|
||||
private Color GetAlarmStateColor(AtmosAlarmType alarmType)
|
||||
{
|
||||
switch (alarmType)
|
||||
{
|
||||
case AtmosAlarmType.Normal:
|
||||
return StyleNano.GoodGreenFore;
|
||||
case AtmosAlarmType.Warning:
|
||||
return StyleNano.ConcerningOrangeFore;
|
||||
case AtmosAlarmType.Danger:
|
||||
return StyleNano.DangerousRedFore;
|
||||
}
|
||||
|
||||
return StyleNano.DisabledFore;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user