Compare commits

..

2 Commits

Author SHA1 Message Date
comasqw
a20f2ba6a3 refactor Prototype class init 2024-11-19 17:12:42 +04:00
comasqw
c00de7fd09 refactor 2024-11-19 17:09:14 +04:00
3879 changed files with 123150 additions and 1239292 deletions

37
.github/CODEOWNERS vendored
View File

@@ -2,30 +2,49 @@
# Sorting by path instead of by who added it one day :( # 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 # 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 # Moony's Gargantuan List Of Things She Cares About, or MGLOTSCA for short.
/Content.*/Administration/ @DrSmugleaf @nikthechampiongr @crazybrain23 # You need to add your name to these entries, not make a new one, if you care about them.
/Resources/ServerInfo/ @nikthechampiongr @crazybrain23 /Content.*/Toolshed/ @moonheart08
/Resources/ServerInfo/Guidebook/ServerRules/ @nikthechampiongr @crazybrain23 **/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/Body/ @DrSmugleaf # suffering
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf /Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
/Resources/Prototypes/Entities/Mobs/Species/ @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.*/Body/ @DrSmugleaf
/Content.YAMLLinter @DrSmugleaf /Content.YAMLLinter @DrSmugleaf
/Content.Shared/Damage/ @DrSmugleaf /Content.Shared/Damage/ @DrSmugleaf
/Content.*/Anomaly/ @TheShuEd /Content.*/Anomaly/ @EmoGarbage404 @TheShuEd
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @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 /Content.*/Forensics/ @ficcialfaint
# SKREEEE # SKREEEE
/Content.*.Database/ @PJB3005 @DrSmugleaf /Content.*.Database/ @PJB3005 @DrSmugleaf
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @nikthechampiongr @crazybrain23 /Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @Chief-Engineer
/Pow3r/ @PJB3005 /Pow3r/ @PJB3005
/Content.Server/Power/Pow3r/ @PJB3005 /Content.Server/Power/Pow3r/ @PJB3005
@@ -33,7 +52,7 @@
/Content.*/Atmos/ @Partmedia /Content.*/Atmos/ @Partmedia
/Content.*/Botany/ @Partmedia /Content.*/Botany/ @Partmedia
# Jezi #Jezi
/Content.*/Medical @Jezithyr /Content.*/Medical @Jezithyr
/Content.*/Body @Jezithyr /Content.*/Body @Jezithyr

6
.github/labeler.yml vendored
View File

@@ -16,11 +16,7 @@
- changed-files: - changed-files:
- any-glob-to-any-file: '**/*.swsl' - any-glob-to-any-file: '**/*.swsl'
"Changes: Audio": "No C#":
- changed-files:
- any-glob-to-any-file: '**/*.ogg'
"Changes: No C#":
- changed-files: - 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. # 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" - all-globs-to-all-files: "!**/*.cs"

View File

@@ -21,7 +21,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 8.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore

View File

@@ -2,11 +2,11 @@
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ master, staging, trying ]
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, reopened, synchronize, ready_for_review ] types: [ opened, reopened, synchronize, ready_for_review ]
branches: [ master, staging, stable ] branches: [ master ]
jobs: jobs:
build: build:
@@ -36,7 +36,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 8.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore

View File

@@ -2,11 +2,11 @@ name: Build & Test Debug
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ master, staging, trying ]
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, reopened, synchronize, ready_for_review ] types: [ opened, reopened, synchronize, ready_for_review ]
branches: [ master, staging, stable ] branches: [ master ]
jobs: jobs:
build: build:
@@ -36,7 +36,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 8.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore

View File

@@ -1,38 +0,0 @@
name: "CLA Assistant"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened,closed,synchronize]
paths:
- '**/*.cs' # Указываем, что триггер срабатывает только при изменении .cs файлов
# 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'
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.'

View File

@@ -16,6 +16,6 @@ jobs:
- name: Check for Merge Conflicts - name: Check for Merge Conflicts
uses: eps1lon/actions-label-merge-conflict@v3.0.0 uses: eps1lon/actions-label-merge-conflict@v3.0.0
with: with:
dirtyLabel: "S: Merge Conflict" dirtyLabel: "Merge Conflict"
repoToken: "${{ secrets.GITHUB_TOKEN }}" repoToken: "${{ secrets.GITHUB_TOKEN }}"
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request." commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."

View File

@@ -10,7 +10,7 @@ jobs:
steps: steps:
- uses: actions-ecosystem/action-add-labels@v1 - uses: actions-ecosystem/action-add-labels@v1
with: with:
labels: "S: Needs Review" labels: "Status: Needs Review"
- uses: actions-ecosystem/action-remove-labels@v1 - uses: actions-ecosystem/action-remove-labels@v1
with: with:
labels: "S: Awaiting Changes" labels: "Status: Awaiting Changes"

View File

@@ -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"

View File

@@ -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"
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -3,8 +3,6 @@
on: on:
issues: issues:
types: [opened] types: [opened]
pull_request_target:
types: [opened]
jobs: jobs:
add_label: add_label:
@@ -13,4 +11,4 @@ jobs:
- uses: actions-ecosystem/action-add-labels@v1 - uses: actions-ecosystem/action-add-labels@v1
if: join(github.event.issue.labels) == '' if: join(github.event.issue.labels) == ''
with: with:
labels: "S: Untriaged" labels: "Status: Untriaged"

View File

@@ -22,7 +22,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 8.0.x
- name: Get Engine Tag - name: Get Engine Tag
run: | run: |

View File

@@ -2,7 +2,7 @@
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ master, staging, trying ]
paths: paths:
- '**.cs' - '**.cs'
- '**.csproj' - '**.csproj'
@@ -16,7 +16,7 @@ on:
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, reopened, synchronize, ready_for_review ] types: [ opened, reopened, synchronize, ready_for_review ]
branches: [ master, staging, stable ] branches: [ master ]
paths: paths:
- '**.cs' - '**.cs'
- '**.csproj' - '**.csproj'
@@ -51,7 +51,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 8.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore

View File

@@ -1,7 +1,7 @@
name: RGA schema validator name: RGA schema validator
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ master, staging, trying ]
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, reopened, synchronize, ready_for_review ] types: [ opened, reopened, synchronize, ready_for_review ]

View File

@@ -2,7 +2,7 @@ name: RSI Validator
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ staging, trying ]
merge_group: merge_group:
pull_request: pull_request:
paths: paths:

View File

@@ -1,7 +1,7 @@
name: Map file schema validator name: Map file schema validator
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ master, staging, trying ]
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, reopened, synchronize, ready_for_review ] types: [ opened, reopened, synchronize, ready_for_review ]

View File

@@ -2,7 +2,7 @@ name: YAML Linter
on: on:
push: push:
branches: [ master, staging, stable ] branches: [ master, staging, trying ]
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, reopened, synchronize, ready_for_review ] types: [ opened, reopened, synchronize, ready_for_review ]
@@ -26,7 +26,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 8.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore
- name: Build - name: Build

80
CLA.md
View File

@@ -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 partys 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.

View File

@@ -88,9 +88,8 @@ namespace Content.Client.Access.UI
button.Disabled = !interfaceEnabled; button.Disabled = !interfaceEnabled;
if (interfaceEnabled) if (interfaceEnabled)
{ {
// Explicit cast because Rider gives a false error otherwise. button.Pressed = state.TargetAccessReaderIdAccessList?.Contains(accessName) ?? false;
button.Pressed = state.TargetAccessReaderIdAccessList?.Contains((ProtoId<AccessLevelPrototype>) accessName) ?? false; button.Disabled = (!state.AllowedModifyAccessList?.Contains(accessName)) ?? true;
button.Disabled = (!state.AllowedModifyAccessList?.Contains((ProtoId<AccessLevelPrototype>) accessName)) ?? true;
} }
} }
} }

View File

@@ -258,13 +258,13 @@ namespace Content.Client.Actions
public void LinkAllActions(ActionsComponent? actions = null) public void LinkAllActions(ActionsComponent? actions = null)
{ {
if (_playerManager.LocalEntity is not { } user || if (_playerManager.LocalEntity is not { } user ||
!Resolve(user, ref actions, false)) !Resolve(user, ref actions, false))
{ {
return; return;
} }
LinkActions?.Invoke(actions); LinkActions?.Invoke(actions);
} }
public override void Shutdown() public override void Shutdown()

View File

@@ -1,21 +1,16 @@
using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.Administration.Systems; using Content.Client.Administration.Systems;
using Content.Shared.CCVar;
using Content.Shared.Mind;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.Configuration; using Robust.Shared;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Configuration;
namespace Content.Client.Administration; namespace Content.Client.Administration;
internal sealed class AdminNameOverlay : Overlay internal sealed class AdminNameOverlay : Overlay
{ {
[Dependency] private readonly IConfigurationManager _config = default!;
private readonly AdminSystem _system; private readonly AdminSystem _system;
private readonly IEntityManager _entityManager; private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager; private readonly IEyeManager _eyeManager;
@@ -23,16 +18,8 @@ internal sealed class AdminNameOverlay : Overlay
private readonly IUserInterfaceManager _userInterfaceManager; private readonly IUserInterfaceManager _userInterfaceManager;
private readonly Font _font; private readonly Font _font;
//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");
private readonly Color _antagColorClassic = Color.OrangeRed;
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager) public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager)
{ {
IoCManager.InjectDependencies(this);
_system = system; _system = system;
_entityManager = entityManager; _entityManager = entityManager;
_eyeManager = eyeManager; _eyeManager = eyeManager;
@@ -48,9 +35,6 @@ internal sealed class AdminNameOverlay : Overlay
{ {
var viewport = args.WorldAABB; var viewport = args.WorldAABB;
//TODO make this adjustable via GUI
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
foreach (var playerInfo in _system.PlayerList) foreach (var playerInfo in _system.PlayerList)
{ {
var entity = _entityManager.GetEntity(playerInfo.NetEntity); var entity = _entityManager.GetEntity(playerInfo.NetEntity);
@@ -80,20 +64,12 @@ internal sealed class AdminNameOverlay : Overlay
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center + var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec( new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f); aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
if (playerInfo.Antag)
if (classic && playerInfo.Antag)
{ {
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), _antagLabelClassic, uiScale, _antagColorClassic); args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", uiScale, Color.OrangeRed);
;
} }
else if (!classic && _filter.Contains(playerInfo.RoleProto.ID)) args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
{
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var color = playerInfo.RoleProto.Color;
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), label, uiScale, color);
}
args.ScreenHandle.DrawString(_font, screenCoordinates + lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White); args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
} }
} }

View File

@@ -6,7 +6,8 @@
xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs" xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs"
xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab" xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab"
xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab" 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"> <TabContainer Name="MasterTabContainer">
<adminTab:AdminTab /> <adminTab:AdminTab />
<adminbusTab:AdminbusTab /> <adminbusTab:AdminbusTab />
@@ -14,6 +15,7 @@
<tabs:RoundTab /> <tabs:RoundTab />
<tabs:ServerTab /> <tabs:ServerTab />
<panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" /> <panic:PanicBunkerTab Name="PanicBunkerControl" Access="Public" />
<baby:BabyJailTab Name="BabyJailControl" Access="Public" />
<playerTab:PlayerTab Name="PlayerTabControl" Access="Public" /> <playerTab:PlayerTab Name="PlayerTabControl" Access="Public" />
<objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" /> <objectsTab:ObjectsTab Name="ObjectsTabControl" Access="Public" />
</TabContainer> </TabContainer>

View File

@@ -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.Round, Loc.GetString("admin-menu-round-tab"));
MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab")); MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab"));
MasterTabContainer.SetTabTitle((int) TabIndex.PanicBunker, Loc.GetString("admin-menu-panic-bunker-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.Players, Loc.GetString("admin-menu-players-tab"));
MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab")); MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab"));
MasterTabContainer.OnTabChanged += OnTabChanged; MasterTabContainer.OnTabChanged += OnTabChanged;
@@ -48,6 +52,7 @@ public sealed partial class AdminMenuWindow : DefaultWindow
Round, Round,
Server, Server,
PanicBunker, PanicBunker,
BabyJail,
Players, Players,
Objects, Objects,
} }

View File

@@ -22,11 +22,11 @@ namespace Content.Client.Administration.UI.BanPanel;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class BanPanel : DefaultWindow 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; public event Action<string>? PlayerChanged;
private string? PlayerUsername { get; set; } private string? PlayerUsername { get; set; }
private (IPAddress, int)? IpAddress { get; set; } private (IPAddress, int)? IpAddress { get; set; }
private ImmutableTypedHwid? Hwid { get; set; } private byte[]? Hwid { get; set; }
private double TimeEntered { get; set; } private double TimeEntered { get; set; }
private uint Multiplier { get; set; } private uint Multiplier { get; set; }
private bool HasBanFlag { get; set; } private bool HasBanFlag { get; set; }
@@ -371,8 +371,9 @@ public sealed partial class BanPanel : DefaultWindow
private void OnHwidChanged() private void OnHwidChanged()
{ {
var hwidString = HwidLine.Text; var hwidString = HwidLine.Text;
ImmutableTypedHwid? hwid = null; var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid)) Hwid = new byte[length];
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
{ {
ErrorLevel |= ErrorLevelEnum.Hwid; ErrorLevel |= ErrorLevelEnum.Hwid;
HwidLine.ModulateSelfOverride = Color.Red; HwidLine.ModulateSelfOverride = Color.Red;
@@ -389,7 +390,7 @@ public sealed partial class BanPanel : DefaultWindow
Hwid = null; Hwid = null;
return; return;
} }
Hwid = hwid; Hwid = Convert.FromHexString(hwidString);
} }
private void OnTypeChanged() private void OnTypeChanged()

View File

@@ -8,7 +8,6 @@
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" /> <Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}" <HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
Visible="False" HorizontalExpand="True" /> Visible="False" HorizontalExpand="True" />
<OptionButton Name="ExpiryLengthDropdown" Visible="False" />
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<OptionButton Name="TypeOption" HorizontalAlignment="Center" /> <OptionButton Name="TypeOption" HorizontalAlignment="Center" />

View File

@@ -17,17 +17,6 @@ public sealed partial class NoteEdit : FancyWindow
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IClientConsoleHost _console = 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 event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit) public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
@@ -42,20 +31,6 @@ public sealed partial class NoteEdit : FancyWindow
ResetSubmitButton(); 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-note"), (int) NoteType.Note);
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message); TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist); 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; SecretCheckBox.Pressed = false;
SeverityOption.Disabled = false; SeverityOption.Disabled = false;
PermanentCheckBox.Pressed = true; PermanentCheckBox.Pressed = true;
SubmitButton.Disabled = true;
UpdatePermanentCheckboxFields(); UpdatePermanentCheckboxFields();
break; break;
case (int) NoteType.Message: // Message: these are shown to the player when they log on 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; ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
ExpiryLineEdit.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 _) private void OnSecretPressed(BaseButton.ButtonEventArgs _)
@@ -214,16 +187,6 @@ public sealed partial class NoteEdit : FancyWindow
SeverityOption.SelectId(args.Id); 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) private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
{ {
if (!ParseExpiryTime()) if (!ParseExpiryTime())
@@ -300,24 +263,13 @@ public sealed partial class NoteEdit : FancyWindow
return true; 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; ExpiryLineEdit.ModulateSelfOverride = Color.Red;
return false; return false;
} }
var mult = ExpiryLengthDropdown.SelectedId switch ExpiryTime = result.ToUniversalTime();
{
(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);
ExpiryLineEdit.ModulateSelfOverride = null; ExpiryLineEdit.ModulateSelfOverride = null;
return true; return true;
} }

View File

@@ -130,7 +130,6 @@ namespace Content.Client.Administration.UI
} }
var title = string.IsNullOrWhiteSpace(popup.TitleEdit.Text) ? null : popup.TitleEdit.Text; var title = string.IsNullOrWhiteSpace(popup.TitleEdit.Text) ? null : popup.TitleEdit.Text;
var suspended = popup.SuspendedCheckbox.Pressed;
if (popup.SourceData is { } src) if (popup.SourceData is { } src)
{ {
@@ -140,8 +139,7 @@ namespace Content.Client.Administration.UI
Title = title, Title = title,
PosFlags = pos, PosFlags = pos,
NegFlags = neg, NegFlags = neg,
RankId = rank, RankId = rank
Suspended = suspended,
}); });
} }
else else
@@ -154,8 +152,7 @@ namespace Content.Client.Administration.UI
Title = title, Title = title,
PosFlags = pos, PosFlags = pos,
NegFlags = neg, NegFlags = neg,
RankId = rank, RankId = rank
Suspended = suspended,
}); });
} }
@@ -174,7 +171,7 @@ namespace Content.Client.Administration.UI
{ {
Id = src, Id = src,
Flags = flags, Flags = flags,
Name = name, Name = name
}); });
} }
else else
@@ -354,7 +351,6 @@ namespace Content.Client.Administration.UI
public readonly OptionButton RankButton; public readonly OptionButton RankButton;
public readonly Button SaveButton; public readonly Button SaveButton;
public readonly Button? RemoveButton; public readonly Button? RemoveButton;
public readonly CheckBox SuspendedCheckbox;
public readonly Dictionary<AdminFlags, (Button inherit, Button sub, Button plus)> FlagButtons public readonly Dictionary<AdminFlags, (Button inherit, Button sub, Button plus)> FlagButtons
= new(); = new();
@@ -385,12 +381,6 @@ namespace Content.Client.Administration.UI
RankButton = new OptionButton(); RankButton = new OptionButton();
SaveButton = new Button { Text = Loc.GetString("permissions-eui-edit-admin-window-save-button"), HorizontalAlignment = HAlignment.Right }; 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); RankButton.AddItem(Loc.GetString("permissions-eui-edit-admin-window-no-rank-button"), NoRank);
foreach (var (rId, rank) in ui._ranks) foreach (var (rId, rank) in ui._ranks)
{ {
@@ -498,8 +488,7 @@ namespace Content.Client.Administration.UI
{ {
nameControl, nameControl,
TitleEdit, TitleEdit,
RankButton, RankButton
SuspendedCheckbox,
} }
}, },
permGrid permGrid

View File

@@ -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>

View File

@@ -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"));
}
}

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -197,7 +197,6 @@ public sealed partial class PlayerTab : Control
Header.Character => Compare(x.CharacterName, y.CharacterName), Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob), Header.Job => Compare(x.StartingJob, y.StartingJob),
Header.Antagonist => x.Antag.CompareTo(y.Antag), Header.Antagonist => x.Antag.CompareTo(y.Antag),
Header.RoleType => Compare(x.RoleProto.Name , y.RoleProto.Name),
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default), Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1 _ => 1
}; };

View File

@@ -24,11 +24,6 @@
HorizontalExpand="True" HorizontalExpand="True"
ClipText="True"/> ClipText="True"/>
<customControls:VSeparator/> <customControls:VSeparator/>
<Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="OverallPlaytimeLabel" <Label Name="OverallPlaytimeLabel"
SizeFlagsStretchRatio="1" SizeFlagsStretchRatio="1"
HorizontalExpand="True" HorizontalExpand="True"

View File

@@ -23,8 +23,6 @@ public sealed partial class PlayerTabEntry : PanelContainer
if (player.IdentityName != player.CharacterName) if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]"; CharacterLabel.Text += $" [{player.IdentityName}]";
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no"); AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
BackgroundColorPanel.PanelOverride = styleBoxFlat; BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString; OverallPlaytimeLabel.Text = player.PlaytimeString;
PlayerEntity = player.NetEntity; PlayerEntity = player.NetEntity;

View File

@@ -32,13 +32,6 @@
Text="{Loc player-tab-antagonist}" Text="{Loc player-tab-antagonist}"
MouseFilter="Pass"/> MouseFilter="Pass"/>
<cc:VSeparator/> <cc:VSeparator/>
<Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"
Text="{Loc player-tab-roletype}"
MouseFilter="Pass"/>
<cc:VSeparator/>
<Label Name="PlaytimeLabel" <Label Name="PlaytimeLabel"
SizeFlagsStretchRatio="1" SizeFlagsStretchRatio="1"
HorizontalExpand="True" HorizontalExpand="True"

View File

@@ -19,7 +19,6 @@ public sealed partial class PlayerTabHeader : Control
CharacterLabel.OnKeyBindDown += CharacterClicked; CharacterLabel.OnKeyBindDown += CharacterClicked;
JobLabel.OnKeyBindDown += JobClicked; JobLabel.OnKeyBindDown += JobClicked;
AntagonistLabel.OnKeyBindDown += AntagonistClicked; AntagonistLabel.OnKeyBindDown += AntagonistClicked;
RoleTypeLabel.OnKeyBindDown += RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown += PlaytimeClicked; PlaytimeLabel.OnKeyBindDown += PlaytimeClicked;
} }
@@ -31,7 +30,6 @@ public sealed partial class PlayerTabHeader : Control
Header.Character => CharacterLabel, Header.Character => CharacterLabel,
Header.Job => JobLabel, Header.Job => JobLabel,
Header.Antagonist => AntagonistLabel, Header.Antagonist => AntagonistLabel,
Header.RoleType => RoleTypeLabel,
Header.Playtime => PlaytimeLabel, Header.Playtime => PlaytimeLabel,
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null) _ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
}; };
@@ -43,7 +41,6 @@ public sealed partial class PlayerTabHeader : Control
CharacterLabel.Text = Loc.GetString("player-tab-character"); CharacterLabel.Text = Loc.GetString("player-tab-character");
JobLabel.Text = Loc.GetString("player-tab-job"); JobLabel.Text = Loc.GetString("player-tab-job");
AntagonistLabel.Text = Loc.GetString("player-tab-antagonist"); AntagonistLabel.Text = Loc.GetString("player-tab-antagonist");
RoleTypeLabel.Text = Loc.GetString("player-tab-roletype");
PlaytimeLabel.Text = Loc.GetString("player-tab-playtime"); PlaytimeLabel.Text = Loc.GetString("player-tab-playtime");
} }
@@ -78,11 +75,6 @@ public sealed partial class PlayerTabHeader : Control
HeaderClicked(args, Header.Antagonist); HeaderClicked(args, Header.Antagonist);
} }
private void RoleTypeClicked(GUIBoundKeyEventArgs args)
{
HeaderClicked(args, Header.RoleType);
}
private void PlaytimeClicked(GUIBoundKeyEventArgs args) private void PlaytimeClicked(GUIBoundKeyEventArgs args)
{ {
HeaderClicked(args, Header.Playtime); HeaderClicked(args, Header.Playtime);
@@ -98,7 +90,6 @@ public sealed partial class PlayerTabHeader : Control
CharacterLabel.OnKeyBindDown -= CharacterClicked; CharacterLabel.OnKeyBindDown -= CharacterClicked;
JobLabel.OnKeyBindDown -= JobClicked; JobLabel.OnKeyBindDown -= JobClicked;
AntagonistLabel.OnKeyBindDown -= AntagonistClicked; AntagonistLabel.OnKeyBindDown -= AntagonistClicked;
RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked; PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked;
} }
} }
@@ -109,7 +100,6 @@ public sealed partial class PlayerTabHeader : Control
Character, Character,
Job, Job,
Antagonist, Antagonist,
RoleType,
Playtime Playtime
} }
} }

View File

@@ -2,7 +2,6 @@ using System.Linq;
using Content.Shared.Alert; using Content.Shared.Alert;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Shared.GameStates;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -25,7 +24,8 @@ public sealed class ClientAlertsSystem : AlertsSystem
SubscribeLocalEvent<AlertsComponent, LocalPlayerAttachedEvent>(OnPlayerAttached); SubscribeLocalEvent<AlertsComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<AlertsComponent, LocalPlayerDetachedEvent>(OnPlayerDetached); SubscribeLocalEvent<AlertsComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<AlertsComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<AlertsComponent, AfterAutoHandleStateEvent>(ClientAlertsHandleState);
} }
protected override void LoadPrototypes() 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 = cast.Alerts;
UpdateHud(alerts);
}
protected override void AfterShowAlert(Entity<AlertsComponent> alerts) protected override void AfterShowAlert(Entity<AlertsComponent> alerts)
{ {
UpdateHud(alerts); UpdateHud(alerts);
@@ -67,6 +57,11 @@ public sealed class ClientAlertsSystem : AlertsSystem
UpdateHud(alerts); UpdateHud(alerts);
} }
private void ClientAlertsHandleState(Entity<AlertsComponent> alerts, ref AfterAutoHandleStateEvent args)
{
UpdateHud(alerts);
}
private void UpdateHud(Entity<AlertsComponent> entity) private void UpdateHud(Entity<AlertsComponent> entity)
{ {
if (_playerManager.LocalEntity == entity.Owner) if (_playerManager.LocalEntity == entity.Owner)

View File

@@ -1,4 +1,8 @@
using Robust.Shared.GameObjects;
namespace Content.Client.Atmos.Components; namespace Content.Client.Atmos.Components;
[RegisterComponent] [RegisterComponent]
public sealed partial class PipeColorVisualsComponent : Component; public sealed partial class PipeColorVisualsComponent : Component
{
}

View File

@@ -1,5 +1,6 @@
<BoxContainer xmlns="https://spacestation14.io" <BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:Content.Client.Stylesheets"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3"> Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
@@ -61,7 +62,7 @@
</PanelContainer> </PanelContainer>
</BoxContainer> </BoxContainer>
<!-- If the alarm is inactive, this is label is displayed instead --> <!-- If the alarm is inactive, this is label is diplayed instead -->
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label> <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 --> <!-- Silencing progress bar -->

View File

@@ -31,6 +31,19 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
[AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state", [AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state",
}; };
private Dictionary<Gas, string> _gasShorthands = new Dictionary<Gas, string>()
{
[Gas.Ammonia] = "NH₃",
[Gas.CarbonDioxide] = "CO₂",
[Gas.Frezon] = "F",
[Gas.Nitrogen] = "N₂",
[Gas.NitrousOxide] = "N₂O",
[Gas.Oxygen] = "O₂",
[Gas.Plasma] = "P",
[Gas.Tritium] = "T",
[Gas.WaterVapor] = "H₂O",
};
public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates) public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -123,9 +136,8 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
GasGridContainer.RemoveAllChildren(); GasGridContainer.RemoveAllChildren();
var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen); var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen);
var keyValuePairs = gasData.ToList();
if (keyValuePairs.Count == 0) if (gasData.Count() == 0)
{ {
// No other gases // No other gases
var gasLabel = new Label() var gasLabel = new Label()
@@ -146,14 +158,17 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
else else
{ {
// Add an entry for each gas // Add an entry for each gas
foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs) foreach ((var gas, (var mol, var percent, var alert)) in gasData)
{ {
FixedPoint2 gasPercent = percent * 100f; var gasPercent = (FixedPoint2)0f;
var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation")); gasPercent = percent * 100f;
if (!_gasShorthands.TryGetValue(gas, out var gasShorthand))
gasShorthand = "X";
var gasLabel = new Label() var gasLabel = new Label()
{ {
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasAbbreviation), ("value", gasPercent)), Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasShorthand), ("value", gasPercent)),
FontOverride = normalFont, FontOverride = normalFont,
FontColorOverride = GetAlarmStateColor(alert), FontColorOverride = GetAlarmStateColor(alert),
HorizontalAlignment = HAlignment.Center, HorizontalAlignment = HAlignment.Center,

View File

@@ -14,6 +14,8 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
_menu = new AtmosAlertsComputerWindow(this, Owner); _menu = new AtmosAlertsComputerWindow(this, Owner);
_menu.OpenCentered(); _menu.OpenCentered();
_menu.OnClose += Close; _menu.OnClose += Close;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
} }
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
@@ -22,6 +24,9 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
var castState = (AtmosAlertsComputerBoundInterfaceState) state; var castState = (AtmosAlertsComputerBoundInterfaceState) state;
if (castState == null)
return;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform); EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
_menu?.UpdateUI(xform?.Coordinates, castState.AirAlarms, castState.FireAlarms, castState.FocusData); _menu?.UpdateUI(xform?.Coordinates, castState.AirAlarms, castState.FireAlarms, castState.FocusData);
} }

View File

@@ -1,6 +1,7 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI" xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'atmos-alerts-window-title'}" Title="{Loc 'atmos-alerts-window-title'}"
Resizable="False" Resizable="False"
SetSize="1120 750" SetSize="1120 750"

View File

@@ -1,40 +0,0 @@
using Content.Shared.Atmos.Components;
namespace Content.Client.Atmos.Consoles;
public sealed class AtmosMonitoringConsoleBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private AtmosMonitoringConsoleWindow? _menu;
public AtmosMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
protected override void Open()
{
base.Open();
_menu = new AtmosMonitoringConsoleWindow(this, Owner);
_menu.OpenCentered();
_menu.OnClose += Close;
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not AtmosMonitoringConsoleBoundInterfaceState castState)
return;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
_menu?.UpdateUI(xform?.Coordinates, castState.AtmosNetworks);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
}

View File

@@ -1,295 +0,0 @@
using Content.Client.Pinpointer.UI;
using Content.Shared.Atmos.Components;
using Content.Shared.Pinpointer;
using Robust.Client.Graphics;
using Robust.Shared.Collections;
using Robust.Shared.Map.Components;
using System.Linq;
using System.Numerics;
namespace Content.Client.Atmos.Consoles;
public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
{
[Dependency] private readonly IEntityManager _entManager = default!;
public bool ShowPipeNetwork = true;
public int? FocusNetId = null;
private const int ChunkSize = 4;
private readonly Color _basePipeNetColor = Color.LightGray;
private readonly Color _unfocusedPipeNetColor = Color.DimGray;
private List<AtmosMonitoringConsoleLine> _atmosPipeNetwork = new();
private Dictionary<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
// Look up tables for merging continuous lines. Indexed by line color
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _horizLines = new();
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _horizLinesReversed = new();
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _vertLines = new();
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _vertLinesReversed = new();
public AtmosMonitoringConsoleNavMapControl() : base()
{
PostWallDrawingAction += DrawAllPipeNetworks;
}
protected override void UpdateNavMap()
{
base.UpdateNavMap();
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(Owner, out var console))
return;
if (!_entManager.TryGetComponent<MapGridComponent>(MapUid, out var grid))
return;
_atmosPipeNetwork = GetDecodedAtmosPipeChunks(console.AtmosPipeChunks, grid);
}
private void DrawAllPipeNetworks(DrawingHandleScreen handle)
{
if (!ShowPipeNetwork)
return;
// Draw networks
if (_atmosPipeNetwork != null && _atmosPipeNetwork.Any())
DrawPipeNetwork(handle, _atmosPipeNetwork);
}
private void DrawPipeNetwork(DrawingHandleScreen handle, List<AtmosMonitoringConsoleLine> atmosPipeNetwork)
{
var offset = GetOffset();
offset = offset with { Y = -offset.Y };
if (WorldRange / WorldMaxRange > 0.5f)
{
var pipeNetworks = new Dictionary<Color, ValueList<Vector2>>();
foreach (var chunkedLine in atmosPipeNetwork)
{
var start = ScalePosition(chunkedLine.Origin - offset);
var end = ScalePosition(chunkedLine.Terminus - offset);
if (!pipeNetworks.TryGetValue(chunkedLine.Color, out var subNetwork))
subNetwork = new ValueList<Vector2>();
subNetwork.Add(start);
subNetwork.Add(end);
pipeNetworks[chunkedLine.Color] = subNetwork;
}
foreach ((var color, var subNetwork) in pipeNetworks)
{
if (subNetwork.Count > 0)
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, subNetwork.Span, color);
}
}
else
{
var pipeVertexUVs = new Dictionary<Color, ValueList<Vector2>>();
foreach (var chunkedLine in atmosPipeNetwork)
{
var leftTop = ScalePosition(new Vector2
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
- offset);
var rightTop = ScalePosition(new Vector2
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
- offset);
var leftBottom = ScalePosition(new Vector2
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
- offset);
var rightBottom = ScalePosition(new Vector2
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
- offset);
if (!pipeVertexUVs.TryGetValue(chunkedLine.Color, out var pipeVertexUV))
pipeVertexUV = new ValueList<Vector2>();
pipeVertexUV.Add(leftBottom);
pipeVertexUV.Add(leftTop);
pipeVertexUV.Add(rightBottom);
pipeVertexUV.Add(leftTop);
pipeVertexUV.Add(rightBottom);
pipeVertexUV.Add(rightTop);
pipeVertexUVs[chunkedLine.Color] = pipeVertexUV;
}
foreach ((var color, var pipeVertexUV) in pipeVertexUVs)
{
if (pipeVertexUV.Count > 0)
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, pipeVertexUV.Span, color);
}
}
}
private List<AtmosMonitoringConsoleLine> GetDecodedAtmosPipeChunks(Dictionary<Vector2i, AtmosPipeChunk>? chunks, MapGridComponent? grid)
{
var decodedOutput = new List<AtmosMonitoringConsoleLine>();
if (chunks == null || grid == null)
return decodedOutput;
// Clear stale look up table values
_horizLines.Clear();
_horizLinesReversed.Clear();
_vertLines.Clear();
_vertLinesReversed.Clear();
// Generate masks
var northMask = (ulong)1 << 0;
var southMask = (ulong)1 << 1;
var westMask = (ulong)1 << 2;
var eastMask = (ulong)1 << 3;
foreach ((var chunkOrigin, var chunk) in chunks)
{
var list = new List<AtmosMonitoringConsoleLine>();
foreach (var ((netId, hexColor), atmosPipeData) in chunk.AtmosPipeData)
{
// Determine the correct coloration for the pipe
var color = Color.FromHex(hexColor) * _basePipeNetColor;
if (FocusNetId != null && FocusNetId != netId)
color *= _unfocusedPipeNetColor;
// Get the associated line look up tables
if (!_horizLines.TryGetValue(color, out var horizLines))
{
horizLines = new();
_horizLines[color] = horizLines;
}
if (!_horizLinesReversed.TryGetValue(color, out var horizLinesReversed))
{
horizLinesReversed = new();
_horizLinesReversed[color] = horizLinesReversed;
}
if (!_vertLines.TryGetValue(color, out var vertLines))
{
vertLines = new();
_vertLines[color] = vertLines;
}
if (!_vertLinesReversed.TryGetValue(color, out var vertLinesReversed))
{
vertLinesReversed = new();
_vertLinesReversed[color] = vertLinesReversed;
}
// Loop over the chunk
for (var tileIdx = 0; tileIdx < ChunkSize * ChunkSize; tileIdx++)
{
if (atmosPipeData == 0)
continue;
var mask = (ulong)SharedNavMapSystem.AllDirMask << tileIdx * SharedNavMapSystem.Directions;
if ((atmosPipeData & mask) == 0)
continue;
var relativeTile = GetTileFromIndex(tileIdx);
var tile = (chunk.Origin * ChunkSize + relativeTile) * grid.TileSize;
tile = tile with { Y = -tile.Y };
// Calculate the draw point offsets
var vertLineOrigin = (atmosPipeData & northMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 1f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
var vertLineTerminus = (atmosPipeData & southMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
var horizLineOrigin = (atmosPipeData & eastMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
new Vector2(grid.TileSize * 1f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
var horizLineTerminus = (atmosPipeData & westMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
new Vector2(grid.TileSize * 0f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
// Since we can have pipe lines that have a length of a half tile,
// double the vectors and convert to vector2i so we can merge them
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, 2), ConvertVector2ToVector2i(tile + horizLineTerminus, 2), horizLines, horizLinesReversed);
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, 2), ConvertVector2ToVector2i(tile + vertLineTerminus, 2), vertLines, vertLinesReversed);
}
}
}
// Scale the vector2is back down and convert to vector2
foreach (var (color, horizLines) in _horizLines)
{
// Get the corresponding sRBG color
var sRGB = GetsRGBColor(color);
foreach (var (origin, terminal) in horizLines)
decodedOutput.Add(new AtmosMonitoringConsoleLine
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
}
foreach (var (color, vertLines) in _vertLines)
{
// Get the corresponding sRBG color
var sRGB = GetsRGBColor(color);
foreach (var (origin, terminal) in vertLines)
decodedOutput.Add(new AtmosMonitoringConsoleLine
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
}
return decodedOutput;
}
private Vector2 ConvertVector2iToVector2(Vector2i vector, float scale = 1f)
{
return new Vector2(vector.X * scale, vector.Y * scale);
}
private Vector2i ConvertVector2ToVector2i(Vector2 vector, float scale = 1f)
{
return new Vector2i((int)MathF.Round(vector.X * scale), (int)MathF.Round(vector.Y * scale));
}
private Vector2i GetTileFromIndex(int index)
{
var x = index / ChunkSize;
var y = index % ChunkSize;
return new Vector2i(x, y);
}
private Color GetsRGBColor(Color color)
{
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
{
sRGB = Color.ToSrgb(color);
_sRGBLookUp[color] = sRGB;
}
return sRGB;
}
}
public struct AtmosMonitoringConsoleLine
{
public readonly Vector2 Origin;
public readonly Vector2 Terminus;
public readonly Color Color;
public AtmosMonitoringConsoleLine(Vector2 origin, Vector2 terminus, Color color)
{
Origin = origin;
Terminus = terminus;
Color = color;
}
}

View File

@@ -1,69 +0,0 @@
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Consoles;
using Robust.Shared.GameStates;
namespace Content.Client.Atmos.Consoles;
public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, ComponentHandleState>(OnHandleState);
}
private void OnHandleState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentHandleState args)
{
Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks;
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices;
switch (args.Current)
{
case AtmosMonitoringConsoleDeltaState delta:
{
modifiedChunks = delta.ModifiedChunks;
atmosDevices = delta.AtmosDevices;
foreach (var index in component.AtmosPipeChunks.Keys)
{
if (!delta.AllChunks!.Contains(index))
component.AtmosPipeChunks.Remove(index);
}
break;
}
case AtmosMonitoringConsoleState state:
{
modifiedChunks = state.Chunks;
atmosDevices = state.AtmosDevices;
foreach (var index in component.AtmosPipeChunks.Keys)
{
if (!state.Chunks.ContainsKey(index))
component.AtmosPipeChunks.Remove(index);
}
break;
}
default:
return;
}
foreach (var (origin, chunk) in modifiedChunks)
{
var newChunk = new AtmosPipeChunk(origin);
newChunk.AtmosPipeData = new Dictionary<(int, string), ulong>(chunk);
component.AtmosPipeChunks[origin] = newChunk;
}
component.AtmosDevices.Clear();
foreach (var (nuid, atmosDevice) in atmosDevices)
{
component.AtmosDevices[nuid] = atmosDevice;
}
}
}

View File

@@ -1,99 +0,0 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Atmos.Consoles"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'atmos-monitoring-window-title'}"
Resizable="False"
SetSize="1120 750"
MinSize="1120 750">
<BoxContainer Orientation="Vertical">
<!-- Main display -->
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
<!-- Nav map -->
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<ui:AtmosMonitoringConsoleNavMapControl Name="NavMap" Margin="5 5" VerticalExpand="True" HorizontalExpand="True">
<!-- System warning -->
<PanelContainer Name="SystemWarningPanel"
HorizontalAlignment="Center"
VerticalAlignment="Top"
HorizontalExpand="True"
Margin="0 48 0 0"
Visible="False">
<RichTextLabel Name="SystemWarningLabel" Margin="12 8 12 8"/>
</PanelContainer>
</ui:AtmosMonitoringConsoleNavMapControl>
<!-- Nav map legend -->
<BoxContainer Orientation="Horizontal" Margin="0 10 0 10">
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_square.png"
Modulate="#a9a9a9"
SetSize="16 16"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-monitoring-window-label-gas-opening'}"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
SetSize="16 16"
Modulate="#a9a9a9"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-monitoring-window-label-gas-scrubber'}"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_arrow_east.png"
SetSize="16 16"
Modulate="#a9a9a9"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-monitoring-window-label-gas-flow-regulator'}"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_hexagon.png"
SetSize="16 16"
Modulate="#a9a9a9"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-monitoring-window-label-thermoregulator'}"/>
</BoxContainer>
</BoxContainer>
<!-- Atmosphere status -->
<BoxContainer Orientation="Vertical" VerticalExpand="True" SetWidth="440" Margin="0 0 10 10">
<!-- Station name -->
<controls:StripeBack>
<PanelContainer>
<RichTextLabel Name="StationName" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 5 0 3"/>
</PanelContainer>
</controls:StripeBack>
<!-- Alarm status (entries added by C# code) -->
<TabContainer Name="MasterTabContainer" VerticalExpand="True" HorizontalExpand="True" Margin="0 10 0 0">
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
<BoxContainer Name="AtmosNetworksTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
</ScrollContainer>
</TabContainer>
<!-- Overlay toggles -->
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
<Label Text="{Loc 'atmos-monitoring-window-toggle-overlays'}" Margin="0 0 0 5"/>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<CheckBox Name="ShowPipeNetwork" Text="{Loc 'atmos-monitoring-window-show-pipe-network'}" Pressed="True" HorizontalExpand="True"/>
<CheckBox Name="ShowGasPipeSensors" Text="{Loc 'atmos-monitoring-window-show-gas-pipe-sensors'}" Pressed="False" HorizontalExpand="True"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Footer -->
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc 'atmos-monitoring-window-flavor-left'}" StyleClasses="WindowFooterText" />
<Label Text="{Loc 'atmos-monitoring-window-flavor-right'}" StyleClasses="WindowFooterText"
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -1,455 +0,0 @@
using Content.Client.Pinpointer.UI;
using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos.Components;
using Content.Shared.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Client.Atmos.Consoles;
[GenerateTypedNameReferences]
public sealed partial class AtmosMonitoringConsoleWindow : FancyWindow
{
private readonly IEntityManager _entManager;
private readonly IPrototypeManager _protoManager;
private readonly SpriteSystem _spriteSystem;
private EntityUid? _owner;
private NetEntity? _focusEntity;
private int? _focusNetId;
private bool _autoScrollActive = false;
private readonly Color _unfocusedDeviceColor = Color.DimGray;
private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole";
private ProtoId<NavMapBlipPrototype> _gasPipeSensorProtoId = "GasPipeSensor";
public AtmosMonitoringConsoleWindow(AtmosMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_protoManager = IoCManager.Resolve<IPrototypeManager>();
_spriteSystem = _entManager.System<SpriteSystem>();
// Pass the owner to nav map
_owner = owner;
NavMap.Owner = _owner;
// Set nav map grid uid
var stationName = Loc.GetString("atmos-monitoring-window-unknown-location");
EntityCoordinates? consoleCoords = null;
if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
{
consoleCoords = xform.Coordinates;
NavMap.MapUid = xform.GridUid;
// Assign station name
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
stationName = stationMetaData.EntityName;
var msg = new FormattedMessage();
msg.TryAddMarkup(Loc.GetString("atmos-monitoring-window-station-name", ("stationName", stationName)), out _);
StationName.SetMessage(msg);
}
else
{
StationName.SetMessage(stationName);
NavMap.Visible = false;
}
// Set trackable entity selected action
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
// Update nav map
NavMap.ForceNavMapUpdate();
// Set tab container headers
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-monitoring-window-tab-networks"));
// Set UI toggles
ShowPipeNetwork.OnToggled += _ => OnShowPipeNetworkToggled();
ShowGasPipeSensors.OnToggled += _ => OnShowGasPipeSensors();
// Set nav map colors
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
return;
NavMap.TileColor = console.NavMapTileColor;
NavMap.WallColor = console.NavMapWallColor;
// Initalize
UpdateUI(consoleCoords, Array.Empty<AtmosMonitoringConsoleEntry>());
}
#region Toggle handling
private void OnShowPipeNetworkToggled()
{
if (_owner == null)
return;
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
return;
NavMap.ShowPipeNetwork = ShowPipeNetwork.Pressed;
foreach (var (netEnt, device) in console.AtmosDevices)
{
if (device.NavMapBlip == _gasPipeSensorProtoId)
continue;
if (ShowPipeNetwork.Pressed)
AddTrackedEntityToNavMap(device);
else
NavMap.TrackedEntities.Remove(netEnt);
}
}
private void OnShowGasPipeSensors()
{
if (_owner == null)
return;
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
return;
foreach (var (netEnt, device) in console.AtmosDevices)
{
if (device.NavMapBlip != _gasPipeSensorProtoId)
continue;
if (ShowGasPipeSensors.Pressed)
AddTrackedEntityToNavMap(device, true);
else
NavMap.TrackedEntities.Remove(netEnt);
}
}
#endregion
public void UpdateUI
(EntityCoordinates? consoleCoords,
AtmosMonitoringConsoleEntry[] atmosNetworks)
{
if (_owner == null)
return;
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
return;
// Reset nav map values
NavMap.TrackedCoordinates.Clear();
NavMap.TrackedEntities.Clear();
if (_focusEntity != null && !console.AtmosDevices.Any(x => x.Key == _focusEntity))
ClearFocus();
// Add tracked entities to the nav map
UpdateNavMapBlips();
// Show the monitor location
var consoleNetEnt = _entManager.GetNetEntity(_owner);
if (consoleCoords != null && consoleNetEnt != null)
{
var proto = _protoManager.Index(_navMapConsoleProtoId);
if (proto.TexturePaths != null && proto.TexturePaths.Length != 0)
{
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(proto.TexturePaths[0]));
var blip = new NavMapBlip(consoleCoords.Value, texture, proto.Color, proto.Blinks, proto.Selectable);
NavMap.TrackedEntities[consoleNetEnt.Value] = blip;
}
}
// Update the nav map
NavMap.ForceNavMapUpdate();
// Clear excess children from the tables
while (AtmosNetworksTable.ChildCount > atmosNetworks.Length)
AtmosNetworksTable.RemoveChild(AtmosNetworksTable.GetChild(AtmosNetworksTable.ChildCount - 1));
// Update all entries in each table
for (int index = 0; index < atmosNetworks.Length; index++)
{
var entry = atmosNetworks.ElementAt(index);
UpdateUIEntry(entry, index, AtmosNetworksTable, console);
}
}
private void UpdateNavMapBlips()
{
if (_owner == null || !_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
return;
if (NavMap.Visible)
{
foreach (var (netEnt, device) in console.AtmosDevices)
{
// Update the focus network ID, incase it has changed
if (_focusEntity == netEnt)
{
_focusNetId = device.NetId;
NavMap.FocusNetId = _focusNetId;
}
var isSensor = device.NavMapBlip == _gasPipeSensorProtoId;
// Skip network devices if the toggled is off
if (!ShowPipeNetwork.Pressed && !isSensor)
continue;
// Skip gas pipe sensors if the toggle is off
if (!ShowGasPipeSensors.Pressed && isSensor)
continue;
AddTrackedEntityToNavMap(device, isSensor);
}
}
}
private void AddTrackedEntityToNavMap(AtmosDeviceNavMapData metaData, bool isSensor = false)
{
var proto = _protoManager.Index(metaData.NavMapBlip);
if (proto.TexturePaths == null || proto.TexturePaths.Length == 0)
return;
var idx = Math.Clamp((int)metaData.Direction / 2, 0, proto.TexturePaths.Length - 1);
var texture = proto.TexturePaths.Length > 0 ? proto.TexturePaths[idx] : proto.TexturePaths[0];
var color = isSensor ? proto.Color : proto.Color * metaData.PipeColor;
if (_focusNetId != null && metaData.NetId != _focusNetId)
color *= _unfocusedDeviceColor;
var blinks = proto.Blinks || _focusEntity == metaData.NetEntity;
var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(new SpriteSpecifier.Texture(texture)), color, blinks, proto.Selectable, proto.Scale);
NavMap.TrackedEntities[metaData.NetEntity] = blip;
}
private void UpdateUIEntry(AtmosMonitoringConsoleEntry data, int index, Control table, AtmosMonitoringConsoleComponent console)
{
// Make new UI entry if required
if (index >= table.ChildCount)
{
var newEntryContainer = new AtmosMonitoringEntryContainer(data);
// On click
newEntryContainer.FocusButton.OnButtonUp += args =>
{
if (_focusEntity == newEntryContainer.Data.NetEntity)
{
ClearFocus();
}
else
{
SetFocus(newEntryContainer.Data.NetEntity, newEntryContainer.Data.NetId);
var coords = _entManager.GetCoordinates(newEntryContainer.Data.Coordinates);
NavMap.CenterToCoordinates(coords);
}
// Update affected UI elements across all tables
UpdateConsoleTable(console, AtmosNetworksTable, _focusEntity);
};
// Add the entry to the current table
table.AddChild(newEntryContainer);
}
// Update values and UI elements
var tableChild = table.GetChild(index);
if (tableChild is not AtmosMonitoringEntryContainer)
{
table.RemoveChild(tableChild);
UpdateUIEntry(data, index, table, console);
return;
}
var entryContainer = (AtmosMonitoringEntryContainer)tableChild;
entryContainer.UpdateEntry(data, data.NetEntity == _focusEntity);
}
private void UpdateConsoleTable(AtmosMonitoringConsoleComponent console, Control table, NetEntity? currTrackedEntity)
{
foreach (var tableChild in table.Children)
{
if (tableChild is not AtmosAlarmEntryContainer)
continue;
var entryContainer = (AtmosAlarmEntryContainer)tableChild;
if (entryContainer.NetEntity != currTrackedEntity)
entryContainer.RemoveAsFocus();
else if (entryContainer.NetEntity == currTrackedEntity)
entryContainer.SetAsFocus();
}
}
private void SetTrackedEntityFromNavMap(NetEntity? focusEntity)
{
if (focusEntity == null)
return;
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
return;
foreach (var (netEnt, device) in console.AtmosDevices)
{
if (netEnt != focusEntity)
continue;
if (device.NavMapBlip != _gasPipeSensorProtoId)
return;
// Set new focus
SetFocus(focusEntity.Value, device.NetId);
// Get the scroll position of the selected entity on the selected button the UI
ActivateAutoScrollToFocus();
break;
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
AutoScrollToFocus();
}
private void ActivateAutoScrollToFocus()
{
_autoScrollActive = true;
}
private void AutoScrollToFocus()
{
if (!_autoScrollActive)
return;
var scroll = AtmosNetworksTable.Parent as ScrollContainer;
if (scroll == null)
return;
if (!TryGetVerticalScrollbar(scroll, out var vScrollbar))
return;
if (!TryGetNextScrollPosition(out float? nextScrollPosition))
return;
vScrollbar.ValueTarget = nextScrollPosition.Value;
if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget))
_autoScrollActive = false;
}
private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar)
{
vScrollBar = null;
foreach (var control in scroll.Children)
{
if (control is not VScrollBar)
continue;
vScrollBar = (VScrollBar)control;
return true;
}
return false;
}
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
{
nextScrollPosition = null;
var scroll = AtmosNetworksTable.Parent as ScrollContainer;
if (scroll == null)
return false;
var container = scroll.Children.ElementAt(0) as BoxContainer;
if (container == null || container.Children.Count() == 0)
return false;
// Exit if the heights of the children haven't been initialized yet
if (!container.Children.Any(x => x.Height > 0))
return false;
nextScrollPosition = 0;
foreach (var control in container.Children)
{
if (control is not AtmosMonitoringEntryContainer)
continue;
var entry = (AtmosMonitoringEntryContainer)control;
if (entry.Data.NetEntity == _focusEntity)
return true;
nextScrollPosition += control.Height;
}
// Failed to find control
nextScrollPosition = null;
return false;
}
private void SetFocus(NetEntity focusEntity, int focusNetId)
{
_focusEntity = focusEntity;
_focusNetId = focusNetId;
NavMap.FocusNetId = focusNetId;
OnFocusChanged();
}
private void ClearFocus()
{
_focusEntity = null;
_focusNetId = null;
NavMap.FocusNetId = null;
OnFocusChanged();
}
private void OnFocusChanged()
{
UpdateNavMapBlips();
NavMap.ForceNavMapUpdate();
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
return;
for (int index = 0; index < AtmosNetworksTable.ChildCount; index++)
{
var entry = (AtmosMonitoringEntryContainer)AtmosNetworksTable.GetChild(index);
if (entry == null)
continue;
UpdateUIEntry(entry.Data, index, AtmosNetworksTable, console);
}
}
}

View File

@@ -1,74 +0,0 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:Content.Client.Stylesheets"
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">
<!-- Network selection button -->
<Button Name="FocusButton" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 6 8" StyleClasses="OpenLeft" Access="Public">
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal" SetHeight="32">
<PanelContainer Name="NetworkColorStripe" HorizontalAlignment="Left" SetWidth="8" VerticalExpand="True" Margin="-8 -2 0 0">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#d7d7d7"/>
</PanelContainer.PanelOverride>
</PanelContainer>
<Label Name="NetworkNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Center"/>
</BoxContainer>
<!-- Panel that appears on selecting the device -->
<PanelContainer HorizontalExpand="True" Margin="-8 0 -14 -4" Access="Public">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
</PanelContainer.PanelOverride>
<BoxContainer Name="MainDataContainer" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
<Control>
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
<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="TotalMolHeaderLabel" Text="{Loc 'atmos-alerts-window-total-mol-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="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
<Label Name="PressureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
<Label Name="TotalMolLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
</BoxContainer>
</PanelContainer>
<BoxContainer 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="GasesHeaderLabel" Text="{Loc 'atmos-monitoring-window-label-gases'}" HorizontalAlignment="Left" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="4 0 0 0" SetHeight="24"></Label>
</BoxContainer>
</BoxContainer>
</Control>
<!-- Atmosphere status -->
<Control Name="FocusContainer" ReservesSpace="False" Visible="False">
<!-- Main container for displaying atmospheric data -->
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
<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>
</Control>
</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>
</PanelContainer>
</BoxContainer>
</Button>
</BoxContainer>

View File

@@ -1,166 +0,0 @@
using Content.Client.Stylesheets;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
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 System.Linq;
namespace Content.Client.Atmos.Consoles;
[GenerateTypedNameReferences]
public sealed partial class AtmosMonitoringEntryContainer : BoxContainer
{
public AtmosMonitoringConsoleEntry Data;
private readonly IEntityManager _entManager;
private readonly IResourceCache _cache;
public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_cache = IoCManager.Resolve<IResourceCache>();
Data = data;
// Modulate colored stripe
NetworkColorStripe.Modulate = data.Color;
// 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);
// Set fonts
TemperatureHeaderLabel.FontOverride = headerFont;
PressureHeaderLabel.FontOverride = headerFont;
TotalMolHeaderLabel.FontOverride = headerFont;
GasesHeaderLabel.FontOverride = headerFont;
TemperatureLabel.FontOverride = normalFont;
PressureLabel.FontOverride = normalFont;
TotalMolLabel.FontOverride = normalFont;
NoDataLabel.FontOverride = headerFont;
}
public void UpdateEntry(AtmosMonitoringConsoleEntry updatedData, bool isFocus)
{
// Load fonts
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
// Update name and values
if (!string.IsNullOrEmpty(updatedData.Address))
NetworkNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", updatedData.EntityName), ("address", updatedData.Address));
else
NetworkNameLabel.Text = Loc.GetString(updatedData.EntityName);
Data = updatedData;
// Modulate colored stripe
NetworkColorStripe.Modulate = Data.Color;
// Focus updates
if (isFocus)
SetAsFocus();
else
RemoveAsFocus();
// Check if powered
if (!updatedData.IsPowered)
{
MainDataContainer.Visible = false;
NoDataLabel.Visible = true;
return;
}
// Set container visibility
MainDataContainer.Visible = true;
NoDataLabel.Visible = false;
// Update temperature
var isNotVacuum = updatedData.TotalMolData > 1e-6f;
var tempK = (FixedPoint2)updatedData.TemperatureData;
var tempC = (FixedPoint2)TemperatureHelpers.KelvinToCelsius(tempK.Float());
TemperatureLabel.Text = isNotVacuum ?
Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK)) :
Loc.GetString("atmos-alerts-window-invalid-value");
TemperatureLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
// Update pressure
PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2)updatedData.PressureData));
PressureLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
// Update total mol
TotalMolLabel.Text = Loc.GetString("atmos-alerts-window-total-mol-value", ("value", (FixedPoint2)updatedData.TotalMolData));
TotalMolLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
// Update other present gases
GasGridContainer.RemoveAllChildren();
if (updatedData.GasData.Count() == 0)
{
// No 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, percent) in updatedData.GasData)
{
var gasPercent = (FixedPoint2)0f;
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,
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";
FocusContainer.Visible = true;
}
public void RemoveAsFocus()
{
FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png";
FocusContainer.Visible = false;
}
}

View File

@@ -4,6 +4,8 @@ using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
namespace Content.Client.Atmos.EntitySystems; namespace Content.Client.Atmos.EntitySystems;
@@ -17,7 +19,7 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<PipeAppearanceComponent, ComponentInit>(OnInit); SubscribeLocalEvent<PipeAppearanceComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<PipeAppearanceComponent, AppearanceChangeEvent>(OnAppearanceChanged, after: [typeof(SubFloorHideSystem)]); SubscribeLocalEvent<PipeAppearanceComponent, AppearanceChangeEvent>(OnAppearanceChanged, after: new[] { typeof(SubFloorHideSystem) });
} }
private void OnInit(EntityUid uid, PipeAppearanceComponent component, ComponentInit args) private void OnInit(EntityUid uid, PipeAppearanceComponent component, ComponentInit args)
@@ -82,8 +84,7 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
layer.Visible &= visible; layer.Visible &= visible;
if (!visible) if (!visible) continue;
continue;
layer.Color = color; layer.Color = color;
} }

View File

@@ -1,23 +0,0 @@
using Content.Client.Atmos.UI;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Piping.Binary.Components;
namespace Content.Client.Atmos.EntitySystems;
public sealed class GasPressurePumpSystem : SharedGasPressurePumpSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasPressurePumpComponent, AfterAutoHandleStateEvent>(OnPumpUpdate);
}
private void OnPumpUpdate(Entity<GasPressurePumpComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (UserInterfaceSystem.TryGetOpenUi<GasPressurePumpBoundUserInterface>(ent.Owner, GasPressurePumpUiKey.Key, out var bui))
{
bui.Update();
}
}
}

View File

@@ -1,7 +1,12 @@
using System.Collections.Generic;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Power; using Content.Shared.Power;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.Atmos.Monitor; namespace Content.Client.Atmos.Monitor;
@@ -22,7 +27,7 @@ public sealed class AtmosAlarmableVisualsSystem : VisualizerSystem<AtmosAlarmabl
{ {
foreach (var visLayer in component.HideOnDepowered) foreach (var visLayer in component.HideOnDepowered)
{ {
if (args.Sprite.LayerMapTryGet(visLayer, out var powerVisibilityLayer)) if (args.Sprite.LayerMapTryGet(visLayer, out int powerVisibilityLayer))
args.Sprite.LayerSetVisible(powerVisibilityLayer, powered); args.Sprite.LayerSetVisible(powerVisibilityLayer, powered);
} }
} }
@@ -31,7 +36,7 @@ public sealed class AtmosAlarmableVisualsSystem : VisualizerSystem<AtmosAlarmabl
{ {
foreach (var (setLayer, powerState) in component.SetOnDepowered) foreach (var (setLayer, powerState) in component.SetOnDepowered)
{ {
if (args.Sprite.LayerMapTryGet(setLayer, out var setStateLayer)) if (args.Sprite.LayerMapTryGet(setLayer, out int setStateLayer))
args.Sprite.LayerSetState(setStateLayer, new RSI.StateId(powerState)); args.Sprite.LayerSetState(setStateLayer, new RSI.StateId(powerState));
} }
} }

View File

@@ -1,7 +1,11 @@
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Monitor.Components;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Client.Atmos.Monitor.UI; namespace Content.Client.Atmos.Monitor.UI;
@@ -74,7 +78,6 @@ public sealed class AirAlarmBoundUserInterface : BoundUserInterface
{ {
base.Dispose(disposing); base.Dispose(disposing);
if (disposing) if (disposing) _window?.Dispose();
_window?.Dispose();
} }
} }

View File

@@ -8,6 +8,7 @@ using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
@@ -58,7 +59,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
AirAlarmMode.Fill => "air-alarm-ui-mode-fill", AirAlarmMode.Fill => "air-alarm-ui-mode-fill",
AirAlarmMode.Panic => "air-alarm-ui-mode-panic", AirAlarmMode.Panic => "air-alarm-ui-mode-panic",
AirAlarmMode.None => "air-alarm-ui-mode-none", AirAlarmMode.None => "air-alarm-ui-mode-none",
_ => "error", _ => "error"
}; };
_modes.AddItem(Loc.GetString(text)); _modes.AddItem(Loc.GetString(text));
} }
@@ -69,7 +70,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
AirAlarmModeChanged!.Invoke((AirAlarmMode) args.Id); AirAlarmModeChanged!.Invoke((AirAlarmMode) args.Id);
}; };
_autoMode.OnToggled += _ => _autoMode.OnToggled += args =>
{ {
AutoModeChanged!.Invoke(_autoMode.Pressed); AutoModeChanged!.Invoke(_autoMode.Pressed);
}; };
@@ -130,8 +131,8 @@ public sealed partial class AirAlarmWindow : FancyWindow
if (!_pumps.TryGetValue(addr, out var pumpControl)) if (!_pumps.TryGetValue(addr, out var pumpControl))
{ {
var control= new PumpControl(pump, addr); var control= new PumpControl(pump, addr);
control.PumpDataChanged += AtmosDeviceDataChanged; control.PumpDataChanged += AtmosDeviceDataChanged!.Invoke;
control.PumpDataCopied += AtmosDeviceDataCopied; control.PumpDataCopied += AtmosDeviceDataCopied!.Invoke;
_pumps.Add(addr, control); _pumps.Add(addr, control);
CVentContainer.AddChild(control); CVentContainer.AddChild(control);
} }
@@ -145,8 +146,8 @@ public sealed partial class AirAlarmWindow : FancyWindow
if (!_scrubbers.TryGetValue(addr, out var scrubberControl)) if (!_scrubbers.TryGetValue(addr, out var scrubberControl))
{ {
var control = new ScrubberControl(scrubber, addr); var control = new ScrubberControl(scrubber, addr);
control.ScrubberDataChanged += AtmosDeviceDataChanged; control.ScrubberDataChanged += AtmosDeviceDataChanged!.Invoke;
control.ScrubberDataCopied += AtmosDeviceDataCopied; control.ScrubberDataCopied += AtmosDeviceDataCopied!.Invoke;
_scrubbers.Add(addr, control); _scrubbers.Add(addr, control);
CScrubberContainer.AddChild(control); CScrubberContainer.AddChild(control);
} }
@@ -161,7 +162,6 @@ public sealed partial class AirAlarmWindow : FancyWindow
{ {
var control = new SensorInfo(sensor, addr); var control = new SensorInfo(sensor, addr);
control.OnThresholdUpdate += AtmosAlarmThresholdChanged; control.OnThresholdUpdate += AtmosAlarmThresholdChanged;
control.SensorDataCopied += AtmosDeviceDataCopied;
_sensors.Add(addr, control); _sensors.Add(addr, control);
CSensorContainer.AddChild(control); CSensorContainer.AddChild(control);
} }
@@ -176,18 +176,22 @@ public sealed partial class AirAlarmWindow : FancyWindow
public static Color ColorForThreshold(float amount, AtmosAlarmThreshold threshold) public static Color ColorForThreshold(float amount, AtmosAlarmThreshold threshold)
{ {
threshold.CheckThreshold(amount, out var curAlarm); threshold.CheckThreshold(amount, out AtmosAlarmType curAlarm);
return ColorForAlarm(curAlarm); return ColorForAlarm(curAlarm);
} }
public static Color ColorForAlarm(AtmosAlarmType curAlarm) public static Color ColorForAlarm(AtmosAlarmType curAlarm)
{ {
return curAlarm switch if(curAlarm == AtmosAlarmType.Danger)
{ {
AtmosAlarmType.Danger => StyleNano.DangerousRedFore, return StyleNano.DangerousRedFore;
AtmosAlarmType.Warning => StyleNano.ConcerningOrangeFore, }
_ => StyleNano.GoodGreenFore, else if(curAlarm == AtmosAlarmType.Warning)
}; {
return StyleNano.ConcerningOrangeFore;
}
return StyleNano.GoodGreenFore;
} }

View File

@@ -1,8 +1,12 @@
using System;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
namespace Content.Client.Atmos.Monitor.UI.Widgets; namespace Content.Client.Atmos.Monitor.UI.Widgets;
@@ -21,7 +25,7 @@ public sealed partial class PumpControl : BoxContainer
private OptionButton _pressureCheck => CPressureCheck; private OptionButton _pressureCheck => CPressureCheck;
private FloatSpinBox _externalBound => CExternalBound; private FloatSpinBox _externalBound => CExternalBound;
private FloatSpinBox _internalBound => CInternalBound; private FloatSpinBox _internalBound => CInternalBound;
private Button _copySettings => CCopySettings; private Button _copySettings => CCopySettings;
public PumpControl(GasVentPumpData data, string address) public PumpControl(GasVentPumpData data, string address)
{ {
@@ -82,11 +86,11 @@ public sealed partial class PumpControl : BoxContainer
_data.PressureChecks = (VentPressureBound) args.Id; _data.PressureChecks = (VentPressureBound) args.Id;
PumpDataChanged?.Invoke(_address, _data); PumpDataChanged?.Invoke(_address, _data);
}; };
_copySettings.OnPressed += _ => _copySettings.OnPressed += _ =>
{ {
PumpDataCopied?.Invoke(_data); PumpDataCopied?.Invoke(_data);
}; };
} }
public void ChangeData(GasVentPumpData data) public void ChangeData(GasVentPumpData data)

View File

@@ -1,9 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
namespace Content.Client.Atmos.Monitor.UI.Widgets; namespace Content.Client.Atmos.Monitor.UI.Widgets;
@@ -21,7 +27,7 @@ public sealed partial class ScrubberControl : BoxContainer
private OptionButton _pumpDirection => CPumpDirection; private OptionButton _pumpDirection => CPumpDirection;
private FloatSpinBox _volumeRate => CVolumeRate; private FloatSpinBox _volumeRate => CVolumeRate;
private CheckBox _wideNet => CWideNet; private CheckBox _wideNet => CWideNet;
private Button _copySettings => CCopySettings; private Button _copySettings => CCopySettings;
private GridContainer _gases => CGasContainer; private GridContainer _gases => CGasContainer;
private Dictionary<Gas, Button> _gasControls = new(); private Dictionary<Gas, Button> _gasControls = new();
@@ -71,11 +77,11 @@ public sealed partial class ScrubberControl : BoxContainer
_data.PumpDirection = (ScrubberPumpDirection) args.Id; _data.PumpDirection = (ScrubberPumpDirection) args.Id;
ScrubberDataChanged?.Invoke(_address, _data); ScrubberDataChanged?.Invoke(_address, _data);
}; };
_copySettings.OnPressed += _ => _copySettings.OnPressed += _ =>
{ {
ScrubberDataCopied?.Invoke(_data); ScrubberDataCopied?.Invoke(_data);
}; };
foreach (var value in Enum.GetValues<Gas>()) foreach (var value in Enum.GetValues<Gas>())
{ {

View File

@@ -3,9 +3,6 @@
<CollapsibleHeading Name="SensorAddress" /> <CollapsibleHeading Name="SensorAddress" />
<CollapsibleBody Margin="20 2 2 2"> <CollapsibleBody Margin="20 2 2 2">
<BoxContainer Orientation="Vertical" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" HorizontalExpand="True">
<BoxContainer Orientation="Horizontal" Margin ="0 0 0 2">
<Button Name="CCopySettings" Text="{Loc 'air-alarm-ui-thresholds-copy'}" ToolTip="{Loc 'air-alarm-ui-thresholds-copy-tooltip'}" />
</BoxContainer>
<BoxContainer Orientation="Vertical" Margin="0 0 2 0" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" Margin="0 0 2 0" HorizontalExpand="True">
<RichTextLabel Name="AlarmStateLabel" /> <RichTextLabel Name="AlarmStateLabel" />
<RichTextLabel Name="PressureLabel" /> <RichTextLabel Name="PressureLabel" />

View File

@@ -12,14 +12,12 @@ namespace Content.Client.Atmos.Monitor.UI.Widgets;
public sealed partial class SensorInfo : BoxContainer public sealed partial class SensorInfo : BoxContainer
{ {
public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate; public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate;
public event Action<AtmosSensorData>? SensorDataCopied;
private string _address; private string _address;
private ThresholdControl _pressureThreshold; private ThresholdControl _pressureThreshold;
private ThresholdControl _temperatureThreshold; private ThresholdControl _temperatureThreshold;
private Dictionary<Gas, ThresholdControl> _gasThresholds = new(); private Dictionary<Gas, ThresholdControl> _gasThresholds = new();
private Dictionary<Gas, RichTextLabel> _gasLabels = new(); private Dictionary<Gas, RichTextLabel> _gasLabels = new();
private Button _copySettings => CCopySettings;
public SensorInfo(AtmosSensorData data, string address) public SensorInfo(AtmosSensorData data, string address)
{ {
@@ -45,8 +43,7 @@ public sealed partial class SensorInfo : BoxContainer
var label = new RichTextLabel(); var label = new RichTextLabel();
var fractionGas = amount / data.TotalMoles; var fractionGas = amount / data.TotalMoles;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", ("gas", $"{gas}"),
("gas", $"{gas}"),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])), ("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"), ("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}"))); ("percentage", $"{(100 * fractionGas):0.##}")));
@@ -56,9 +53,9 @@ public sealed partial class SensorInfo : BoxContainer
var threshold = data.GasThresholds[gas]; var threshold = data.GasThresholds[gas];
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100); var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
gasThresholdControl.Margin = new Thickness(20, 2, 2, 2); gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) => gasThresholdControl.ThresholdDataChanged += (type, threshold, arg3) =>
{ {
OnThresholdUpdate?.Invoke(_address, type, alarmThreshold, arg3); OnThresholdUpdate!(_address, type, threshold, arg3);
}; };
_gasThresholds.Add(gas, gasThresholdControl); _gasThresholds.Add(gas, gasThresholdControl);
@@ -67,24 +64,18 @@ public sealed partial class SensorInfo : BoxContainer
_pressureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure); _pressureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure);
PressureThresholdContainer.AddChild(_pressureThreshold); PressureThresholdContainer.AddChild(_pressureThreshold);
_temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"), _temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"), data.TemperatureThreshold,
data.TemperatureThreshold,
AtmosMonitorThresholdType.Temperature); AtmosMonitorThresholdType.Temperature);
TemperatureThresholdContainer.AddChild(_temperatureThreshold); TemperatureThresholdContainer.AddChild(_temperatureThreshold);
_pressureThreshold.ThresholdDataChanged += (type, threshold, arg3) => _pressureThreshold.ThresholdDataChanged += (type, threshold, arg3) =>
{ {
OnThresholdUpdate?.Invoke(_address, type, threshold, arg3); OnThresholdUpdate!(_address, type, threshold, arg3);
}; };
_temperatureThreshold.ThresholdDataChanged += (type, threshold, arg3) => _temperatureThreshold.ThresholdDataChanged += (type, threshold, arg3) =>
{ {
OnThresholdUpdate?.Invoke(_address, type, threshold, arg3); OnThresholdUpdate!(_address, type, threshold, arg3);
};
_copySettings.OnPressed += _ =>
{
SensorDataCopied?.Invoke(data);
}; };
} }
@@ -112,8 +103,7 @@ public sealed partial class SensorInfo : BoxContainer
} }
var fractionGas = amount / data.TotalMoles; var fractionGas = amount / data.TotalMoles;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", ("gas", $"{gas}"),
("gas", $"{gas}"),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])), ("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"), ("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}"))); ("percentage", $"{(100 * fractionGas):0.##}")));

View File

@@ -1,4 +1,7 @@
using Content.Client.Message;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Temperature;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;

View File

@@ -1,8 +1,12 @@
using System;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
// holy FUCK // holy FUCK
// this technically works because some of this you can *not* do in XAML but holy FUCK // this technically works because some of this you can *not* do in XAML but holy FUCK
@@ -111,38 +115,29 @@ public sealed partial class ThresholdControl : BoxContainer
_enabled.Pressed = !_threshold.Ignore; _enabled.Pressed = !_threshold.Ignore;
} }
private string LabelForBound(string boundType) //<todo.eoin Replace this with enums private String LabelForBound(string boundType) //<todo.eoin Replace this with enums
{ {
return Loc.GetString($"air-alarm-ui-thresholds-{boundType}"); return Loc.GetString($"air-alarm-ui-thresholds-{boundType}");
} }
public void UpdateThresholdData(AtmosAlarmThreshold threshold, float currentAmount) public void UpdateThresholdData(AtmosAlarmThreshold threshold, float currentAmount)
{ {
threshold.CheckThreshold(currentAmount, out var alarm, out var bound); threshold.CheckThreshold(currentAmount, out AtmosAlarmType alarm, out AtmosMonitorThresholdBound which);
var upperDangerState = AtmosAlarmType.Normal; var upperDangerState = AtmosAlarmType.Normal;
var lowerDangerState = AtmosAlarmType.Normal; var lowerDangerState = AtmosAlarmType.Normal;
var upperWarningState = AtmosAlarmType.Normal; var upperWarningState = AtmosAlarmType.Normal;
var lowerWarningState = AtmosAlarmType.Normal; var lowerWarningState = AtmosAlarmType.Normal;
switch (alarm) if(alarm == AtmosAlarmType.Danger)
{ {
case AtmosAlarmType.Danger: if(which == AtmosMonitorThresholdBound.Upper) upperDangerState = alarm;
{ else lowerDangerState = alarm;
if (bound == AtmosMonitorThresholdBound.Upper) }
upperDangerState = alarm; else if(alarm == AtmosAlarmType.Warning)
else {
lowerDangerState = alarm; if(which == AtmosMonitorThresholdBound.Upper) upperWarningState = alarm;
break; else lowerWarningState = alarm;
}
case AtmosAlarmType.Warning:
{
if (bound == AtmosMonitorThresholdBound.Upper)
upperWarningState = alarm;
else
lowerWarningState = alarm;
break;
}
} }
_upperBoundControl.SetValue(threshold.UpperBound.Value); _upperBoundControl.SetValue(threshold.UpperBound.Value);

View File

@@ -1,4 +1,3 @@
using System.Globalization;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.Atmos.EntitySystems; using Content.Client.Atmos.EntitySystems;
@@ -102,9 +101,14 @@ public sealed class AtmosDebugOverlay : Overlay
else else
{ {
// Red-Green-Blue interpolation // Red-Green-Blue interpolation
res = interp < 0.5f if (interp < 0.5f)
? Color.InterpolateBetween(Color.Red, Color.LimeGreen, interp * 2) {
: Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2); res = Color.InterpolateBetween(Color.Red, Color.LimeGreen, interp * 2);
}
else
{
res = Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2);
}
} }
res = res.WithAlpha(0.75f); res = res.WithAlpha(0.75f);
@@ -177,10 +181,7 @@ public sealed class AtmosDebugOverlay : Overlay
handle.DrawCircle(tileCentre, 0.05f, Color.Black); handle.DrawCircle(tileCentre, 0.05f, Color.Black);
} }
private void CheckAndShowBlockDir( private void CheckAndShowBlockDir(AtmosDebugOverlayData data, DrawingHandleWorld handle, AtmosDirection dir,
AtmosDebugOverlayData data,
DrawingHandleWorld handle,
AtmosDirection dir,
Vector2 tileCentre) Vector2 tileCentre)
{ {
if (!data.BlockDirection.HasFlag(dir)) if (!data.BlockDirection.HasFlag(dir))
@@ -242,7 +243,7 @@ public sealed class AtmosDebugOverlay : Overlay
var moles = data.Moles == null var moles = data.Moles == null
? "No Air" ? "No Air"
: data.Moles.Sum().ToString(CultureInfo.InvariantCulture); : data.Moles.Sum().ToString();
handle.DrawString(_font, pos, $"Moles: {moles}"); handle.DrawString(_font, pos, $"Moles: {moles}");
pos += offset; pos += offset;
@@ -262,12 +263,7 @@ public sealed class AtmosDebugOverlay : Overlay
private void GetGrids(MapId mapId, Box2Rotated box) private void GetGrids(MapId mapId, Box2Rotated box)
{ {
_grids.Clear(); _grids.Clear();
_mapManager.FindGridsIntersecting( _mapManager.FindGridsIntersecting(mapId, box, ref _grids, (EntityUid uid, MapGridComponent grid,
mapId,
box,
ref _grids,
(EntityUid uid,
MapGridComponent grid,
ref List<(Entity<MapGridComponent>, DebugMessage)> state) => ref List<(Entity<MapGridComponent>, DebugMessage)> state) =>
{ {
if (_system.TileData.TryGetValue(uid, out var data)) if (_system.TileData.TryGetValue(uid, out var data))

View File

@@ -1,63 +1,65 @@
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI; namespace Content.Client.Atmos.UI
/// <summary>
/// Initializes a <see cref="GasPressurePumpWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
{ {
[ViewVariables] /// <summary>
private const float MaxPressure = Atmospherics.MaxOutputPressure; /// Initializes a <see cref="GasPressurePumpWindow"/> and updates it when new server messages are received.
/// </summary>
[ViewVariables] [UsedImplicitly]
private GasPressurePumpWindow? _window; public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
public GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {
} [ViewVariables]
private const float MaxPressure = Atmospherics.MaxOutputPressure;
protected override void Open() [ViewVariables]
{ private GasPressurePumpWindow? _window;
base.Open();
_window = this.CreateWindow<GasPressurePumpWindow>(); public GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; protected override void Open()
_window.PumpOutputPressureChanged += OnPumpOutputPressurePressed; {
Update(); base.Open();
}
public void Update() _window = this.CreateWindow<GasPressurePumpWindow>();
{
if (_window == null)
return;
_window.Title = Identity.Name(Owner, EntMan); _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.PumpOutputPressureChanged += OnPumpOutputPressurePressed;
}
if (!EntMan.TryGetComponent(Owner, out GasPressurePumpComponent? pump)) private void OnToggleStatusButtonPressed()
return; {
if (_window is null) return;
SendMessage(new GasPressurePumpToggleStatusMessage(_window.PumpStatus));
}
_window.SetPumpStatus(pump.Enabled); private void OnPumpOutputPressurePressed(string value)
_window.MaxPressure = pump.MaxTargetPressure; {
_window.SetOutputPressure(pump.TargetPressure); var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
} if (pressure > MaxPressure) pressure = MaxPressure;
private void OnToggleStatusButtonPressed() SendMessage(new GasPressurePumpChangeOutputPressureMessage(pressure));
{ }
if (_window is null) return;
SendPredictedMessage(new GasPressurePumpToggleStatusMessage(_window.PumpStatus));
}
private void OnPumpOutputPressurePressed(float value) /// <summary>
{ /// Update the UI state based on server-sent info
SendPredictedMessage(new GasPressurePumpChangeOutputPressureMessage(value)); /// </summary>
/// <param name="state"></param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (_window == null || state is not GasPressurePumpBoundUserInterfaceState cast)
return;
_window.Title = (cast.PumpLabel);
_window.SetPumpStatus(cast.Enabled);
_window.SetOutputPressure(cast.OutputPressure);
}
} }
} }

View File

@@ -1,18 +1,22 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" MinSize="200 120" Title="Pressure Pump">
SetSize="340 110" MinSize="340 110" Title="Pressure Pump">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-pump-ui-pump-status}" Margin="0 0 5 0"/> <Label Text="{Loc comp-gas-pump-ui-pump-status}"/>
<Control MinSize="5 0" />
<Button Name="ToggleStatusButton"/> <Button Name="ToggleStatusButton"/>
<Control HorizontalExpand="True"/>
<Button HorizontalAlignment="Right" Name="SetOutputPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-rate}" Disabled="True" Margin="0 0 5 0"/>
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-pump-ui-pump-output-pressure}"/> <Label Text="{Loc comp-gas-pump-ui-pump-output-pressure}"/>
<FloatSpinBox HorizontalExpand="True" Name="PumpPressureOutputInput" MinSize="70 0" /> <Control MinSize="5 0" />
<LineEdit Name="PumpPressureOutputInput" MinSize="70 0" />
<Control MinSize="5 0" />
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
<Control MinSize="5 0" />
<Control HorizontalExpand="True" />
<Button Name="SetOutputPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-rate}" HorizontalAlignment="Right" Disabled="True"/>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
</controls:FancyWindow> </DefaultWindow>

View File

@@ -1,8 +1,14 @@
using Content.Client.UserInterface.Controls; using System;
using System.Collections.Generic;
using System.Globalization;
using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -10,25 +16,12 @@ namespace Content.Client.Atmos.UI
/// Client-side UI used to control a gas pressure pump. /// Client-side UI used to control a gas pressure pump.
/// </summary> /// </summary>
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasPressurePumpWindow : FancyWindow public sealed partial class GasPressurePumpWindow : DefaultWindow
{ {
public bool PumpStatus = true; public bool PumpStatus = true;
public event Action? ToggleStatusButtonPressed; public event Action? ToggleStatusButtonPressed;
public event Action<float>? PumpOutputPressureChanged; public event Action<string>? PumpOutputPressureChanged;
public float MaxPressure
{
get => _maxPressure;
set
{
_maxPressure = value;
PumpPressureOutputInput.Value = MathF.Min(value, PumpPressureOutputInput.Value);
}
}
private float _maxPressure = Atmospherics.MaxOutputPressure;
public GasPressurePumpWindow() public GasPressurePumpWindow()
{ {
@@ -37,25 +30,23 @@ namespace Content.Client.Atmos.UI
ToggleStatusButton.OnPressed += _ => SetPumpStatus(!PumpStatus); ToggleStatusButton.OnPressed += _ => SetPumpStatus(!PumpStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke(); ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
PumpPressureOutputInput.OnValueChanged += _ => SetOutputPressureButton.Disabled = false; PumpPressureOutputInput.OnTextChanged += _ => SetOutputPressureButton.Disabled = false;
SetOutputPressureButton.OnPressed += _ => SetOutputPressureButton.OnPressed += _ =>
{ {
PumpPressureOutputInput.Value = Math.Clamp(PumpPressureOutputInput.Value, 0f, _maxPressure); PumpOutputPressureChanged?.Invoke(PumpPressureOutputInput.Text ??= "");
PumpOutputPressureChanged?.Invoke(PumpPressureOutputInput.Value);
SetOutputPressureButton.Disabled = true; SetOutputPressureButton.Disabled = true;
}; };
SetMaxPressureButton.OnPressed += _ => SetMaxPressureButton.OnPressed += _ =>
{ {
PumpPressureOutputInput.Value = _maxPressure; PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
SetOutputPressureButton.Disabled = false; SetOutputPressureButton.Disabled = false;
}; };
} }
public void SetOutputPressure(float pressure) public void SetOutputPressure(float pressure)
{ {
PumpPressureOutputInput.Value = pressure; PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
} }
public void SetPumpStatus(bool enabled) public void SetPumpStatus(bool enabled)

View File

@@ -1,4 +1,3 @@
using Content.Shared.Cargo.Components;
using Content.Shared.Timing; using Content.Shared.Timing;
using Content.Shared.Cargo.Systems; using Content.Shared.Cargo.Systems;
@@ -11,9 +10,9 @@ public sealed class ClientPriceGunSystem : SharedPriceGunSystem
{ {
[Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!;
protected override bool GetPriceOrBounty(Entity<PriceGunComponent> entity, EntityUid target, EntityUid user) protected override bool GetPriceOrBounty(EntityUid priceGunUid, EntityUid target, EntityUid user)
{ {
if (!TryComp(entity, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((entity, useDelay))) if (!TryComp(priceGunUid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((priceGunUid, useDelay)))
return false; return false;
// It feels worse if the cooldown is predicted but the popup isn't! So only do the cooldown reset on the server. // It feels worse if the cooldown is predicted but the popup isn't! So only do the cooldown reset on the server.

View File

@@ -2,7 +2,6 @@ using System.Numerics;
using Content.Client.Chat.Managers; using Content.Client.Chat.Managers;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Speech;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
@@ -142,12 +141,7 @@ namespace Content.Client.Chat.UI
Modulate = Color.White; Modulate = Color.White;
} }
var baseOffset = 0f; var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -EntityVerticalOffset;
if (_entityManager.TryGetComponent<SpeechComponent>(_senderEntity, out var speech))
baseOffset = speech.SpeechBubbleOffset;
var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -(EntityVerticalOffset + baseOffset);
var worldPos = _transformSystem.GetWorldPosition(xform) + offset; var worldPos = _transformSystem.GetWorldPosition(xform) + offset;
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale; var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;

View File

@@ -12,7 +12,6 @@ using Robust.Shared.Utility;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Client.Graphics;
using static Robust.Client.UserInterface.Controls.BoxContainer; using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Content.Client.Chemistry.UI namespace Content.Client.Chemistry.UI
@@ -91,40 +90,10 @@ namespace Content.Client.Chemistry.UI
private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass) private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass)
{ {
var reagentTransferButton = new ReagentButton(text, amount, id, isBuffer, styleClass); var button = new ReagentButton(text, amount, id, isBuffer, styleClass);
reagentTransferButton.OnPressed += args button.OnPressed += args
=> OnReagentButtonPressed?.Invoke(args, reagentTransferButton); => OnReagentButtonPressed?.Invoke(args, button);
return reagentTransferButton; return button;
}
/// <summary>
/// Conditionally generates a set of reagent buttons based on the supplied boolean argument.
/// This was moved outside of BuildReagentRow to facilitate conditional logic, stops indentation depth getting out of hand as well.
/// </summary>
private List<ReagentButton> CreateReagentTransferButtons(ReagentId reagent, bool isBuffer, bool addReagentButtons)
{
if (!addReagentButtons)
return new List<ReagentButton>(); // Return an empty list if reagentTransferButton creation is disabled.
var buttonConfigs = new (string text, ChemMasterReagentAmount amount, string styleClass)[]
{
("1", ChemMasterReagentAmount.U1, StyleBase.ButtonOpenBoth),
("5", ChemMasterReagentAmount.U5, StyleBase.ButtonOpenBoth),
("10", ChemMasterReagentAmount.U10, StyleBase.ButtonOpenBoth),
("25", ChemMasterReagentAmount.U25, StyleBase.ButtonOpenBoth),
("50", ChemMasterReagentAmount.U50, StyleBase.ButtonOpenBoth),
("100", ChemMasterReagentAmount.U100, StyleBase.ButtonOpenBoth),
(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, StyleBase.ButtonOpenLeft),
};
var buttons = new List<ReagentButton>();
foreach (var (text, amount, styleClass) in buttonConfigs)
{
var reagentTransferButton = MakeReagentButton(text, amount, reagent, isBuffer, styleClass);
buttons.Add(reagentTransferButton);
}
return buttons;
} }
/// <summary> /// <summary>
@@ -133,36 +102,25 @@ namespace Content.Client.Chemistry.UI
/// <param name="state">State data sent by the server.</param> /// <param name="state">State data sent by the server.</param>
public void UpdateState(BoundUserInterfaceState state) public void UpdateState(BoundUserInterfaceState state)
{ {
var castState = (ChemMasterBoundUserInterfaceState)state; var castState = (ChemMasterBoundUserInterfaceState) state;
if (castState.UpdateLabel) if (castState.UpdateLabel)
LabelLine = GenerateLabel(castState); LabelLine = GenerateLabel(castState);
// Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on
UpdatePanelInfo(castState); UpdatePanelInfo(castState);
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
InputEjectButton.Disabled = castState.InputContainerInfo is null;
OutputEjectButton.Disabled = castState.OutputContainerInfo is null;
CreateBottleButton.Disabled = castState.OutputContainerInfo?.Reagents == null;
CreatePillButton.Disabled = castState.OutputContainerInfo?.Entities == null;
UpdateDosageFields(castState);
}
//assign default values for pill and bottle fields.
private void UpdateDosageFields(ChemMasterBoundUserInterfaceState castState)
{
var output = castState.OutputContainerInfo; var output = castState.OutputContainerInfo;
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
InputEjectButton.Disabled = castState.InputContainerInfo is null;
OutputEjectButton.Disabled = output is null;
CreateBottleButton.Disabled = output?.Reagents == null;
CreatePillButton.Disabled = output?.Entities == null;
var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int(); var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int();
var holdsReagents = output?.Reagents != null; var holdsReagents = output?.Reagents != null;
var pillNumberMax = holdsReagents ? 0 : remainingCapacity; var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
var bottleAmountMax = holdsReagents ? remainingCapacity : 0; var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0;
PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit);
PillTypeButtons[castState.SelectedPillType].Pressed = true; PillTypeButtons[castState.SelectedPillType].Pressed = true;
PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax; PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax;
PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit; PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit;
@@ -172,19 +130,8 @@ namespace Content.Client.Chemistry.UI
PillNumber.Value = pillNumberMax; PillNumber.Value = pillNumberMax;
if (BottleDosage.Value > bottleAmountMax) if (BottleDosage.Value > bottleAmountMax)
BottleDosage.Value = bottleAmountMax; BottleDosage.Value = bottleAmountMax;
// Avoid division by zero
if (PillDosage.Value > 0)
{
PillNumber.Value = Math.Min(bufferVolume / PillDosage.Value, pillNumberMax);
}
else
{
PillNumber.Value = 0;
}
BottleDosage.Value = Math.Min(bottleAmountMax, bufferVolume);
} }
/// <summary> /// <summary>
/// Generate a product label based on reagents in the buffer. /// Generate a product label based on reagents in the buffer.
/// </summary> /// </summary>
@@ -231,23 +178,46 @@ namespace Content.Client.Chemistry.UI
var bufferVol = new Label var bufferVol = new Label
{ {
Text = $"{state.BufferCurrentVolume}u", Text = $"{state.BufferCurrentVolume}u",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor } StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
}; };
bufferHBox.AddChild(bufferVol); bufferHBox.AddChild(bufferVol);
// initialises rowCount to allow for striped rows
var rowCount = 0;
foreach (var (reagent, quantity) in state.BufferReagents) foreach (var (reagent, quantity) in state.BufferReagents)
{ {
var reagentId = reagent; // Try to get the prototype for the given reagent. This gives us its name.
_prototypeManager.TryIndex(reagentId.Prototype, out ReagentPrototype? proto); _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto);
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
var reagentColor = proto?.SubstanceColor ?? default(Color);
BufferInfo.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagentId, quantity, true, true)); if (proto != null)
{
BufferInfo.Children.Add(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label {Text = $"{name}: "},
new Label
{
Text = $"{quantity}u",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
},
// Padding
new Control {HorizontalExpand = true},
MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent, true, StyleBase.ButtonOpenRight),
MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent, true, StyleBase.ButtonOpenLeft),
}
});
}
} }
} }
private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons) private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons)
{ {
control.Children.Clear(); control.Children.Clear();
@@ -258,111 +228,104 @@ namespace Content.Client.Chemistry.UI
{ {
Text = Loc.GetString("chem-master-window-no-container-loaded-text") Text = Loc.GetString("chem-master-window-no-container-loaded-text")
}); });
return;
} }
else
// Name of the container and its fill status (Ex: 44/100u)
control.Children.Add(new BoxContainer
{ {
Orientation = LayoutOrientation.Horizontal, // Name of the container and its fill status (Ex: 44/100u)
Children = control.Children.Add(new BoxContainer
{ {
new Label { Text = $"{info.DisplayName}: " }, Orientation = LayoutOrientation.Horizontal,
new Label Children =
{ {
Text = $"{info.CurrentVolume}/{info.MaxVolume}", new Label {Text = $"{info.DisplayName}: "},
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor } new Label
}
}
});
// Initialises rowCount to allow for striped rows
var rowCount = 0;
// Handle entities if they are not null
if (info.Entities != null)
{
foreach (var (id, quantity) in info.Entities.Select(x => (x.Id, x.Quantity)))
{
control.Children.Add(BuildReagentRow(default(Color), rowCount++, id, default(ReagentId), quantity, false, addReagentButtons));
}
}
// Handle reagents if they are not null
if (info.Reagents != null)
{
foreach (var reagent in info.Reagents)
{
_prototypeManager.TryIndex(reagent.Reagent.Prototype, out ReagentPrototype? proto);
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
var reagentColor = proto?.SubstanceColor ?? default(Color);
control.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagent.Reagent, reagent.Quantity, false, addReagentButtons));
}
}
}
/// <summary>
/// Take reagent/entity data and present rows, labels, and buttons appropriately. todo sprites?
/// </summary>
private Control BuildReagentRow(Color reagentColor, int rowCount, string name, ReagentId reagent, FixedPoint2 quantity, bool isBuffer, bool addReagentButtons)
{
//Colors rows and sets fallback for reagentcolor to the same as background, this will hide colorPanel for entities hopefully
var rowColor1 = Color.FromHex("#1B1B1E");
var rowColor2 = Color.FromHex("#202025");
var currentRowColor = (rowCount % 2 == 1) ? rowColor1 : rowColor2;
if ((reagentColor == default(Color))|(!addReagentButtons))
{
reagentColor = currentRowColor;
}
//this calls the separated button builder, and stores the return to render after labels
var reagentButtonConstructors = CreateReagentTransferButtons(reagent, isBuffer, addReagentButtons);
// Create the row layout with the color panel
var rowContainer = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label { Text = $"{name}: " },
new Label
{
Text = $"{quantity}u",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
},
// Padding
new Control { HorizontalExpand = true },
// Colored panels for reagents
new PanelContainer
{
Name = "colorPanel",
VerticalExpand = true,
MinWidth = 4,
PanelOverride = new StyleBoxFlat
{ {
BackgroundColor = reagentColor Text = $"{info.CurrentVolume}/{info.MaxVolume}",
}, StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
Margin = new Thickness(0, 1) }
} }
} });
};
IEnumerable<(string Name, ReagentId Id, FixedPoint2 Quantity)> contents;
if (info.Entities != null)
{
contents = info.Entities.Select(x => (x.Id, default(ReagentId), x.Quantity));
}
else if (info.Reagents != null)
{
contents = info.Reagents.Select(x =>
{
_prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto);
var name = proto?.LocalizedName
?? Loc.GetString("chem-master-window-unknown-reagent-text");
return (name, Id: x.Reagent, x.Quantity);
})
.OrderBy(r => r.Item1);
}
else
{
return;
}
foreach (var (name, id, quantity) in contents)
{
var inner = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label { Text = $"{name}: " },
new Label
{
Text = $"{quantity}u",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor },
}
}
};
if (addReagentButtons)
{
var cs = inner.Children;
// Padding
cs.Add(new Control { HorizontalExpand = true });
cs.Add(MakeReagentButton(
"1", ChemMasterReagentAmount.U1, id, false, StyleBase.ButtonOpenRight));
cs.Add(MakeReagentButton(
"5", ChemMasterReagentAmount.U5, id, false, StyleBase.ButtonOpenBoth));
cs.Add(MakeReagentButton(
"10", ChemMasterReagentAmount.U10, id, false, StyleBase.ButtonOpenBoth));
cs.Add(MakeReagentButton(
"25", ChemMasterReagentAmount.U25, id, false, StyleBase.ButtonOpenBoth));
cs.Add(MakeReagentButton(
"50", ChemMasterReagentAmount.U50, id, false, StyleBase.ButtonOpenBoth));
cs.Add(MakeReagentButton(
"100", ChemMasterReagentAmount.U100, id, false, StyleBase.ButtonOpenBoth));
cs.Add(MakeReagentButton(
Loc.GetString("chem-master-window-buffer-all-amount"),
ChemMasterReagentAmount.All, id, false, StyleBase.ButtonOpenLeft));
}
control.Children.Add(inner);
}
// Add the reagent buttons after the color panel
foreach (var reagentTransferButton in reagentButtonConstructors)
{
rowContainer.AddChild(reagentTransferButton);
} }
//Apply panencontainer to allow for striped rows
return new PanelContainer
{
PanelOverride = new StyleBoxFlat(currentRowColor),
Children = { rowContainer }
};
} }
public string LabelLine public String LabelLine
{ {
get => LabelLineEdit.Text; get
set => LabelLineEdit.Text = value; {
return LabelLineEdit.Text;
}
set
{
LabelLineEdit.Text = value;
}
} }
} }

View File

@@ -30,7 +30,7 @@ public sealed class ClientClothingSystem : ClothingSystem
/// For some context, im currently refactoring inventory. Part of that is slots not being indexed by a massive enum anymore, but by strings. /// For some context, im currently refactoring inventory. Part of that is slots not being indexed by a massive enum anymore, but by strings.
/// Problem here: Every rsi-state is using the old enum-names in their state. I already used the new inventoryslots ALOT. tldr: its this or another week of renaming files. /// Problem here: Every rsi-state is using the old enum-names in their state. I already used the new inventoryslots ALOT. tldr: its this or another week of renaming files.
/// </summary> /// </summary>
public static readonly Dictionary<string, string> TemporarySlotMap = new() //CP14 Public private static readonly Dictionary<string, string> TemporarySlotMap = new()
{ {
{"head", "HELMET"}, {"head", "HELMET"},
{"eyes", "EYES"}, {"eyes", "EYES"},
@@ -65,7 +65,6 @@ public sealed class ClientClothingSystem : ClothingSystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ClothingComponent, GetEquipmentVisualsEvent>(OnGetVisuals); SubscribeLocalEvent<ClothingComponent, GetEquipmentVisualsEvent>(OnGetVisuals);
SubscribeLocalEvent<ClothingComponent, InventoryTemplateUpdated>(OnInventoryTemplateUpdated);
SubscribeLocalEvent<InventoryComponent, VisualsChangedEvent>(OnVisualsChanged); SubscribeLocalEvent<InventoryComponent, VisualsChangedEvent>(OnVisualsChanged);
SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip); SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip);
@@ -78,7 +77,11 @@ public sealed class ClientClothingSystem : ClothingSystem
if (args.Sprite == null) if (args.Sprite == null)
return; return;
UpdateAllSlots(uid, component); var enumerator = _inventorySystem.GetSlotEnumerator((uid, component));
while (enumerator.NextItem(out var item, out var slot))
{
RenderEquipment(uid, item, slot.Name, component);
}
// No clothing equipped -> make sure the layer is hidden, though this should already be handled by on-unequip. // No clothing equipped -> make sure the layer is hidden, though this should already be handled by on-unequip.
if (args.Sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer)) if (args.Sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
@@ -88,23 +91,6 @@ public sealed class ClientClothingSystem : ClothingSystem
} }
} }
private void OnInventoryTemplateUpdated(Entity<ClothingComponent> ent, ref InventoryTemplateUpdated args)
{
UpdateAllSlots(ent.Owner, clothing: ent.Comp);
}
private void UpdateAllSlots(
EntityUid uid,
InventoryComponent? inventoryComponent = null,
ClothingComponent? clothing = null)
{
var enumerator = _inventorySystem.GetSlotEnumerator((uid, inventoryComponent));
while (enumerator.NextItem(out var item, out var slot))
{
RenderEquipment(uid, item, slot.Name, inventoryComponent, clothingComponent: clothing);
}
}
private void OnGetVisuals(EntityUid uid, ClothingComponent item, GetEquipmentVisualsEvent args) private void OnGetVisuals(EntityUid uid, ClothingComponent item, GetEquipmentVisualsEvent args)
{ {
if (!TryComp(args.Equipee, out InventoryComponent? inventory)) if (!TryComp(args.Equipee, out InventoryComponent? inventory))

View File

@@ -7,7 +7,7 @@ using Robust.Client.GameObjects;
namespace Content.Client.Clothing; namespace Content.Client.Clothing;
public sealed class FlippableClothingVisualizerSystem : VisualizerSystem<FlippableClothingVisualsComponent> public sealed class FlippableClothingVisualizerSystem : VisualizerSystem<FlippableClothingVisualsComponent>
{ {
[Dependency] private readonly SharedItemSystem _itemSys = default!; [Dependency] private readonly SharedItemSystem _itemSys = default!;

View File

@@ -1,5 +1,4 @@
using System.Linq; using System.Linq;
using Content.Client.PDA;
using Content.Shared.Clothing.Components; using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems; using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Inventory; using Content.Shared.Inventory;
@@ -52,15 +51,6 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem
{ {
sprite.CopyFrom(otherSprite); sprite.CopyFrom(otherSprite);
} }
// Edgecase for PDAs to include visuals when UI is open
if (TryComp(uid, out PdaBorderColorComponent? borderColor)
&& proto.TryGetComponent(out PdaBorderColorComponent? otherBorderColor, _factory))
{
borderColor.BorderColor = otherBorderColor.BorderColor;
borderColor.AccentHColor = otherBorderColor.AccentHColor;
borderColor.AccentVColor = otherBorderColor.AccentVColor;
}
} }
/// <summary> /// <summary>

View File

@@ -1,21 +1,15 @@
using Content.Client.Clothing.Systems; using Content.Client.Clothing.Systems;
using Content.Shared.Clothing.Components; using Content.Shared.Clothing.Components;
using Content.Shared.Tag;
using Content.Shared.Prototypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Clothing.UI; namespace Content.Client.Clothing.UI;
[UsedImplicitly] [UsedImplicitly]
public sealed class ChameleonBoundUserInterface : BoundUserInterface public sealed class ChameleonBoundUserInterface : BoundUserInterface
{ {
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly ChameleonClothingSystem _chameleon; private readonly ChameleonClothingSystem _chameleon;
private readonly TagSystem _tag;
[ViewVariables] [ViewVariables]
private ChameleonMenu? _menu; private ChameleonMenu? _menu;
@@ -23,7 +17,6 @@ public sealed class ChameleonBoundUserInterface : BoundUserInterface
public ChameleonBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) public ChameleonBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {
_chameleon = EntMan.System<ChameleonClothingSystem>(); _chameleon = EntMan.System<ChameleonClothingSystem>();
_tag = EntMan.System<TagSystem>();
} }
protected override void Open() protected override void Open()
@@ -41,24 +34,7 @@ public sealed class ChameleonBoundUserInterface : BoundUserInterface
return; return;
var targets = _chameleon.GetValidTargets(st.Slot); var targets = _chameleon.GetValidTargets(st.Slot);
if (st.RequiredTag != null) _menu?.UpdateState(targets, st.SelectedId);
{
var newTargets = new List<string>();
foreach (var target in targets)
{
if (string.IsNullOrEmpty(target) || !_proto.TryIndex(target, out EntityPrototype? proto))
continue;
if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, st.RequiredTag))
continue;
newTargets.Add(target);
}
_menu?.UpdateState(newTargets, st.SelectedId);
} else
{
_menu?.UpdateState(targets, st.SelectedId);
}
} }
private void OnIdSelected(string selectedId) private void OnIdSelected(string selectedId)

View File

@@ -1,62 +1,17 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'comms-console-menu-title'}" Title="{Loc 'comms-console-menu-title'}"
MinSize="400 300"> MinSize="400 225">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="5">
<TextEdit Name="MessageInput" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 0 5" MinHeight="100" />
<Button Name="AnnounceButton" Text="{Loc 'comms-console-menu-announcement-button'}" ToolTip="{Loc 'comms-console-menu-announcement-button-tooltip'}" StyleClasses="OpenLeft" Access="Public" />
<Button Name="BroadcastButton" Text="{Loc 'comms-console-menu-broadcast-button'}" ToolTip="{Loc 'comms-console-menu-broadcast-button-tooltip'}" StyleClasses="OpenLeft" Access="Public" />
<!-- Main Container --> <OptionButton Name="AlertLevelButton" ToolTip="{Loc 'comms-console-menu-alert-level-button-tooltip'}" StyleClasses="OpenRight" Access="Public" />
<BoxContainer Orientation="Vertical"
HorizontalExpand="False"
VerticalExpand="True"
Margin="6 6 6 5">
<TextEdit Name="MessageInput" <Control MinSize="10 10" />
VerticalExpand="True"
HorizontalExpand="True"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
MinHeight="100"/>
<!-- ButtonsPart --> <RichTextLabel Name="CountdownLabel" VerticalExpand="True" />
<BoxContainer Orientation="Vertical" <Button Name="EmergencyShuttleButton" Text="Placeholder Text" ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}" Access="Public" />
VerticalAlignment="Bottom"
SeparationOverride="4">
<!-- AnnouncePart -->
<BoxContainer Orientation="Vertical"
Margin="0 2">
<Button Name="AnnounceButton"
Access="Public"
Text="{Loc 'comms-console-menu-announcement-button'}"
ToolTip="{Loc 'comms-console-menu-announcement-button-tooltip'}"
StyleClasses="OpenLeft"
Margin="0 0 1 0"
Disabled="True"/>
<Button Name="BroadcastButton"
Access="Public"
Text="{Loc 'comms-console-menu-broadcast-button'}"
ToolTip="{Loc 'comms-console-menu-broadcast-button-tooltip'}"
StyleClasses="OpenBoth"/>
<OptionButton Name="AlertLevelButton"
Access="Public"
ToolTip="{Loc 'comms-console-menu-alert-level-button-tooltip'}"
StyleClasses="OpenRight"/>
</BoxContainer>
<!-- EmergencyPart -->
<BoxContainer Orientation="Vertical"
SeparationOverride="6">
<RichTextLabel Name="CountdownLabel"/>
<Button Name="EmergencyShuttleButton"
Access="Public"
Text="Placeholder Text"
ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}"/>
</BoxContainer>
</BoxContainer>
</BoxContainer> </BoxContainer>
</controls:FancyWindow> </controls:FancyWindow>

View File

@@ -1,20 +1,15 @@
<DefaultWindow xmlns="https://spacestation14.io"> <DefaultWindow xmlns="https://spacestation14.io">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<BoxContainer Orientation="Vertical" MinWidth="243" Margin="0 0 5 0"> <BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.4" Margin="0 0 5 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 0 0 5"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 0 0 5">
<LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True"/> <LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True"/>
<OptionButton Name="OptionCategories" Access="Public" MinSize="130 0"/> <OptionButton Name="OptionCategories" Access="Public" MinSize="130 0"/>
</BoxContainer> </BoxContainer>
<ItemList Name="Recipes" Access="Public" SelectMode="Single" VerticalExpand="True"/> <ItemList Name="Recipes" Access="Public" SelectMode="Single" VerticalExpand="True"/>
<ScrollContainer Name="RecipesGridScrollContainer" VerticalExpand="True" Access="Public" Visible="False">
<GridContainer Name="RecipesGrid" Columns="5" Access="Public"/>
</ScrollContainer>
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.6">
<BoxContainer Orientation="Horizontal"> <Button Name="FavoriteButton" Visible="false" HorizontalExpand="False"
<Button Name="MenuGridViewButton" ToggleMode="True" Text="{Loc construction-menu-grid-view}"/> HorizontalAlignment="Right" Margin="0 0 0 15"/>
<Button Name="FavoriteButton" Visible="false"/>
</BoxContainer>
<Control> <Control>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="0 0 0 5"> <BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="0 0 0 5">
<BoxContainer Orientation="Horizontal" Align="Center"> <BoxContainer Orientation="Horizontal" Align="Center">

View File

@@ -25,16 +25,11 @@ namespace Content.Client.Construction.UI
OptionButton OptionCategories { get; } OptionButton OptionCategories { get; }
bool EraseButtonPressed { get; set; } bool EraseButtonPressed { get; set; }
bool GridViewButtonPressed { get; set; }
bool BuildButtonPressed { get; set; } bool BuildButtonPressed { get; set; }
ItemList Recipes { get; } ItemList Recipes { get; }
ItemList RecipeStepList { get; } ItemList RecipeStepList { get; }
ScrollContainer RecipesGridScrollContainer { get; }
GridContainer RecipesGrid { get; }
event EventHandler<(string search, string catagory)> PopulateRecipes; event EventHandler<(string search, string catagory)> PopulateRecipes;
event EventHandler<ItemList.Item?> RecipeSelected; event EventHandler<ItemList.Item?> RecipeSelected;
event EventHandler RecipeFavorited; event EventHandler RecipeFavorited;
@@ -77,16 +72,9 @@ namespace Content.Client.Construction.UI
set => EraseButton.Pressed = value; set => EraseButton.Pressed = value;
} }
public bool GridViewButtonPressed
{
get => MenuGridViewButton.Pressed;
set => MenuGridViewButton.Pressed = value;
}
public ConstructionMenu() public ConstructionMenu()
{ {
SetSize = new Vector2(560, 450); SetSize = MinSize = new Vector2(720, 320);
MinSize = new Vector2(560, 320);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -114,9 +102,6 @@ namespace Content.Client.Construction.UI
EraseButton.OnToggled += args => EraseButtonToggled?.Invoke(this, args.Pressed); EraseButton.OnToggled += args => EraseButtonToggled?.Invoke(this, args.Pressed);
FavoriteButton.OnPressed += args => RecipeFavorited?.Invoke(this, EventArgs.Empty); FavoriteButton.OnPressed += args => RecipeFavorited?.Invoke(this, EventArgs.Empty);
MenuGridViewButton.OnPressed += _ =>
PopulateRecipes?.Invoke(this, (SearchBar.Text, Categories[OptionCategories.SelectedId]));
} }
public event EventHandler? ClearAllGhosts; public event EventHandler? ClearAllGhosts;

View File

@@ -1,8 +1,7 @@
using System.Linq; using System.Linq;
using System.Numerics;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.MenuBar.Widgets; using Content.Client.UserInterface.Systems.MenuBar.Widgets;
using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Prototypes;
using Content.Shared.Tag;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -12,6 +11,7 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.Utility; using Robust.Client.Utility;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Graphics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BaseButton;
@@ -33,12 +33,10 @@ namespace Content.Client.Construction.UI
private readonly IConstructionMenuView _constructionView; private readonly IConstructionMenuView _constructionView;
private readonly EntityWhitelistSystem _whitelistSystem; private readonly EntityWhitelistSystem _whitelistSystem;
private readonly SpriteSystem _spriteSystem;
private ConstructionSystem? _constructionSystem; private ConstructionSystem? _constructionSystem;
private ConstructionPrototype? _selected; private ConstructionPrototype? _selected;
private List<ConstructionPrototype> _favoritedRecipes = []; private List<ConstructionPrototype> _favoritedRecipes = [];
private Dictionary<string, TextureButton> _recipeButtons = new();
private string _selectedCategory = string.Empty; private string _selectedCategory = string.Empty;
private string _favoriteCatName = "construction-category-favorites"; private string _favoriteCatName = "construction-category-favorites";
private string _forAllCategoryName = "construction-category-all"; private string _forAllCategoryName = "construction-category-all";
@@ -87,7 +85,6 @@ namespace Content.Client.Construction.UI
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_constructionView = new ConstructionMenu(); _constructionView = new ConstructionMenu();
_whitelistSystem = _entManager.System<EntityWhitelistSystem>(); _whitelistSystem = _entManager.System<EntityWhitelistSystem>();
_spriteSystem = _entManager.System<SpriteSystem>();
// This is required so that if we load after the system is initialized, we can bind to it immediately // This is required so that if we load after the system is initialized, we can bind to it immediately
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem)) if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
@@ -153,24 +150,12 @@ namespace Content.Client.Construction.UI
PopulateInfo(_selected); PopulateInfo(_selected);
} }
private void OnGridViewRecipeSelected(object? sender, ConstructionPrototype? recipe)
{
if (recipe is null)
{
_selected = null;
_constructionView.ClearRecipeInfo();
return;
}
_selected = recipe;
if (_placementManager.IsActive && !_placementManager.Eraser) UpdateGhostPlacement();
PopulateInfo(_selected);
}
private void OnViewPopulateRecipes(object? sender, (string search, string catagory) args) private void OnViewPopulateRecipes(object? sender, (string search, string catagory) args)
{ {
var (search, category) = args; var (search, category) = args;
var recipesList = _constructionView.Recipes;
recipesList.Clear();
var recipes = new List<ConstructionPrototype>(); var recipes = new List<ConstructionPrototype>();
var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName; var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName;
@@ -185,7 +170,7 @@ namespace Content.Client.Construction.UI
if (recipe.Hide) if (recipe.Hide)
continue; continue;
if (!recipe.CrystallPunkAllowed) //CrystallEdge clearing recipes if (!recipe.CrystallPunkAllowed) //CrystallPunk clearing recipes
continue; continue;
if (_playerManager.LocalSession == null if (_playerManager.LocalSession == null
@@ -219,73 +204,12 @@ namespace Content.Client.Construction.UI
recipes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCulture)); recipes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCulture));
var recipesList = _constructionView.Recipes; foreach (var recipe in recipes)
recipesList.Clear();
var recipesGrid = _constructionView.RecipesGrid;
recipesGrid.RemoveAllChildren();
_constructionView.RecipesGridScrollContainer.Visible = _constructionView.GridViewButtonPressed;
_constructionView.Recipes.Visible = !_constructionView.GridViewButtonPressed;
if (_constructionView.GridViewButtonPressed)
{ {
foreach (var recipe in recipes) recipesList.Add(GetItem(recipe, recipesList));
{
var itemButton = new TextureButton
{
TextureNormal = _spriteSystem.Frame0(recipe.Icon),
VerticalAlignment = Control.VAlignment.Center,
Name = recipe.Name,
ToolTip = recipe.Name,
Scale = new Vector2(1.35f),
ToggleMode = true,
};
var itemButtonPanelContainer = new PanelContainer
{
PanelOverride = new StyleBoxFlat { BackgroundColor = StyleNano.ButtonColorDefault },
Children = { itemButton },
};
itemButton.OnToggled += buttonToggledEventArgs =>
{
SelectGridButton(itemButton, buttonToggledEventArgs.Pressed);
if (buttonToggledEventArgs.Pressed &&
_selected != null &&
_recipeButtons.TryGetValue(_selected.Name, out var oldButton))
{
oldButton.Pressed = false;
SelectGridButton(oldButton, false);
}
OnGridViewRecipeSelected(this, buttonToggledEventArgs.Pressed ? recipe : null);
};
recipesGrid.AddChild(itemButtonPanelContainer);
_recipeButtons[recipe.Name] = itemButton;
var isCurrentButtonSelected = _selected == recipe;
itemButton.Pressed = isCurrentButtonSelected;
SelectGridButton(itemButton, isCurrentButtonSelected);
}
} }
else
{
foreach (var recipe in recipes)
{
recipesList.Add(GetItem(recipe, recipesList));
}
}
}
private void SelectGridButton(TextureButton button, bool select) // There is apparently no way to set which
{
if (button.Parent is not PanelContainer buttonPanel)
return;
button.Modulate = select ? Color.Green : Color.White;
var buttonColor = select ? StyleNano.ButtonColorDefault : Color.Transparent;
buttonPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = buttonColor };
} }
private void PopulateCategories(string? selectCategory = null) private void PopulateCategories(string? selectCategory = null)
@@ -336,10 +260,11 @@ namespace Content.Client.Construction.UI
private void PopulateInfo(ConstructionPrototype prototype) private void PopulateInfo(ConstructionPrototype prototype)
{ {
var spriteSys = _systemManager.GetEntitySystem<SpriteSystem>();
_constructionView.ClearRecipeInfo(); _constructionView.ClearRecipeInfo();
_constructionView.SetRecipeInfo( _constructionView.SetRecipeInfo(
prototype.Name, prototype.Description, _spriteSystem.Frame0(prototype.Icon), prototype.Name, prototype.Description, spriteSys.Frame0(prototype.Icon),
prototype.Type != ConstructionType.Item, prototype.Type != ConstructionType.Item,
!_favoritedRecipes.Contains(prototype)); !_favoritedRecipes.Contains(prototype));
@@ -352,6 +277,7 @@ namespace Content.Client.Construction.UI
if (_constructionSystem?.GetGuide(prototype) is not { } guide) if (_constructionSystem?.GetGuide(prototype) is not { } guide)
return; return;
var spriteSys = _systemManager.GetEntitySystem<SpriteSystem>();
foreach (var entry in guide.Entries) foreach (var entry in guide.Entries)
{ {
@@ -367,20 +293,20 @@ namespace Content.Client.Construction.UI
// The padding needs to be applied regardless of text length... (See PadLeft documentation) // The padding needs to be applied regardless of text length... (See PadLeft documentation)
text = text.PadLeft(text.Length + entry.Padding); text = text.PadLeft(text.Length + entry.Padding);
var icon = entry.Icon != null ? _spriteSystem.Frame0(entry.Icon) : Texture.Transparent; var icon = entry.Icon != null ? spriteSys.Frame0(entry.Icon) : Texture.Transparent;
stepList.AddItem(text, icon, false); stepList.AddItem(text, icon, false);
} }
} }
private ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList) private static ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList)
{ {
return new(itemList) return new(itemList)
{ {
Metadata = recipe, Metadata = recipe,
Text = recipe.Name, Text = recipe.Name,
Icon = _spriteSystem.Frame0(recipe.Icon), Icon = recipe.Icon.Frame0(),
TooltipEnabled = true, TooltipEnabled = true,
TooltipText = recipe.Description, TooltipText = recipe.Description
}; };
} }

View File

@@ -31,7 +31,7 @@ namespace Content.Client.Crayon.UI
private void PopulateCrayons() private void PopulateCrayons()
{ {
var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon")); var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
_menu?.Populate(crayonDecals.ToList()); _menu?.Populate(crayonDecals);
} }
public override void OnProtoReload(PrototypesReloadedEventArgs args) public override void OnProtoReload(PrototypesReloadedEventArgs args)
@@ -44,16 +44,6 @@ namespace Content.Client.Crayon.UI
PopulateCrayons(); PopulateCrayons();
} }
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
base.ReceiveMessage(message);
if (_menu is null || message is not CrayonUsedMessage crayonMessage)
return;
_menu.AdvanceState(crayonMessage.DrawnDecal);
}
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
{ {
base.UpdateState(state); base.UpdateState(state);

View File

@@ -1,13 +1,14 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'crayon-window-title'}" Title="{Loc 'crayon-window-title'}"
MinSize="450 500" MinSize="250 300"
SetSize="450 500"> SetSize="250 300">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<ColorSelectorSliders Name="ColorSelector" Visible="False" /> <ColorSelectorSliders Name="ColorSelector" Visible="False" />
<LineEdit Name="Search" Margin="0 0 0 8" PlaceHolder="{Loc 'crayon-window-placeholder'}" /> <LineEdit Name="Search" />
<ScrollContainer VerticalExpand="True"> <ScrollContainer VerticalExpand="True">
<BoxContainer Name="Grids" Orientation="Vertical"> <GridContainer Name="Grid" Columns="6">
</BoxContainer> <!-- Crayon decals get added here by code -->
</GridContainer>
</ScrollContainer> </ScrollContainer>
</BoxContainer> </BoxContainer>
</DefaultWindow> </DefaultWindow>

View File

@@ -1,10 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.Crayon; using Content.Shared.Crayon;
using Content.Shared.Decals; using Content.Shared.Decals;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
@@ -20,12 +18,7 @@ namespace Content.Client.Crayon.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class CrayonWindow : DefaultWindow public sealed partial class CrayonWindow : DefaultWindow
{ {
[Dependency] private readonly IEntitySystemManager _entitySystem = default!; private Dictionary<string, Texture>? _decals;
private readonly SpriteSystem _spriteSystem = default!;
private Dictionary<string, List<(string Name, Texture Texture)>>? _decals;
private List<string>? _allDecals;
private string? _autoSelected;
private string? _selected; private string? _selected;
private Color _color; private Color _color;
@@ -35,10 +28,8 @@ namespace Content.Client.Crayon.UI
public CrayonWindow() public CrayonWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
Search.OnTextChanged += SearchChanged; Search.OnTextChanged += _ => RefreshList();
ColorSelector.OnColorChanged += SelectColor; ColorSelector.OnColorChanged += SelectColor;
} }
@@ -53,94 +44,51 @@ namespace Content.Client.Crayon.UI
private void RefreshList() private void RefreshList()
{ {
// Clear // Clear
Grids.DisposeAllChildren(); Grid.DisposeAllChildren();
if (_decals == null)
if (_decals == null || _allDecals == null)
return; return;
var filter = Search.Text; var filter = Search.Text;
var comma = filter.IndexOf(','); foreach (var (decal, tex) in _decals)
var first = (comma == -1 ? filter : filter[..comma]).Trim();
var names = _decals.Keys.ToList();
names.Sort((a, b) => a == "random" ? 1 : b == "random" ? -1 : a.CompareTo(b));
if (_autoSelected != null && first != _autoSelected && _allDecals.Contains(first))
{ {
_selected = first; if (!decal.Contains(filter))
_autoSelected = _selected;
OnSelected?.Invoke(_selected);
}
foreach (var categoryName in names)
{
var locName = Loc.GetString("crayon-category-" + categoryName);
var category = _decals[categoryName].Where(d => locName.Contains(first) || d.Name.Contains(first)).ToList();
if (category.Count == 0)
continue; continue;
var label = new Label var button = new TextureButton()
{ {
Text = locName TextureNormal = tex,
Name = decal,
ToolTip = decal,
Modulate = _color,
}; };
button.OnPressed += ButtonOnPressed;
var grid = new GridContainer if (_selected == decal)
{ {
Columns = 6, var panelContainer = new PanelContainer()
Margin = new Thickness(0, 0, 0, 16)
};
Grids.AddChild(label);
Grids.AddChild(grid);
foreach (var (name, texture) in category)
{
var button = new TextureButton()
{ {
TextureNormal = texture, PanelOverride = new StyleBoxFlat()
Name = name,
ToolTip = name,
Modulate = _color,
Scale = new System.Numerics.Vector2(2, 2)
};
button.OnPressed += ButtonOnPressed;
if (_selected == name)
{
var panelContainer = new PanelContainer()
{ {
PanelOverride = new StyleBoxFlat() BackgroundColor = StyleNano.ButtonColorDefault,
{ },
BackgroundColor = StyleNano.ButtonColorDefault, Children =
}, {
Children = button,
{ },
button, };
}, Grid.AddChild(panelContainer);
}; }
grid.AddChild(panelContainer); else
} {
else Grid.AddChild(button);
{
grid.AddChild(button);
}
} }
} }
} }
private void SearchChanged(LineEdit.LineEditEventArgs obj)
{
_autoSelected = ""; // Placeholder to kick off the auto-select in refreshlist()
RefreshList();
}
private void ButtonOnPressed(ButtonEventArgs obj) private void ButtonOnPressed(ButtonEventArgs obj)
{ {
if (obj.Button.Name == null) return; if (obj.Button.Name == null) return;
_selected = obj.Button.Name; _selected = obj.Button.Name;
_autoSelected = null;
OnSelected?.Invoke(_selected); OnSelected?.Invoke(_selected);
RefreshList(); RefreshList();
} }
@@ -159,38 +107,12 @@ namespace Content.Client.Crayon.UI
RefreshList(); RefreshList();
} }
public void AdvanceState(string drawnDecal) public void Populate(IEnumerable<DecalPrototype> prototypes)
{ {
var filter = Search.Text; _decals = new Dictionary<string, Texture>();
if (!filter.Contains(',') || !filter.Contains(drawnDecal))
return;
var first = filter[..filter.IndexOf(',')].Trim();
if (first.Equals(drawnDecal, StringComparison.InvariantCultureIgnoreCase))
{
Search.Text = filter[(filter.IndexOf(',') + 1)..].Trim();
_autoSelected = first;
}
RefreshList();
}
public void Populate(List<DecalPrototype> prototypes)
{
_decals = [];
_allDecals = [];
prototypes.Sort((a, b) => a.ID.CompareTo(b.ID));
foreach (var decalPrototype in prototypes) foreach (var decalPrototype in prototypes)
{ {
var category = "random"; _decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
if (decalPrototype.Tags.Count > 1 && decalPrototype.Tags[1].StartsWith("crayon-"))
category = decalPrototype.Tags[1].Replace("crayon-", "");
var list = _decals.GetOrNew(category);
list.Add((decalPrototype.ID, _spriteSystem.Frame0(decalPrototype.Sprite)));
_allDecals.Add(decalPrototype.ID);
} }
RefreshList(); RefreshList();

View File

@@ -124,10 +124,6 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
continue; continue;
} }
var targetEv = new GetFlashEffectTargetEvent(ent);
RaiseLocalEvent(ent, ref targetEv);
ent = targetEv.Target;
EnsureComp<ColorFlashEffectComponent>(ent, out comp); EnsureComp<ColorFlashEffectComponent>(ent, out comp);
comp.NetSyncEnabled = false; comp.NetSyncEnabled = false;
comp.Color = sprite.Color; comp.Color = sprite.Color;
@@ -136,9 +132,3 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
} }
} }
} }
/// <summary>
/// Raised on an entity to change the target for a color flash effect.
/// </summary>
[ByRefEvent]
public record struct GetFlashEffectTargetEvent(EntityUid Target);

View File

@@ -1,95 +0,0 @@
using Content.Shared.Electrocution;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Player;
namespace Content.Client.Electrocution;
/// <summary>
/// Shows the Electrocution HUD to entities with the ShowElectrocutionHUDComponent.
/// </summary>
public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<ElectrocutionHUDVisualsComponent>
{
[Dependency] private readonly IPlayerManager _playerMan = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ShowElectrocutionHUDComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<ShowElectrocutionHUDComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<ShowElectrocutionHUDComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<ShowElectrocutionHUDComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
}
private void OnPlayerAttached(Entity<ShowElectrocutionHUDComponent> ent, ref LocalPlayerAttachedEvent args)
{
ShowHUD();
}
private void OnPlayerDetached(Entity<ShowElectrocutionHUDComponent> ent, ref LocalPlayerDetachedEvent args)
{
RemoveHUD();
}
private void OnInit(Entity<ShowElectrocutionHUDComponent> ent, ref ComponentInit args)
{
if (_playerMan.LocalEntity == ent)
{
ShowHUD();
}
}
private void OnShutdown(Entity<ShowElectrocutionHUDComponent> ent, ref ComponentShutdown args)
{
if (_playerMan.LocalEntity == ent)
{
RemoveHUD();
}
}
// Show the HUD to the client.
// We have to look for all current entities that can be electrified and toggle the HUD layer on if they are.
private void ShowHUD()
{
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
{
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, appearanceComp))
continue;
if (electrified)
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, true);
else
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
}
}
// Remove the HUD from the client.
// Find all current entities that can be electrified and hide the HUD layer.
private void RemoveHUD()
{
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
{
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
}
}
// Toggle the HUD layer if an entity becomes (de-)electrified
protected override void OnAppearanceChange(EntityUid uid, ElectrocutionHUDVisualsComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, args.Component))
return;
var player = _playerMan.LocalEntity;
if (electrified && HasComp<ShowElectrocutionHUDComponent>(player))
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, true);
else
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, false);
}
}

View File

@@ -12,7 +12,6 @@ using Content.Client.IoC;
using Content.Client.Launcher; using Content.Client.Launcher;
using Content.Client.Lobby; using Content.Client.Lobby;
using Content.Client.MainMenu; using Content.Client.MainMenu;
using Content.Client.Overlays;
using Content.Client.Parallax.Managers; using Content.Client.Parallax.Managers;
using Content.Client.Players.PlayTimeTracking; using Content.Client.Players.PlayTimeTracking;
using Content.Client.Radiation.Overlays; using Content.Client.Radiation.Overlays;
@@ -160,14 +159,13 @@ namespace Content.Client.Entry
_parallaxManager.LoadDefaultParallax(); _parallaxManager.LoadDefaultParallax();
_overlayManager.AddOverlay(new CP14BasePostProcessOverlay()); // CP14-PostProcess
_overlayManager.AddOverlay(new SingularityOverlay()); _overlayManager.AddOverlay(new SingularityOverlay());
_overlayManager.AddOverlay(new RadiationPulseOverlay()); _overlayManager.AddOverlay(new RadiationPulseOverlay());
_chatManager.Initialize(); _chatManager.Initialize();
_clientPreferencesManager.Initialize(); _clientPreferencesManager.Initialize();
_euiManager.Initialize(); _euiManager.Initialize();
_voteManager.Initialize(); _voteManager.Initialize();
_userInterfaceManager.SetDefaultTheme("SS14DefaultTheme"); _userInterfaceManager.SetDefaultTheme(_configManager.GetCVar(CCVars.UIDefaultInterfaceTheme));
_userInterfaceManager.SetActiveTheme(_configManager.GetCVar(CVars.InterfaceTheme)); _userInterfaceManager.SetActiveTheme(_configManager.GetCVar(CVars.InterfaceTheme));
_documentParsingManager.Initialize(); _documentParsingManager.Initialize();
_titleWindowManager.Initialize(); _titleWindowManager.Initialize();

View File

@@ -55,7 +55,7 @@ namespace Content.Client.Eui
/// </summary> /// </summary>
protected void SendMessage(EuiMessageBase msg) protected void SendMessage(EuiMessageBase msg)
{ {
var netMsg = new MsgEuiMessage(); var netMsg = _netManager.CreateNetMessage<MsgEuiMessage>();
netMsg.Id = Id; netMsg.Id = Id;
netMsg.Message = msg; netMsg.Message = msg;

View File

@@ -1,8 +0,0 @@
using Content.Shared.Explosion.EntitySystems;
namespace Content.Client.Explosion;
public sealed class ScatteringGrenadeSystem : SharedScatteringGrenadeSystem
{
}

View File

@@ -112,12 +112,7 @@ namespace Content.Client.Ghost
_actions.RemoveAction(uid, component.ToggleLightingActionEntity); _actions.RemoveAction(uid, component.ToggleLightingActionEntity);
_actions.RemoveAction(uid, component.ToggleFoVActionEntity); _actions.RemoveAction(uid, component.ToggleFoVActionEntity);
_actions.RemoveAction(uid, component.ToggleGhostsActionEntity); _actions.RemoveAction(uid, component.ToggleGhostsActionEntity);
//_actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity); //Dont need in CP14 _actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity);
//CP14
_actions.RemoveAction(uid, component.CP14ZLevelUpActionEntity);
_actions.RemoveAction(uid, component.CP14ZLevelDownActionEntity);
_actions.RemoveAction(uid, component.CP14ToggleRoofActionEntity);
//CP14 end
if (uid != _playerManager.LocalEntity) if (uid != _playerManager.LocalEntity)
return; return;

View File

@@ -168,8 +168,8 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
var parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries); var parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
foreach (var entry in GetSortedEntries(roots)) foreach (var entry in GetSortedEntries(roots))
{ {
if (!entry.CrystallPunkAllowed) continue; //CrystallEdge guidebook filter if (!entry.CrystallPunkAllowed) continue; //CrystallPunk guidebook filter
if (entry.LocFilter is not null && entry.LocFilter != ContentLocalizationManager.Culture) continue; //CrystallEdge guidebook filter if (entry.LocFilter is not null && entry.LocFilter != ContentLocalizationManager.Culture) continue; //CrystallPunk guidebook filter
AddEntry(entry.Id, parent, addedEntries); AddEntry(entry.Id, parent, addedEntries);
} }

View File

@@ -32,7 +32,7 @@ public sealed class GuidebookSystem : EntitySystem
[Dependency] private readonly RgbLightControllerSystem _rgbLightControllerSystem = default!; [Dependency] private readonly RgbLightControllerSystem _rgbLightControllerSystem = default!;
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!; [Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
[Dependency] private readonly TagSystem _tags = default!; [Dependency] private readonly TagSystem _tags = default!;
[Dependency] private readonly IPrototypeManager _proto = default!; //CrystallEdge guidebook filter [Dependency] private readonly IPrototypeManager _proto = default!; //CrystallPunk guidebook filter
public event Action<List<ProtoId<GuideEntryPrototype>>, public event Action<List<ProtoId<GuideEntryPrototype>>,
List<ProtoId<GuideEntryPrototype>>?, List<ProtoId<GuideEntryPrototype>>?,
@@ -47,7 +47,7 @@ public sealed class GuidebookSystem : EntitySystem
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<GuideHelpComponent, MapInitEvent>(OnCrystallEdgeMapInit); //CrystallEdge guidebook filter SubscribeLocalEvent<GuideHelpComponent, MapInitEvent>(OnCrystallPunkMapInit); //CrystallPunk guidebook filter
SubscribeLocalEvent<GuideHelpComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerbs); SubscribeLocalEvent<GuideHelpComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerbs);
SubscribeLocalEvent<GuideHelpComponent, ActivateInWorldEvent>(OnInteract); SubscribeLocalEvent<GuideHelpComponent, ActivateInWorldEvent>(OnInteract);
@@ -57,12 +57,12 @@ public sealed class GuidebookSystem : EntitySystem
OnGuidebookControlsTestGetAlternateVerbs); OnGuidebookControlsTestGetAlternateVerbs);
} }
//CrystallEdge guidebook filter //CrystallPunk guidebook filter
private void OnCrystallEdgeMapInit(Entity<GuideHelpComponent> ent, ref MapInitEvent args) private void OnCrystallPunkMapInit(Entity<GuideHelpComponent> ent, ref MapInitEvent args)
{ {
foreach (var guide in ent.Comp.Guides) foreach (var guide in ent.Comp.Guides)
{ {
var guideProto = _proto.Index(guide); var guideProto = _proto.Index<GuideEntryPrototype>(guide);
if (!guideProto.CrystallPunkAllowed) //REMOVE unnecessary guidebook if (!guideProto.CrystallPunkAllowed) //REMOVE unnecessary guidebook
{ {
RemComp<GuideHelpComponent>(ent); RemComp<GuideHelpComponent>(ent);
@@ -70,7 +70,7 @@ public sealed class GuidebookSystem : EntitySystem
} }
} }
} }
//CrystallEdge guidebook filter end //CrystallPunk guidebook filter end
/// <summary> /// <summary>
/// Gets a user entity to use for verbs and examinations. If the player has no attached entity, this will use a /// Gets a user entity to use for verbs and examinations. If the player has no attached entity, this will use a

View File

@@ -1,101 +0,0 @@
using Content.Shared.Holopad;
using Content.Shared.Silicons.StationAi;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Shared.Player;
using System.Numerics;
namespace Content.Client.Holopad;
public sealed class HolopadBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[ViewVariables]
private HolopadWindow? _window;
public HolopadBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
base.Open();
_window = this.CreateWindow<HolopadWindow>();
_window.Title = Loc.GetString("holopad-window-title", ("title", EntMan.GetComponent<MetaDataComponent>(Owner).EntityName));
if (this.UiKey is not HolopadUiKey)
{
Close();
return;
}
var uiKey = (HolopadUiKey)this.UiKey;
// AIs will see a different holopad interface to crew when interacting with them in the world
if (uiKey == HolopadUiKey.InteractionWindow && EntMan.HasComponent<StationAiHeldComponent>(_playerManager.LocalEntity))
uiKey = HolopadUiKey.InteractionWindowForAi;
_window.SetState(Owner, uiKey);
_window.UpdateState(new Dictionary<NetEntity, string>());
// Set message actions
_window.SendHolopadStartNewCallMessageAction += SendHolopadStartNewCallMessage;
_window.SendHolopadAnswerCallMessageAction += SendHolopadAnswerCallMessage;
_window.SendHolopadEndCallMessageAction += SendHolopadEndCallMessage;
_window.SendHolopadStartBroadcastMessageAction += SendHolopadStartBroadcastMessage;
_window.SendHolopadActivateProjectorMessageAction += SendHolopadActivateProjectorMessage;
_window.SendHolopadRequestStationAiMessageAction += SendHolopadRequestStationAiMessage;
// If this call is addressed to an AI, open the window in the bottom right hand corner of the screen
if (uiKey == HolopadUiKey.AiRequestWindow)
_window.OpenCenteredAt(new Vector2(1f, 1f));
// Otherwise offset to the left so the holopad can still be seen
else
_window.OpenCenteredAt(new Vector2(0.3333f, 0.50f));
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (HolopadBoundInterfaceState)state;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
_window?.UpdateState(castState.Holopads);
}
public void SendHolopadStartNewCallMessage(NetEntity receiver)
{
SendMessage(new HolopadStartNewCallMessage(receiver));
}
public void SendHolopadAnswerCallMessage()
{
SendMessage(new HolopadAnswerCallMessage());
}
public void SendHolopadEndCallMessage()
{
SendMessage(new HolopadEndCallMessage());
}
public void SendHolopadStartBroadcastMessage()
{
SendMessage(new HolopadStartBroadcastMessage());
}
public void SendHolopadActivateProjectorMessage()
{
SendMessage(new HolopadActivateProjectorMessage());
}
public void SendHolopadRequestStationAiMessage()
{
SendMessage(new HolopadStationAiRequestMessage());
}
}

View File

@@ -1,172 +0,0 @@
using Content.Shared.Chat.TypingIndicator;
using Content.Shared.Holopad;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Linq;
namespace Content.Client.Holopad;
public sealed class HolopadSystem : SharedHolopadSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HolopadHologramComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<HolopadHologramComponent, BeforePostShaderRenderEvent>(OnShaderRender);
SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
SubscribeNetworkEvent<PlayerSpriteStateRequest>(OnPlayerSpriteStateRequest);
SubscribeNetworkEvent<PlayerSpriteStateMessage>(OnPlayerSpriteStateMessage);
}
private void OnComponentInit(EntityUid uid, HolopadHologramComponent component, ComponentInit ev)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
UpdateHologramSprite(uid);
}
private void OnShaderRender(EntityUid uid, HolopadHologramComponent component, BeforePostShaderRenderEvent ev)
{
if (ev.Sprite.PostShader == null)
return;
ev.Sprite.PostShader.SetParameter("t", (float)_timing.CurTime.TotalSeconds * component.ScrollRate);
}
private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)
{
var uid = args.SenderSession.AttachedEntity;
if (!Exists(uid))
return;
if (!HasComp<HolopadUserComponent>(uid))
return;
var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.IsTyping);
RaiseNetworkEvent(netEv);
}
private void OnPlayerSpriteStateRequest(PlayerSpriteStateRequest ev)
{
var targetPlayer = GetEntity(ev.TargetPlayer);
var player = _playerManager.LocalSession?.AttachedEntity;
// Ignore the request if received by a player who isn't the target
if (targetPlayer != player)
return;
if (!TryComp<SpriteComponent>(player, out var playerSprite))
return;
var spriteLayerData = new List<PrototypeLayerData>();
if (playerSprite.Visible)
{
// Record the RSI paths, state names and shader paramaters of all visible layers
for (int i = 0; i < playerSprite.AllLayers.Count(); i++)
{
if (!playerSprite.TryGetLayer(i, out var layer))
continue;
if (!layer.Visible ||
string.IsNullOrEmpty(layer.ActualRsi?.Path.ToString()) ||
string.IsNullOrEmpty(layer.State.Name))
continue;
var layerDatum = new PrototypeLayerData();
layerDatum.RsiPath = layer.ActualRsi.Path.ToString();
layerDatum.State = layer.State.Name;
if (layer.CopyToShaderParameters != null)
{
var key = (string)layer.CopyToShaderParameters.LayerKey;
if (playerSprite.LayerMapTryGet(key, out var otherLayerIdx) &&
playerSprite.TryGetLayer(otherLayerIdx, out var otherLayer) &&
otherLayer.Visible)
{
layerDatum.MapKeys = new() { key };
layerDatum.CopyToShaderParameters = new PrototypeCopyToShaderParameters()
{
LayerKey = key,
ParameterTexture = layer.CopyToShaderParameters.ParameterTexture,
ParameterUV = layer.CopyToShaderParameters.ParameterUV
};
}
}
spriteLayerData.Add(layerDatum);
}
}
// Return the recorded data to the server
var evResponse = new PlayerSpriteStateMessage(ev.TargetPlayer, spriteLayerData.ToArray());
RaiseNetworkEvent(evResponse);
}
private void OnPlayerSpriteStateMessage(PlayerSpriteStateMessage ev)
{
UpdateHologramSprite(GetEntity(ev.SpriteEntity), ev.SpriteLayerData);
}
private void UpdateHologramSprite(EntityUid uid, PrototypeLayerData[]? layerData = null)
{
if (!TryComp<SpriteComponent>(uid, out var hologramSprite))
return;
if (!TryComp<HolopadHologramComponent>(uid, out var holopadhologram))
return;
for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--)
hologramSprite.RemoveLayer(i);
if (layerData == null || layerData.Length == 0)
{
layerData = new PrototypeLayerData[1];
layerData[0] = new PrototypeLayerData()
{
RsiPath = holopadhologram.RsiPath,
State = holopadhologram.RsiState
};
}
for (int i = 0; i < layerData.Length; i++)
{
var layer = layerData[i];
layer.Shader = "unshaded";
hologramSprite.AddLayer(layerData[i], i);
}
UpdateHologramShader(uid, hologramSprite, holopadhologram);
}
private void UpdateHologramShader(EntityUid uid, SpriteComponent sprite, HolopadHologramComponent holopadHologram)
{
// Find the texture height of the largest layer
float texHeight = sprite.AllLayers.Max(x => x.PixelSize.Y);
var instance = _prototypeManager.Index<ShaderPrototype>(holopadHologram.ShaderName).InstanceUnique();
instance.SetParameter("color1", new Vector3(holopadHologram.Color1.R, holopadHologram.Color1.G, holopadHologram.Color1.B));
instance.SetParameter("color2", new Vector3(holopadHologram.Color2.R, holopadHologram.Color2.G, holopadHologram.Color2.B));
instance.SetParameter("alpha", holopadHologram.Alpha);
instance.SetParameter("intensity", holopadHologram.Intensity);
instance.SetParameter("texHeight", texHeight);
instance.SetParameter("t", (float)_timing.CurTime.TotalSeconds * holopadHologram.ScrollRate);
sprite.PostShader = instance;
sprite.RaiseShaderEvent = true;
}
}

View File

@@ -1,118 +0,0 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Resizable="False"
MaxSize="400 800"
MinSize="400 150">
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Name="ControlsLockOutContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False" Visible="False">
<!-- Header text -->
<controls:StripeBack>
<PanelContainer>
<RichTextLabel Name="EmergencyBroadcastText" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10 10 10 10" ReservesSpace="False"/>
</PanelContainer>
</controls:StripeBack>
<Label Text="{Loc 'holopad-window-controls-locked-out'}" HorizontalAlignment="Center" Margin="10 5 10 0" ReservesSpace="False"/>
<RichTextLabel Name="LockOutIdText" HorizontalAlignment="Center" Margin="10 5 10 0" ReservesSpace="False"/>
<Label Name="LockOutCountDownText" Text="{Loc 'holopad-window-controls-unlock-countdown'}" HorizontalAlignment="Center" Margin="10 15 10 10" ReservesSpace="False"/>
</BoxContainer>
<BoxContainer Name="ControlsContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False">
<!-- Active call controls (either this or the call placement controls will be active) -->
<BoxContainer Name="ActiveCallControlsContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False">
<!-- Header text -->
<BoxContainer MinHeight="60" Orientation="Vertical" VerticalAlignment="Center">
<Label Name="CallStatusText" Margin="10 5 10 0" ReservesSpace="False"/>
<BoxContainer Name="CallerIdContainer" Orientation="Vertical" ReservesSpace="False">
<RichTextLabel Name="CallerIdText" HorizontalAlignment="Center" Margin="0 0 0 0"/>
<Label Text="{Loc 'holopad-window-relay-label'}" Margin="10 10 10 0" ReservesSpace="False"/>
<RichTextLabel Name="HolopadIdText" HorizontalAlignment="Center" Margin="0 0 0 10"/>
</BoxContainer>
</BoxContainer>
<!-- Controls (the answer call button is absent when the phone is not ringing) -->
<GridContainer Columns="2" ReservesSpace="False">
<Control HorizontalExpand="True" Margin="10 0 2 5">
<Button Name="AnswerCallButton" Text="{Loc 'holopad-window-answer-call'}" StyleClasses="OpenRight" Margin="0 0 0 5" Disabled="True"/>
</Control>
<Control HorizontalExpand="True" Margin="2 0 10 5">
<Button Name="EndCallButton" Text="{Loc 'holopad-window-end-call'}" StyleClasses="OpenLeft" Margin="0 0 0 5" Disabled="True"/>
</Control>
</GridContainer>
</BoxContainer>
<!-- Call placement controls (either this or the active call controls will be active) -->
<BoxContainer Name="CallPlacementControlsContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False">
<controls:StripeBack>
<PanelContainer>
<BoxContainer Orientation="Vertical">
<RichTextLabel Name="SubtitleText" HorizontalAlignment="Center" Margin="0 5 0 0"/>
<RichTextLabel Name="OptionsText" HorizontalAlignment="Center" Margin="0 0 0 5"/>
</BoxContainer>
</PanelContainer>
</controls:StripeBack>
<!-- Request the station AI or activate the holopad projector (only one of these should be active at a time) -->
<BoxContainer Name="RequestStationAiContainer" Orientation="Vertical" ReservesSpace="False" Visible="False">
<Button Name="RequestStationAiButton" Text="{Loc 'holopad-window-request-station-ai'}" Margin="10 5 10 5" Disabled="False"/>
</BoxContainer>
<BoxContainer Name="ActivateProjectorContainer" Orientation="Vertical" ReservesSpace="False" Visible="False">
<Button Name="ActivateProjectorButton" Text="{Loc 'holopad-window-activate-projector'}" Margin="10 5 10 5" Disabled="False"/>
</BoxContainer>
<!-- List of contactable holopads (the list is created in C#) -->
<BoxContainer Name="HolopadContactListContainer" Orientation="Vertical" Margin="10 0 10 5" ReservesSpace="False" Visible="False">
<PanelContainer Name="HolopadContactListHeaderPanel">
<Label Text="{Loc 'holopad-window-select-contact-from-list'}" HorizontalAlignment="Center" Margin="0 3 0 3"/>
</PanelContainer>
<PanelContainer Name="HolopadContactListPanel">
<BoxContainer Orientation="Vertical">
<!-- Contact filter -->
<LineEdit Name="SearchLineEdit" HorizontalExpand="True" Margin="4, 4, 4, 0"
PlaceHolder="{Loc holopad-window-filter-line-placeholder}" />
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" Margin="8, 8, 8, 8" MinHeight="256">
<!-- If there is no data yet, this will be displayed -->
<BoxContainer Name="FetchingAvailableHolopadsContainer" HorizontalAlignment="Center" HorizontalExpand="True" VerticalExpand="True" ReservesSpace="False">
<Label Text="{Loc 'holopad-window-fetching-contacts-list'}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</BoxContainer>
<!-- Container for the contacts -->
<BoxContainer Name="ContactsList" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="10 0 10 0"/>
</ScrollContainer>
</BoxContainer>
</PanelContainer>
</BoxContainer>
<!-- Button to start an emergency broadcast (the user requires a certain level of access to interact with it) -->
<BoxContainer Name="StartBroadcastContainer" Orientation="Vertical" ReservesSpace="False" Visible="False">
<Button Name="StartBroadcastButton" Text="{Loc 'holopad-window-emergency-broadcast'}" Margin="10 0 10 5" Disabled="False" ReservesSpace="False"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Footer -->
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc 'holopad-window-flavor-left'}" StyleClasses="WindowFooterText" />
<Label Text="{Loc 'holopad-window-flavor-right'}" StyleClasses="WindowFooterText"
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -1,344 +0,0 @@
using Content.Client.Popups;
using Content.Client.UserInterface.Controls;
using Content.Shared.Access.Systems;
using Content.Shared.Holopad;
using Content.Shared.Telephone;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Linq;
namespace Content.Client.Holopad;
[GenerateTypedNameReferences]
public sealed partial class HolopadWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private readonly SharedHolopadSystem _holopadSystem = default!;
private readonly SharedTelephoneSystem _telephoneSystem = default!;
private readonly AccessReaderSystem _accessReaderSystem = default!;
private readonly PopupSystem _popupSystem = default!;
private EntityUid? _owner = null;
private HolopadUiKey _currentUiKey;
private TelephoneState _currentState;
private TelephoneState _previousState;
private TimeSpan _buttonUnlockTime;
private float _updateTimer = 0.25f;
private const float UpdateTime = 0.25f;
private TimeSpan _buttonUnlockDelay = TimeSpan.FromSeconds(0.5f);
public event Action<NetEntity>? SendHolopadStartNewCallMessageAction;
public event Action? SendHolopadAnswerCallMessageAction;
public event Action? SendHolopadEndCallMessageAction;
public event Action? SendHolopadStartBroadcastMessageAction;
public event Action? SendHolopadActivateProjectorMessageAction;
public event Action? SendHolopadRequestStationAiMessageAction;
public HolopadWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_holopadSystem = _entManager.System<SharedHolopadSystem>();
_telephoneSystem = _entManager.System<SharedTelephoneSystem>();
_accessReaderSystem = _entManager.System<AccessReaderSystem>();
_popupSystem = _entManager.System<PopupSystem>();
_buttonUnlockTime = _timing.CurTime + _buttonUnlockDelay;
// Assign button actions
AnswerCallButton.OnPressed += args => { OnHolopadAnswerCallMessage(); };
EndCallButton.OnPressed += args => { OnHolopadEndCallMessage(); };
StartBroadcastButton.OnPressed += args => { OnHolopadStartBroadcastMessage(); };
ActivateProjectorButton.OnPressed += args => { OnHolopadActivateProjectorMessage(); };
RequestStationAiButton.OnPressed += args => { OnHolopadRequestStationAiMessage(); };
// XML formatting
AnswerCallButton.AddStyleClass("ButtonAccept");
EndCallButton.AddStyleClass("Caution");
StartBroadcastButton.AddStyleClass("Caution");
HolopadContactListPanel.PanelOverride = new StyleBoxFlat
{
BackgroundColor = new Color(47, 47, 59) * Color.DarkGray,
BorderColor = new Color(82, 82, 82), //new Color(70, 73, 102),
BorderThickness = new Thickness(2),
};
HolopadContactListHeaderPanel.PanelOverride = new StyleBoxFlat
{
BackgroundColor = new Color(82, 82, 82),
};
EmergencyBroadcastText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-emergency-broadcast-in-progress")));
SubtitleText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-subtitle")));
OptionsText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-options")));
}
#region: Button actions
private void OnSendHolopadStartNewCallMessage(NetEntity receiver)
{
SendHolopadStartNewCallMessageAction?.Invoke(receiver);
}
private void OnHolopadAnswerCallMessage()
{
SendHolopadAnswerCallMessageAction?.Invoke();
}
private void OnHolopadEndCallMessage()
{
SendHolopadEndCallMessageAction?.Invoke();
if (_currentUiKey == HolopadUiKey.AiRequestWindow)
Close();
}
private void OnHolopadStartBroadcastMessage()
{
if (_playerManager.LocalSession?.AttachedEntity == null || _owner == null)
return;
var player = _playerManager.LocalSession.AttachedEntity;
if (!_accessReaderSystem.IsAllowed(player.Value, _owner.Value))
{
_popupSystem.PopupClient(Loc.GetString("holopad-window-access-denied"), _owner.Value, player.Value);
return;
}
SendHolopadStartBroadcastMessageAction?.Invoke();
}
private void OnHolopadActivateProjectorMessage()
{
SendHolopadActivateProjectorMessageAction?.Invoke();
}
private void OnHolopadRequestStationAiMessage()
{
SendHolopadRequestStationAiMessageAction?.Invoke();
}
#endregion
public void SetState(EntityUid owner, HolopadUiKey uiKey)
{
_owner = owner;
_currentUiKey = uiKey;
// Determines what UI containers are available to the user.
// Components of these will be toggled on and off when
// UpdateAppearance() is called
switch (uiKey)
{
case HolopadUiKey.InteractionWindow:
RequestStationAiContainer.Visible = true;
HolopadContactListContainer.Visible = true;
StartBroadcastContainer.Visible = true;
break;
case HolopadUiKey.InteractionWindowForAi:
ActivateProjectorContainer.Visible = true;
StartBroadcastContainer.Visible = true;
break;
case HolopadUiKey.AiActionWindow:
HolopadContactListContainer.Visible = true;
StartBroadcastContainer.Visible = true;
break;
case HolopadUiKey.AiRequestWindow:
break;
}
}
public void UpdateState(Dictionary<NetEntity, string> holopads)
{
if (_owner == null || !_entManager.TryGetComponent<TelephoneComponent>(_owner.Value, out var telephone))
return;
// Caller ID text
var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId.Item1, telephone.LastCallerId.Item2, Color.LightGray, "Default", 11);
var holoapdId = _telephoneSystem.GetFormattedDeviceIdForEntity(telephone.LastCallerId.Item3, Color.LightGray, "Default", 11);
CallerIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId));
HolopadIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(holoapdId));
LockOutIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId));
// Sort holopads alphabetically
var holopadArray = holopads.ToArray();
Array.Sort(holopadArray, AlphabeticalSort);
// Clear excess children from the contact list
while (ContactsList.ChildCount > holopadArray.Length)
ContactsList.RemoveChild(ContactsList.GetChild(ContactsList.ChildCount - 1));
// Make / update required children
for (int i = 0; i < holopadArray.Length; i++)
{
var (netEntity, label) = holopadArray[i];
if (i >= ContactsList.ChildCount)
{
var newContactButton = new HolopadContactButton();
newContactButton.OnPressed += args => { OnSendHolopadStartNewCallMessage(newContactButton.NetEntity); };
ContactsList.AddChild(newContactButton);
}
var child = ContactsList.GetChild(i);
if (child is not HolopadContactButton)
continue;
var contactButton = (HolopadContactButton)child;
contactButton.UpdateValues(netEntity, label);
}
// Update buttons
UpdateAppearance();
}
private void UpdateAppearance()
{
if (_owner == null || !_entManager.TryGetComponent<TelephoneComponent>(_owner.Value, out var telephone))
return;
if (_owner == null || !_entManager.TryGetComponent<HolopadComponent>(_owner.Value, out var holopad))
return;
var hasBroadcastAccess = !_holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad));
var localPlayer = _playerManager.LocalSession?.AttachedEntity;
ControlsLockOutContainer.Visible = _holopadSystem.IsHolopadControlLocked((_owner.Value, holopad), localPlayer);
ControlsContainer.Visible = !ControlsLockOutContainer.Visible;
// Temporarily disable the interface buttons when the call state changes to prevent any misclicks
if (_currentState != telephone.CurrentState)
{
_previousState = _currentState;
_currentState = telephone.CurrentState;
_buttonUnlockTime = _timing.CurTime + _buttonUnlockDelay;
}
var lockButtons = _timing.CurTime < _buttonUnlockTime;
// Make / update required children
foreach (var child in ContactsList.Children)
{
if (child is not HolopadContactButton contactButton)
continue;
var passesFilter = string.IsNullOrEmpty(SearchLineEdit.Text) ||
contactButton.Text?.Contains(SearchLineEdit.Text, StringComparison.CurrentCultureIgnoreCase) == true;
contactButton.Visible = passesFilter;
contactButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
}
// Update control text
var cooldown = _holopadSystem.GetHolopadBroadcastCoolDown((_owner.Value, holopad));
var cooldownString = $"{cooldown.Minutes:00}:{cooldown.Seconds:00}";
StartBroadcastButton.Text = _holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad)) ?
Loc.GetString("holopad-window-emergency-broadcast-with-countdown", ("countdown", cooldownString)) :
Loc.GetString("holopad-window-emergency-broadcast");
var lockout = _holopadSystem.GetHolopadControlLockedPeriod((_owner.Value, holopad));
var lockoutString = $"{lockout.Minutes:00}:{lockout.Seconds:00}";
LockOutCountDownText.Text = Loc.GetString("holopad-window-controls-unlock-countdown", ("countdown", lockoutString));
switch (_currentState)
{
case TelephoneState.Idle:
CallStatusText.Text = Loc.GetString("holopad-window-no-calls-in-progress"); break;
case TelephoneState.Calling:
CallStatusText.Text = Loc.GetString("holopad-window-outgoing-call"); break;
case TelephoneState.Ringing:
CallStatusText.Text = (_currentUiKey == HolopadUiKey.AiRequestWindow) ?
Loc.GetString("holopad-window-ai-request") : Loc.GetString("holopad-window-incoming-call"); break;
case TelephoneState.InCall:
CallStatusText.Text = Loc.GetString("holopad-window-call-in-progress"); break;
case TelephoneState.EndingCall:
if (_previousState == TelephoneState.Calling || _previousState == TelephoneState.Idle)
CallStatusText.Text = Loc.GetString("holopad-window-call-rejected");
else
CallStatusText.Text = Loc.GetString("holopad-window-call-ending");
break;
}
// Update control disability
AnswerCallButton.Disabled = (_currentState != TelephoneState.Ringing || lockButtons);
EndCallButton.Disabled = (_currentState == TelephoneState.Idle || _currentState == TelephoneState.EndingCall || lockButtons);
StartBroadcastButton.Disabled = (_currentState != TelephoneState.Idle || !hasBroadcastAccess || lockButtons);
RequestStationAiButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
ActivateProjectorButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
// Update control visibility
FetchingAvailableHolopadsContainer.Visible = (ContactsList.ChildCount == 0);
ActiveCallControlsContainer.Visible = (_currentState != TelephoneState.Idle || _currentUiKey == HolopadUiKey.AiRequestWindow);
CallPlacementControlsContainer.Visible = !ActiveCallControlsContainer.Visible;
CallerIdContainer.Visible = (_currentState == TelephoneState.Ringing);
AnswerCallButton.Visible = (_currentState == TelephoneState.Ringing);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
_updateTimer += args.DeltaSeconds;
if (_updateTimer >= UpdateTime)
{
_updateTimer -= UpdateTime;
UpdateAppearance();
}
}
private sealed class HolopadContactButton : Button
{
public NetEntity NetEntity;
public HolopadContactButton()
{
HorizontalExpand = true;
SetHeight = 32;
Margin = new Thickness(0f, 1f, 0f, 1f);
ReservesSpace = false;
}
public void UpdateValues(NetEntity netEntity, string label)
{
NetEntity = netEntity;
Text = Loc.GetString("holopad-window-contact-label", ("label", label));
}
}
private int AlphabeticalSort(KeyValuePair<NetEntity, string> x, KeyValuePair<NetEntity, string> y)
{
if (string.IsNullOrEmpty(x.Value))
return -1;
if (string.IsNullOrEmpty(y.Value))
return 1;
return x.Value.CompareTo(y.Value);
}
}

View File

@@ -34,7 +34,6 @@ namespace Content.Client.Info
AddInfoButton("server-info-website-button", CCVars.InfoLinksWebsite); AddInfoButton("server-info-website-button", CCVars.InfoLinksWebsite);
AddInfoButton("server-info-wiki-button", CCVars.InfoLinksWiki); AddInfoButton("server-info-wiki-button", CCVars.InfoLinksWiki);
AddInfoButton("server-info-forum-button", CCVars.InfoLinksForum); AddInfoButton("server-info-forum-button", CCVars.InfoLinksForum);
AddInfoButton("server-info-telegram-button", CCVars.InfoLinksTelegram);
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>(); var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
var guidebookButton = new Button() { Text = Loc.GetString("server-info-guidebook-button") }; var guidebookButton = new Button() { Text = Loc.GetString("server-info-guidebook-button") };

View File

@@ -123,10 +123,6 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.OpenDecalSpawnWindow); common.AddFunction(ContentKeyFunctions.OpenDecalSpawnWindow);
common.AddFunction(ContentKeyFunctions.OpenAdminMenu); common.AddFunction(ContentKeyFunctions.OpenAdminMenu);
common.AddFunction(ContentKeyFunctions.OpenGuidebook); common.AddFunction(ContentKeyFunctions.OpenGuidebook);
//CP14 Keys
human.AddFunction(ContentKeyFunctions.CP14OpenKnowledgeMenu);
//CP14 Keys end
} }
} }
} }

View File

@@ -235,23 +235,9 @@ namespace Content.Client.Inventory
EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true)); EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true));
} }
protected override void UpdateInventoryTemplate(Entity<InventoryComponent> ent)
{
base.UpdateInventoryTemplate(ent);
if (TryComp(ent, out InventorySlotsComponent? inventorySlots))
{
foreach (var slot in ent.Comp.Slots)
{
if (inventorySlots.SlotData.TryGetValue(slot.Name, out var slotData))
slotData.SlotDef = slot;
}
}
}
public sealed class SlotData public sealed class SlotData
{ {
public SlotDefinition SlotDef; public readonly SlotDefinition SlotDef;
public EntityUid? HeldEntity => Container?.ContainedEntity; public EntityUid? HeldEntity => Container?.ContainedEntity;
public bool Blocked; public bool Blocked;
public bool Highlighted; public bool Highlighted;

View File

@@ -17,7 +17,6 @@ using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Strip.Components; using Content.Shared.Strip.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input; using Robust.Shared.Input;
@@ -30,13 +29,10 @@ namespace Content.Client.Inventory
[UsedImplicitly] [UsedImplicitly]
public sealed class StrippableBoundUserInterface : BoundUserInterface public sealed class StrippableBoundUserInterface : BoundUserInterface
{ {
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!; [Dependency] private readonly IUserInterfaceManager _ui = default!;
private readonly ExamineSystem _examine; private readonly ExamineSystem _examine;
private readonly InventorySystem _inv; private readonly InventorySystem _inv;
private readonly SharedCuffableSystem _cuffable; private readonly SharedCuffableSystem _cuffable;
private readonly StrippableSystem _strippable;
[ViewVariables] [ViewVariables]
private const int ButtonSeparation = 4; private const int ButtonSeparation = 4;
@@ -55,8 +51,6 @@ namespace Content.Client.Inventory
_examine = EntMan.System<ExamineSystem>(); _examine = EntMan.System<ExamineSystem>();
_inv = EntMan.System<InventorySystem>(); _inv = EntMan.System<InventorySystem>();
_cuffable = EntMan.System<SharedCuffableSystem>(); _cuffable = EntMan.System<SharedCuffableSystem>();
_strippable = EntMan.System<StrippableSystem>();
_virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace); _virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace);
} }
@@ -191,15 +185,9 @@ namespace Content.Client.Inventory
return; return;
if (ev.Function == ContentKeyFunctions.ExamineEntity) if (ev.Function == ContentKeyFunctions.ExamineEntity)
{
_examine.DoExamine(slot.Entity.Value); _examine.DoExamine(slot.Entity.Value);
ev.Handle();
}
else if (ev.Function == EngineKeyFunctions.UseSecondary) else if (ev.Function == EngineKeyFunctions.UseSecondary)
{
_ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(slot.Entity.Value); _ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(slot.Entity.Value);
ev.Handle();
}
} }
private void AddInventoryButton(EntityUid invUid, string slotId, InventoryComponent inv) private void AddInventoryButton(EntityUid invUid, string slotId, InventoryComponent inv)
@@ -210,8 +198,7 @@ namespace Content.Client.Inventory
var entity = container.ContainedEntity; var entity = container.ContainedEntity;
// If this is a full pocket, obscure the real entity // If this is a full pocket, obscure the real entity
// this does not work for modified clients because they are still sent the real entity if (entity != null && slotDef.StripHidden)
if (entity != null && _strippable.IsStripHidden(slotDef, _player.LocalEntity))
entity = _virtualHiddenEntity; entity = _virtualHiddenEntity;
var button = new SlotButton(new SlotData(slotDef, container)); var button = new SlotButton(new SlotData(slotDef, container));

View File

@@ -21,22 +21,6 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
SubscribeLocalEvent<HandheldLightComponent, AppearanceChangeEvent>(OnAppearanceChange); SubscribeLocalEvent<HandheldLightComponent, AppearanceChangeEvent>(OnAppearanceChange);
} }
/// <remarks>
/// TODO: Not properly predicted yet. Don't call this function if you want a the actual return value!
/// </remarks>
public override bool TurnOff(Entity<HandheldLightComponent> ent, bool makeNoise = true)
{
return true;
}
/// <remarks>
/// TODO: Not properly predicted yet. Don't call this function if you want a the actual return value!
/// </remarks>
public override bool TurnOn(EntityUid user, Entity<HandheldLightComponent> uid)
{
return true;
}
private void OnAppearanceChange(EntityUid uid, HandheldLightComponent? component, ref AppearanceChangeEvent args) private void OnAppearanceChange(EntityUid uid, HandheldLightComponent? component, ref AppearanceChangeEvent args)
{ {
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))

View File

@@ -5,21 +5,19 @@ using Content.Client.Lobby.UI;
using Content.Client.Message; using Content.Client.Message;
using Content.Client.UserInterface.Systems.Chat; using Content.Client.UserInterface.Systems.Chat;
using Content.Client.Voting; using Content.Client.Voting;
using Content.Shared.CCVar;
using Robust.Client; using Robust.Client;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Configuration;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Client.Lobby namespace Content.Client.Lobby
{ {
public sealed class LobbyState : Robust.Client.State.State public sealed class LobbyState : Robust.Client.State.State
{ {
[Dependency] private readonly IBaseClient _baseClient = default!; [Dependency] private readonly IBaseClient _baseClient = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!; [Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IResourceCache _resourceCache = default!;
@@ -51,17 +49,7 @@ namespace Content.Client.Lobby
_voteManager.SetPopupContainer(Lobby.VoteContainer); _voteManager.SetPopupContainer(Lobby.VoteContainer);
LayoutContainer.SetAnchorPreset(Lobby, LayoutContainer.LayoutPreset.Wide); LayoutContainer.SetAnchorPreset(Lobby, LayoutContainer.LayoutPreset.Wide);
Lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you...
var lobbyNameCvar = _cfg.GetCVar(CCVars.ServerLobbyName);
var serverName = _baseClient.GameInfo?.ServerName ?? string.Empty;
Lobby.ServerName.Text = string.IsNullOrEmpty(lobbyNameCvar)
? Loc.GetString("ui-lobby-title", ("serverName", serverName))
: lobbyNameCvar;
var width = _cfg.GetCVar(CCVars.ServerLobbyRightPanelWidth);
Lobby.RightSide.SetWidth = width;
UpdateLobbyUi(); UpdateLobbyUi();
Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed; Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed;
@@ -128,7 +116,7 @@ namespace Content.Client.Lobby
return; return;
} }
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
string text; string text;
if (_gameTicker.Paused) if (_gameTicker.Paused)
@@ -148,10 +136,6 @@ namespace Content.Client.Lobby
{ {
text = Loc.GetString(seconds < -5 ? "lobby-state-right-now-question" : "lobby-state-right-now-confirmation"); text = Loc.GetString(seconds < -5 ? "lobby-state-right-now-question" : "lobby-state-right-now-confirmation");
} }
else if (difference.TotalHours >= 1)
{
text = $"{Math.Floor(difference.TotalHours)}:{difference.Minutes:D2}:{difference.Seconds:D2}";
}
else else
{ {
text = $"{difference.Minutes}:{difference.Seconds:D2}"; text = $"{difference.Minutes}:{difference.Seconds:D2}";

View File

@@ -279,7 +279,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
_profileEditor.OnOpenGuidebook += _guide.OpenHelp; _profileEditor.OnOpenGuidebook += _guide.OpenHelp;
_characterSetup = new CharacterSetupGui(_profileEditor); _characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor);
_characterSetup.CloseButton.OnPressed += _ => _characterSetup.CloseButton.OnPressed += _ =>
{ {
@@ -455,21 +455,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
{ {
EntityUid dummyEnt; EntityUid dummyEnt;
EntProtoId? previewEntity = null; if (humanoid is not null)
if (humanoid != null && jobClothes)
{
job ??= GetPreferredJob(humanoid);
previewEntity = job.JobPreviewEntity ?? (EntProtoId?)job?.JobEntity;
}
if (previewEntity != null)
{
// Special type like borg or AI, do not spawn a human just spawn the entity.
dummyEnt = EntityManager.SpawnEntity(previewEntity, MapCoordinates.Nullspace);
return dummyEnt;
}
else if (humanoid is not null)
{ {
var dummy = _prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype; var dummy = _prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace); dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
@@ -483,8 +469,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
if (humanoid != null && jobClothes) if (humanoid != null && jobClothes)
{ {
DebugTools.Assert(job != null); job ??= GetPreferredJob(humanoid);
GiveDummyJobClothes(dummyEnt, humanoid, job); GiveDummyJobClothes(dummyEnt, humanoid, job);
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID))) if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))

View File

@@ -6,7 +6,6 @@
SeparationOverride="0" SeparationOverride="0"
Name="InternalHBox"> Name="InternalHBox">
<SpriteView Scale="2 2" <SpriteView Scale="2 2"
Margin="0 4 4 4"
OverrideDirection="South" OverrideDirection="South"
Name="View"/> Name="View"/>
<Label Name="DescriptionLabel" <Label Name="DescriptionLabel"

Some files were not shown because too many files have changed in this diff Show More