mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 23:57:29 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37e44ee3ee | ||
|
|
fc28b60175 |
+1
-1
@@ -351,7 +351,7 @@ resharper_csharp_qualified_using_at_nested_scope = false
|
||||
resharper_csharp_prefer_qualified_reference = false
|
||||
resharper_csharp_allow_alias = false
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props,slnx}]
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
[nuget.config]
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
/Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers
|
||||
|
||||
# Server rules
|
||||
/Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators
|
||||
/Resources/ServerInfo/Guidebook/DeltaV/Rules/ @DeltaV-Station/head-administrators
|
||||
|
||||
# Wiki
|
||||
/.wiki/ @DeltaV-Station/direction
|
||||
|
||||
@@ -10,28 +10,22 @@
|
||||
<!-- Summary of code changes for easier review. -->
|
||||
|
||||
## Media
|
||||
<!-- Attach media if the PR makes in-game changes (clothing, items, features, etc).
|
||||
<!-- Attach media if the PR makes ingame changes (clothing, items, features, etc).
|
||||
Small fixes/refactors are exempt. -->
|
||||
|
||||
## Requirements
|
||||
<!-- Confirm the following by placing an X in the brackets without spaces inside (for example: [X] ): -->
|
||||
<!-- Confirm the following by placing an X in the brackets: [X] -->
|
||||
- [ ] I have tested all added content and changes.
|
||||
- [ ] I have added media to this PR or it does not require an in-game showcase.
|
||||
- [ ] I have added media to this PR or it does not require an ingame showcase.
|
||||
<!-- You should understand that not following the above may get your PR closed at maintainer’s discretion -->
|
||||
|
||||
## Licensing
|
||||
<!-- This is for the benefit of other forks wishing to port features from DeltaV
|
||||
Our repository is AGPL, meaning projects using the MIT license would have to ask for permission from the PR author (ideally that's you, reading this) before being allowed to port it over
|
||||
This just saves them the trouble. Note that AGPL or MIT licensing is only applicable to SOFTWARE, and that assets such as textures and audio have their own licensing scheme that is defined in the codebase itself. -->
|
||||
- [ ] I confirm that I am the creator of the code in this PR, and allow licensing it under the following license(s), or that the original author(s) has given me permission to do so:
|
||||
- [X] AGPL (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-AGPLv3.txt) <!-- This one is required to contribute to DeltaV -->
|
||||
- [X] MIT (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-MIT.txt) <!-- Optional - A lot of other SS14 forks use MIT -->
|
||||
<!-- Feel free to add more licenses as you see fit -->
|
||||
## Breaking changes
|
||||
<!-- List any breaking changes, including namespaces, public class/method/field changes, prototype renames; and provide instructions for fixing them. -->
|
||||
|
||||
**Changelog**
|
||||
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
|
||||
Make sure to read the guidelines and take this Changelog template out of the comment block in order for it to show up.
|
||||
Changelog must have a :cl: symbol, so the bot recognizes the changes and adds them to the game's changelog. -->
|
||||
Changelogs must have a :cl: symbol, so the bot recognizes the changes and adds them to the game's changelog. -->
|
||||
<!--
|
||||
:cl:
|
||||
- add: Added fun!
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Build & Test Debug
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [review_requested, opened]
|
||||
types: [review_requested]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request_target'
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
@@ -14,3 +22,24 @@ jobs:
|
||||
- uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: "S: Awaiting Changes"
|
||||
|
||||
# Doesn't work on prs from foreign repos currently
|
||||
#handle_review:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event_name == 'pull_request_review'
|
||||
# steps:
|
||||
# - uses: actions-ecosystem/action-add-labels@v1
|
||||
# if: |
|
||||
# github.event.review.state == 'changes_requested' &&
|
||||
# (github.event.review.author_association == 'OWNER' ||
|
||||
# github.event.review.author_association == 'MEMBER')
|
||||
# with:
|
||||
# labels: "S: Awaiting Changes"
|
||||
#
|
||||
# - uses: actions-ecosystem/action-remove-labels@v1
|
||||
# if: |
|
||||
# github.event.review.state == 'changes_requested' &&
|
||||
# (github.event.review.author_association == 'OWNER' ||
|
||||
# github.event.review.author_association == 'MEMBER')
|
||||
# with:
|
||||
# labels: "S: Needs Review"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# The PAT does not have the correct permissions to make this work; Eventually we'll want to replace this workflow with a proper bot
|
||||
# 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 == 'DeltaV-Station/Delta-v') && (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: "maintainers"
|
||||
# GITHUB_TOKEN: ${{ secrets.LABELER_PAT }}
|
||||
# - if: ${{ steps.checkUserMember.outputs.isTeamMember == 'true' }}
|
||||
# uses: actions-ecosystem/action-add-labels@v1
|
||||
# with:
|
||||
# labels: "S: Approved"
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, trying ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
@@ -34,19 +34,17 @@ jobs:
|
||||
# Uncomment this and comment the other line if you do this.
|
||||
# https://github.com/stefanzweifel/git-auto-commit-action#push-to-protected-branches
|
||||
|
||||
# - name: Commit new credit files
|
||||
# uses: stefanzweifel/git-auto-commit-action@v7
|
||||
# with:
|
||||
# commit_message: Update Credits
|
||||
# commit_author: DeltaV-Bot <github@deltav.gay>
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||
#- name: Commit new credit files
|
||||
# uses: stefanzweifel/git-auto-commit-action@v4
|
||||
# with:
|
||||
# commit_message: Update Credits
|
||||
# commit_author: PJBot <pieterjan.briers+bot@gmail.com>
|
||||
|
||||
# This will make a PR
|
||||
- name: Set current date as env variable
|
||||
run: echo "NOW=$(date +'%Y-%m-%dT%H-%M-%S')" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
commit-message: Update Credits
|
||||
@@ -54,14 +52,3 @@ jobs:
|
||||
body: This is an automated Pull Request. This PR updates the github contributors in the credits section.
|
||||
author: DeltaV-Bot <github@deltav.gay>
|
||||
branch: automated/credits-${{env.NOW}}
|
||||
|
||||
- name: Auto-merge PR with bypass
|
||||
if: steps.cpr.outputs.pull-request-number != ''
|
||||
env:
|
||||
GH_TOKEN: ${{secrets.CREDITS_TOKEN}}
|
||||
run: |
|
||||
gh pr merge ${{ steps.cpr.outputs.pull-request-number }} \
|
||||
--squash \
|
||||
--admin \
|
||||
--delete-branch \
|
||||
--repo ${{ github.repository }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: RGA schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
@@ -2,7 +2,7 @@ name: RSI Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
paths:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Map file schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
@@ -2,7 +2,7 @@ name: YAML Linter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, trying ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 9.0.x
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# MSbuild binlog files
|
||||
*.binlog
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
|
||||
@@ -289,33 +289,4 @@ Emergency alert status. ''Central Command has ordered the Epsilon security level
|
||||
| style="border: 1px solid #000000;" | Martial Law is in effect. Do not engage with Central Command Personnel. Await orders from Central Command Personnel.
|
||||
|}
|
||||
|
||||
== <span style="color:#ff8119;>Orange Alert</span> ==
|
||||
Emergency alert status. ''There is a critical station-wide structural or atmospheric threat and recovery is unlikely. Engineering staff are advised to minimize hazards and secure the Evacuation Dock. Crewmembers are advised to stay away from hazardous areas, and prepare for Evacuation.''
|
||||
|
||||
{| class="wikitable" style="width: 80%; border: 1px solid #000000;"
|
||||
!style="text-align:left; width:25%; padding:5px; padding-left:10px; background-color:#FF8119BF; border: 1px solid #000000;" | '''Condition'''
|
||||
!style="text-align:left; width:75%; padding:5px; padding-left:10px; background-color:#FF8119BF; border: 1px solid #000000;" | '''Description'''
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Chain of Command
|
||||
| style="border: 1px solid #000000;" | [[Standard_Operating_Procedure#Captain’s_Authority|CO]] > [[Chief Engineer]] > Engineering Personnel > Department Heads > Supervisory Roles
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Armoury Policy
|
||||
| style="border: 1px solid #000000;" | Issuing of lethal weapons disadvised. EVA protection heavily advised for all personnel.
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Discipline
|
||||
| style="border: 1px solid #000000;" | Report to supervisor for orders. Non-engineering personnel advised to evacuate affected areas. Security should seek to aid engineering staff where possible.
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Secure Areas
|
||||
| style="border: 1px solid #000000;" | Secure areas unbolted. [[Standard_Operating_Procedure#High_Security_Areas|HSAs]] and areas with an expectation of privacy, such as bedrooms, dorms, or medical treatment facilities, may be bolted as needed.
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Medical
|
||||
| style="border: 1px solid #000000;" | Full suit sensors heavily advised. EVA suits and functioning internals recommended for emergency responders.
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Engineering
|
||||
| style="border: 1px solid #000000;" | EVA suits heavily advised for engineers. Distribute emergency internals to crew. Ensure evacuation as soon as possible.
|
||||
|-
|
||||
| style="border: 1px solid #000000;" | Rules of Engagement
|
||||
| style="border: 1px solid #000000;" | Engage with minimal force required; prioritize de-escalation whenever possible.
|
||||
|}
|
||||
|
||||
{{Guides Menu}}
|
||||
|
||||
+22
-22
@@ -204,49 +204,49 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
|
||||
|style="border:1px solid black;" | 301
|
||||
|style= "border:1px solid black;"| [[File:SL_Manslaughter.png]]
|
||||
|style= "border:1px solid black;"| Manslaughter
|
||||
|style= "border:1px solid black;"| 10 minutes
|
||||
|style= "border:1px solid black;"| 16 minutes
|
||||
|style= "border:1px solid black;"| To act without malice in a manner which, directly or indirectly, leads to the death of an Employee.
|
||||
|-
|
||||
|style= "border:1px solid black;"| 302
|
||||
|style= "border:1px solid black;"| [[File:SL_Kidnapping.png]]
|
||||
|style= "border:1px solid black;"| Kidnapping
|
||||
|style= "border:1px solid black;"| 10 minutes
|
||||
|style= "border:1px solid black;"| 16 minutes
|
||||
|style= "border:1px solid black;"| To unlawfully confine or restrict the free movement of an Employee against their will.
|
||||
|-
|
||||
| style="border:1px solid black;" | 303
|
||||
| style="border:1px solid black;" | WIP
|
||||
| style="border:1px solid black;" | Grand Possession
|
||||
| style="border:1px solid black;" | 12-20 minutes
|
||||
| style="border:1px solid black;" | 15-25 minutes
|
||||
| style="border:1px solid black;" | To be in the possession of a Category C, D, or E Controlled Armament.
|
||||
|-
|
||||
| style="border:1px solid black;" | 304
|
||||
| style="border:1px solid black;" | [[File:SL_Mindbreaking.png]]
|
||||
| style="border:1px solid black;" | Noöspheric Tampering
|
||||
| style="border:1px solid black;" | 8 minutes
|
||||
| style="border:1px solid black;" | 14 minutes
|
||||
| style="border:1px solid black;" | To maliciously tamper with or create noöspheric anomalies, entities, aberrations, or other elements; Or to otherwise deliberately perform actions that would raise Glimmer or other anomalous reading levels in a way that threatens normative reality.
|
||||
|-
|
||||
| style="border:1px solid black;" | 305
|
||||
| style="border:1px solid black;" | [[File:SL_Sabotage.png]]
|
||||
| style="border:1px solid black;" | Sabotage
|
||||
| style="border:1px solid black;" | 8 minutes
|
||||
| style="border:1px solid black;" | 14 minutes
|
||||
| style="border:1px solid black;" | To maliciously commit an act that, directly or indirectly, hinders the operation of a vessel of its part; or, to commit an act that modifies and/or damages technology or equipment one is not authorized to access.
|
||||
|-
|
||||
| style="border:1px solid black;" | 306
|
||||
| style="border:1px solid black;" | [[File:SL_AbuseOfPower.png]]
|
||||
| style="border:1px solid black;" | Abuse of Power
|
||||
| style="border:1px solid black;" | 10 minutes
|
||||
| style="border:1px solid black;" | 14 minutes
|
||||
| style="border:1px solid black;" | To intentionally misuse or wrongfully exercise one’s own authority, influence, or control, resulting in harm, unjust treatment, or demonstratable loss to another Employee.
|
||||
|-
|
||||
| style="border:1px solid black;" | 307
|
||||
| style="border:1px solid black;" | [[File:SL_GrandTheft.png]]
|
||||
| style="border:1px solid black;" | Grand Larceny
|
||||
| style="border:1px solid black;" | 8 minutes
|
||||
| style="border:1px solid black;" | 12 minutes
|
||||
| style="border:1px solid black;" | To deprive an Employee of any Controlled Item in their lawful possession.
|
||||
|-
|
||||
| style="border:1px solid black;" | 308
|
||||
| style="border:1px solid black;" | [[File:SL_BlackMarketeering.png]]
|
||||
| style="border:1px solid black;" | Black Marketeering
|
||||
| style="border:1px solid black;" | 8 minutes
|
||||
| style="border:1px solid black;" | 12 minutes
|
||||
| style="border:1px solid black;" | To sell, distribute, or otherwise circulate Controlled Items to unauthorized entities.
|
||||
|}
|
||||
----
|
||||
@@ -262,49 +262,49 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
|
||||
| style="border: 1px solid black;" | 201
|
||||
| style="border: 1px solid black;" | [[File:SL_Assault.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Assault}}Assault
|
||||
| style="border: 1px solid black;" | 5 minutes
|
||||
| style="border: 1px solid black;" | 10 minutes
|
||||
| style="border: 1px solid black;" | To cause physical harm or to effect unwanted physical contact on an Employee, without the apparent intent to kill them, or to threaten such actions with both capability and intent to do so.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 202
|
||||
| style="border: 1px solid black;" | [[File:SL_BreakingAndEntering.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Breaking and Entering}}Breaking and Entering
|
||||
| style="border: 1px solid black;" | 5 minutes
|
||||
| style="border: 1px solid black;" | To break and enter into an area where one is not authorised nor invited.
|
||||
| style="border: 1px solid black;" | 10 minutes
|
||||
| style="border: 1px solid black;" | To break and enter into a high security area where one is not authorised nor invited, with intent to commit a crime within.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 203
|
||||
| style="border: 1px solid black;" | [[File:SL_Rioting.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Rioting}}Rioting
|
||||
| style="border: 1px solid black;" | 4 minutes
|
||||
| style="border: 1px solid black;" | 8 minutes
|
||||
| style="border: 1px solid black;" | To partake in an unauthorised riotous, tumultuous, and disruptive public assembly that refuses to disperse after warning.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 204
|
||||
| style="border: 1px solid black;" | [[File:SL_Endangerment.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Endangerment}}Endangerment
|
||||
| style="border: 1px solid black;" | 6 minutes
|
||||
| style="border: 1px solid black;" | 8 minutes
|
||||
| style="border: 1px solid black;" | To recklessly abandon obligations involving the continued wellbeing and/or protection of life and property, through malpractice, action, or inaction.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 205
|
||||
| style="border: 1px solid black;" | [[File:SL_Possession.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Possession}}Possession
|
||||
| style="border: 1px solid black;" | 3-8 minutes
|
||||
| style="border: 1px solid black;" | 5-10 minutes
|
||||
| style="border: 1px solid black;" | To be in unauthorised possession of restricted items or items of particular danger. See [[Standard_Operating_Procedure#Controlled_Substances/Items|Controlled Substances/Items]], [[Standard_Operating_Procedure#Controlled_Armaments|Controlled Armaments]].
|
||||
|-
|
||||
| style="border: 1px solid black;" | 206
|
||||
| style="border: 1px solid black;" | [[File:SL_ObstructionOfJustice.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Obstruction of Justice}}Obstruction of Justice
|
||||
| style="border: 1px solid black;" | 4 minutes
|
||||
| style="border: 1px solid black;" | 8 minutes
|
||||
| style="border: 1px solid black;" | To wilfully disobey, interfere with, or refuse a decree of the court, warrant, or arrest.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 207
|
||||
| style="border: 1px solid black;" | [[File:SL_PerjuryOrFalseReport.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Perjury or False Report}}Perjury or False Report
|
||||
| style="border: 1px solid black;" | 4 minutes
|
||||
| style="border: 1px solid black;" | 6 minutes
|
||||
| style="border: 1px solid black;" | To wilfully and maliciously tell an untruth either in court or in the process of making an actionable report to law enforcement.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 208
|
||||
| style="border: 1px solid black;" | [[File:SL_ContemptOfCourt.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Contempt of Court}}Contempt of Court
|
||||
| style="border: 1px solid black;" | 4 minutes
|
||||
| style="border: 1px solid black;" | 6 minutes
|
||||
| style="border: 1px solid black;" | To conduct oneself disruptively and disrespectfully before the court.
|
||||
|-
|
||||
|style= "border:1px solid black;"| 209
|
||||
@@ -329,13 +329,13 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
|
||||
| style="border: 1px solid black;" | 101
|
||||
| style="border: 1px solid black;" | [[File:SL_AnimalCruelty.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Animal Cruelty}}Animal Cruelty
|
||||
| style="border: 1px solid black;" | 2 minutes
|
||||
| style="border: 1px solid black;" | 5 minutes
|
||||
| style="border: 1px solid black;" | To inflict unnecessary suffering on a Pet with malicious intent.
|
||||
|-
|
||||
|style= "border:1px solid black;"| 102
|
||||
|style= "border:1px solid black;"| WIP
|
||||
!style= "border:1px solid black;"| Harassment
|
||||
|style= "border:1px solid black;"| 3 minutes
|
||||
|style= "border:1px solid black;"| 5 minutes
|
||||
|style= "border:1px solid black;"| To verbally demean, humiliate, or harass an Employee with malice; or maliciously spread false information amongst a population.
|
||||
|-
|
||||
|style= "border:1px solid black;"| 103
|
||||
@@ -347,19 +347,19 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
|
||||
| style="border: 1px solid black;" | 104
|
||||
| style="border: 1px solid black;" | [[File:SL_Theft.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Theft}}Petty Larceny
|
||||
| style="border: 1px solid black;" | 2 minutes
|
||||
| style="border: 1px solid black;" | 4 minutes
|
||||
| style="border: 1px solid black;" | To deprive an Employee of an item in their lawful property without consent.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 105
|
||||
| style="border: 1px solid black;" | [[File:SL_Trespass.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Trespass}}Trespass
|
||||
| style="border: 1px solid black;" | 2 minutes
|
||||
| style="border: 1px solid black;" | 3 minutes
|
||||
| style="border: 1px solid black;" | To enter into an area where one is not authorised nor invited.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 106
|
||||
| style="border: 1px solid black;" | [[File:SL_Vandalism.png]]
|
||||
! style="border: 1px solid black;" | {{anchor|Vandalism}}Vandalism
|
||||
| style="border: 1px solid black;" | 2 minutes
|
||||
| style="border: 1px solid black;" | 3 minutes
|
||||
| style="border: 1px solid black;" | To deface or superficially damage public property, or property belonging to another Employee.
|
||||
|-
|
||||
| style="border: 1px solid black;" | 107
|
||||
|
||||
@@ -13,7 +13,7 @@ The terms specified below are defined for further use within the procedures.
|
||||
:* Permanent Brig/Permabrig/Brig: A secure location for the long-term confinement of Prisoners. Typically outfitted to sustain the basic survival needs of the Prisoners within.
|
||||
|
||||
==Right to Refuse Service==
|
||||
All crew operating within their designated job locations are given the right to refuse service at any time for disorderly or abusive conduct. Additionally, all crew members are permitted to use nonlethal force to remove noncompliant or disorderly individuals only when such persons pose a threat to the safety of staff members, or disrupt the orderly functioning of the station. Service may not be refused to first responders or damage-control personnel acting to address or mitigate an emergency or crisis situation, unless such conduct is egregiously disorderly or abusive.
|
||||
All crew operating within their designated job locations are given the right to refuse service at any time for disorderly or abusive conduct. Additionally, all crew members are permitted to use nonlethal force to remove noncompliant or disorderly individuals only when such persons pose a threat to the safety of staff members, or disrupt the orderly functioning of the station. Service may not be refused to first responders acting to address or mitigate an emergency or crisis situation, unless such conduct is egregiously disorderly or abusive.
|
||||
|
||||
= Entity Definitions =
|
||||
The following definitions are provided to clarify the legal protections- and obligations- that Standard Operating Procedures and Space Law provide to any given entity. Unless explicitly covered by a more distinctive term, all Entities are to be treated as such until clarification is provided.
|
||||
@@ -79,7 +79,6 @@ Controlled Items include any item that is restricted from general possession and
|
||||
* Technology utilizing bluespace as an integral part of its function.
|
||||
* Teleportation and portal technology.
|
||||
* Hydraulic or powered equipment designed for the electronic or mechanical bypass of secure-access systems.
|
||||
* Rapid construction/piping technology
|
||||
|
||||
Any individual requiring any above item in the course of their job, or for regular function, may request a document of authorization from the relevant departmental head. Such a document must have an itemized list of the approved Controlled Items, the name and position of the holder, and a signed and stamped statement by the authorizing party.
|
||||
|
||||
@@ -103,25 +102,25 @@ A list of such Substances follow:
|
||||
|
||||
Controlled Armaments are grouped into categories of increasing severity. If the offender possesses several categories of contraband, only the highest severity of said items should be charged.
|
||||
|
||||
'''Category A:''' ''Illegal possession of Cat. A controlled armaments may incur a sentence of up to 3min brig and further legal action as the situation demands.''
|
||||
'''Category A:''' ''Illegal possession of Cat. A controlled armaments may incur a sentence of up to 5min brig and further legal action as the situation demands.''
|
||||
|
||||
* Any object or instrument specifically designed, adapted, or used to cause physical harm in close combat
|
||||
* Any object or equipment specifically designed or adapted to protect against physical harm caused by weapons, projectiles, or other forms of direct assault
|
||||
* Any object or instrument specifically designed or adapted to incapacitate or disorient by using light or sound without causing permanent injury
|
||||
* Any object or equipment specifically designed or adapted for explicitly training purposes that would otherwise fall under another category
|
||||
|
||||
'''Category B:''' ''Illegal possession of Cat. B controlled armaments may incur a sentence of up to 8min brig and further legal action as the situation demands.''
|
||||
'''Category B:''' ''Illegal possession of Cat. B controlled armaments may incur a sentence of up to 10min brig and further legal action as the situation demands.''
|
||||
|
||||
* Any object or instrument specifically designed or adapted to cause physical harm in close combat, which employs a powered system to enhance performance
|
||||
* Any manually-operated ballistic arms that possess an internal magazine or feeding system, or lack an internal magazine or feeding system altogether
|
||||
|
||||
'''Category C:''' ''Illegal possession of Cat. C controlled armaments may incur a sentence of up to 12min brig and further legal action as the situation demands.''
|
||||
'''Category C:''' ''Illegal possession of Cat. C controlled armaments may incur a sentence of up to 15min brig and further legal action as the situation demands.''
|
||||
|
||||
* Any manually-operated ballistic arms that use an external magazine or feeding system
|
||||
* Any semi-automatic ballistic arms not easily concealable within a pocket or coat
|
||||
* Any object or instrument specifically designed or adapted for explicitly less-lethal purposes that would otherwise fall under another category
|
||||
|
||||
'''Category D:''' ''Illegal possession of Cat. D controlled armaments may incur a sentence of up to 15min brig and further legal action as the situation demands.''
|
||||
'''Category D:''' ''Illegal possession of Cat. D controlled armaments may incur a sentence of up to 20min brig and further legal action as the situation demands.''
|
||||
|
||||
* Any semi-automatic ballistic arms that are easily concealable
|
||||
* Any fully-automatic ballistic arms
|
||||
@@ -129,7 +128,7 @@ Controlled Armaments are grouped into categories of increasing severity. If the
|
||||
* Any object or device specifically designed or adapted to cause physical harm through detonation of an explosive charge
|
||||
* Any object or equipment specifically designed or adapted to protect against both a space environment and physical harm caused by weapons, projectiles, or other forms of direct assault
|
||||
|
||||
'''Category E:''' ''Illegal possession of Cat. E controlled armaments may incur a sentence of up to 20min brig and further legal action as the situation demands.''
|
||||
'''Category E:''' ''Illegal possession of Cat. E controlled armaments may incur a sentence of up to 25min brig and further legal action as the situation demands.''
|
||||
|
||||
* Any crew-served or emplaced weapon
|
||||
* Any object or instrument specifically designed or adapted to launch large-calibre explosives or projectiles for offensive or defensive purposes
|
||||
@@ -269,6 +268,7 @@ Prisoners have certain rights that must be upheld by law enforcement and Station
|
||||
* Standard prisoner clothing must always be available for prisoners.
|
||||
* Confiscated clothing should be returned upon request, except in the case of Extended Confinement or if the clothing in question poses a clear risk to prisoner or crew safety.
|
||||
* Food and water for organic prisoners must be available and given on request.
|
||||
* Power for silicon prisoners such as IPCs and cyborgs, at minimum a cyborg recharging station must be provided.
|
||||
* Should the holding cells or Permabrig become uninhabitable, prisoners must be securely and safely relocated to another area for confinement, until the holding cells or Permabrig are returned to a serviceable state.
|
||||
* Prisoners should be granted freedom of movement within their holding area unless there is an undue risk to life and limb.
|
||||
|
||||
@@ -375,22 +375,3 @@ Medical staff are afforded the right to quarantine individuals that currently su
|
||||
|
||||
==Foreign Bodies==
|
||||
Any parasitic alien organism that requires a living host to live and reproduce must be eliminated. An exception can be made for experimental purposes, provided a CO, CMO and Mystagogue were to approve it.
|
||||
|
||||
=Engineering Regulations=
|
||||
==Health & Safety==
|
||||
* In public areas, all wiring must be concealed under tiles. In non-public areas, catwalks are recommended to be placed for the safety and mobility of workers.
|
||||
* The atmospheric distribution pipe ("distro") pressure should be no more than 500 kPa and composition should be a ratio of 70-80% nitrogen and 20-30% oxygen.
|
||||
* The atmospheric waste pipe ("waste") shall be kept free of clogging, and directed either to recycling, gas storage, or space.
|
||||
* Newly constructed burn chambers may not be located within the station or directly adjacent to the station's hull, unless receiving written authorization from Central Command.
|
||||
|
||||
==Building Permits==
|
||||
Any renovations larger than small modifications to the station's structural layout, power network, or atmospheric system shall require a building permit or work order approved expressly by the Chief Engineer, or if a Chief Engineer is not present, the Captain or Acting Commanding Officer. Modifications within departments shall additionally require the approval of the relevant department head. Areas within maintenance tunnels do not require permission to modify. However, any modifications to maintenance tunnel passageways should ensure they remain traversable and unobstructed by personnel who need to use them as part of their job.
|
||||
|
||||
==Emergency Access==
|
||||
In emergency situations, engineers are permitted to bypass door access (i.e. hack) and gain access to unauthorized locations if said locations require repairs.
|
||||
|
||||
==Hazard Zones==
|
||||
An area with a hull breach, hazardous material leak, or other similar danger shall be considered a hazard zone. To ensure crew safety, engineering personnel are required to demarcate hazard zones with holofans, inflatable walls, and/or engineering tape prior to beginning work. Any non-authorized personnel impeding or preventing repairs in hazardous areas may be liable for endangerment.
|
||||
|
||||
==Condemned Areas==
|
||||
The Chief Engineer may declare a section of the station condemned if repairs cannot be reasonably completed. In the event that there is no Chief Engineer, any engineer may condemn a section of the station with authorization from the Captain or Acting Commanding Officer. Doors leading to such areas shall be welded or bolted to prevent crew access.
|
||||
|
||||
@@ -11,7 +11,7 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.slnx"
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
||||
# If this doesn't match the saved version we overwrite them all.
|
||||
CURRENT_HOOKS_VERSION = "4"
|
||||
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
|
||||
<Nullable>disable</Nullable>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
|
||||
<!-- pin transitive deps -->
|
||||
<PackageReference Include="System.Management" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
|
||||
@@ -22,12 +19,10 @@
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
|
||||
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Benchmarks\Robust.Benchmarks.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Server.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Benchmarks.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Testing.props" />
|
||||
</Project>
|
||||
|
||||
@@ -51,8 +51,6 @@ public class DestructibleBenchmark
|
||||
private readonly List<Entity<DamageableComponent>> _damageables = new();
|
||||
private readonly List<Entity<DamageableComponent, DestructibleComponent>> _destructbiles = new();
|
||||
|
||||
private TestMapData _currentMapData = default!;
|
||||
|
||||
private DamageSpecifier _damage;
|
||||
|
||||
private TestPair _pair = default!;
|
||||
@@ -72,6 +70,8 @@ public class DestructibleBenchmark
|
||||
_pair = await PoolManager.GetServerClient();
|
||||
var server = _pair.Server;
|
||||
|
||||
var mapdata = await _pair.CreateTestMap();
|
||||
|
||||
_entMan = server.ResolveDependency<IEntityManager>();
|
||||
_protoMan = server.ResolveDependency<IPrototypeManager>();
|
||||
_random = server.ResolveDependency<IRobustRandom>();
|
||||
@@ -86,25 +86,19 @@ public class DestructibleBenchmark
|
||||
_damage = new DamageSpecifier(type, DamageAmount);
|
||||
|
||||
_random.SetSeed(69420); // Randomness needs to be deterministic for benchmarking.
|
||||
}
|
||||
|
||||
[IterationSetup]
|
||||
public void IterationSetup()
|
||||
{
|
||||
var plating = _tileDefMan[TileRef].TileId;
|
||||
var server = _pair.Server;
|
||||
_currentMapData = _pair.CreateTestMap().GetAwaiter().GetResult();
|
||||
|
||||
// We make a rectangular grid of destructible entities, and then damage them all simultaneously to stress test the system.
|
||||
// Needed for managing the performance of destructive effects and damage application.
|
||||
server.WaitPost(() =>
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
// Set up a thin line of tiles to place our objects on. They should be anchored for a "realistic" scenario...
|
||||
for (var x = 0; x < EntityCount; x++)
|
||||
{
|
||||
for (var y = 0; y < _prototypes.Length; y++)
|
||||
{
|
||||
_map.SetTile(_currentMapData.Grid, _currentMapData.Grid, new Vector2i(x, y), new Tile(plating));
|
||||
_map.SetTile(mapdata.Grid, mapdata.Grid, new Vector2i(x, y), new Tile(plating));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +107,7 @@ public class DestructibleBenchmark
|
||||
var y = 0;
|
||||
foreach (var protoId in _prototypes)
|
||||
{
|
||||
var coords = new EntityCoordinates(_currentMapData.Grid, x + 0.5f, y + 0.5f);
|
||||
var coords = new EntityCoordinates(mapdata.Grid, x + 0.5f, y + 0.5f);
|
||||
_entMan.SpawnEntity(protoId, coords);
|
||||
y++;
|
||||
}
|
||||
@@ -121,17 +115,12 @@ public class DestructibleBenchmark
|
||||
|
||||
var query = _entMan.EntityQueryEnumerator<DamageableComponent, DestructibleComponent>();
|
||||
|
||||
_destructbiles.EnsureCapacity(EntityCount);
|
||||
_damageables.EnsureCapacity(EntityCount);
|
||||
|
||||
while (query.MoveNext(out var uid, out var damageable, out var destructible))
|
||||
{
|
||||
_damageables.Add((uid, damageable));
|
||||
_destructbiles.Add((uid, damageable, destructible));
|
||||
}
|
||||
})
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
@@ -161,26 +150,6 @@ public class DestructibleBenchmark
|
||||
});
|
||||
}
|
||||
|
||||
[IterationCleanup]
|
||||
public void IterationCleanupAsync()
|
||||
{
|
||||
// We need to nuke the entire map and respawn everything as some destructible effects
|
||||
// spawn entities and whatnot.
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
_map.QueueDeleteMap(_currentMapData.MapId);
|
||||
})
|
||||
.Wait();
|
||||
|
||||
// Deletion of entities is often queued (QueueDel) which must be processed by running ticks
|
||||
// or else it will grow infinitely and leak memory.
|
||||
_pair.Server.WaitRunTicks(2)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
_destructbiles.Clear();
|
||||
_damageables.Clear();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public async Task CleanupAsync()
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
[Virtual]
|
||||
[GcServer(true)]
|
||||
[MemoryDiagnoser]
|
||||
public class HeatCapacityBenchmark
|
||||
{
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _sEntMan = default!;
|
||||
private IEntityManager _cEntMan = default!;
|
||||
private Client.Atmos.EntitySystems.AtmosphereSystem _cAtmos = default!;
|
||||
private AtmosphereSystem _sAtmos = default!;
|
||||
private GasMixture _mix;
|
||||
|
||||
[GlobalSetup]
|
||||
public async Task SetupAsync()
|
||||
{
|
||||
ProgramShared.PathOffset = "../../../../";
|
||||
PoolManager.Startup();
|
||||
_pair = await PoolManager.GetServerClient();
|
||||
await _pair.Connect();
|
||||
_cEntMan = _pair.Client.ResolveDependency<IEntityManager>();
|
||||
_sEntMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||
_cAtmos = _cEntMan.System<Client.Atmos.EntitySystems.AtmosphereSystem>();
|
||||
_sAtmos = _sEntMan.System<AtmosphereSystem>();
|
||||
|
||||
const float volume = 2500f;
|
||||
const float temperature = 293.15f;
|
||||
|
||||
const float o2 = 12.3f;
|
||||
const float n2 = 45.6f;
|
||||
const float co2 = 0.42f;
|
||||
const float plasma = 0.05f;
|
||||
|
||||
_mix = new GasMixture(volume) { Temperature = temperature };
|
||||
|
||||
_mix.AdjustMoles(Gas.Oxygen, o2);
|
||||
_mix.AdjustMoles(Gas.Nitrogen, n2);
|
||||
_mix.AdjustMoles(Gas.CarbonDioxide, co2);
|
||||
_mix.AdjustMoles(Gas.Plasma, plasma);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task ClientHeatCapacityBenchmark()
|
||||
{
|
||||
await _pair.Client.WaitPost(delegate
|
||||
{
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_cAtmos.GetHeatCapacity(_mix, applyScaling: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task ServerHeatCapacityBenchmark()
|
||||
{
|
||||
await _pair.Server.WaitPost(delegate
|
||||
{
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_sAtmos.GetHeatCapacity(_mix, applyScaling: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public async Task CleanupAsync()
|
||||
{
|
||||
await _pair.DisposeAsync();
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Server.Maps;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
|
||||
@@ -23,8 +23,6 @@ public class RaiseEventBenchmark
|
||||
PoolManager.Startup(typeof(BenchSystem).Assembly);
|
||||
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
|
||||
var entMan = _pair.Server.EntMan;
|
||||
var fact = _pair.Server.ResolveDependency<IComponentFactory>();
|
||||
var bus = (EntityEventBus)entMan.EventBus;
|
||||
_sys = entMan.System<BenchSystem>();
|
||||
|
||||
_pair.Server.WaitPost(() =>
|
||||
@@ -32,8 +30,6 @@ public class RaiseEventBenchmark
|
||||
var uid = entMan.Spawn();
|
||||
_sys.Ent = new(uid, entMan.GetComponent<TransformComponent>(uid));
|
||||
_sys.Ent2 = new(_sys.Ent.Owner, _sys.Ent.Comp);
|
||||
_sys.NetId = fact.GetRegistration<TransformComponent>().NetID!.Value;
|
||||
_sys.EvSubs = bus.GetNetCompEventHandlers<BenchSystem.BenchEv>();
|
||||
})
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
@@ -64,12 +60,6 @@ public class RaiseEventBenchmark
|
||||
return _sys.RaiseICompEvent();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int RaiseNetEvent()
|
||||
{
|
||||
return _sys.RaiseNetIdEvent();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int RaiseCSharpEvent()
|
||||
{
|
||||
@@ -84,8 +74,6 @@ public class RaiseEventBenchmark
|
||||
public delegate void EntityEventHandler(EntityUid uid, TransformComponent comp, ref BenchEv ev);
|
||||
|
||||
public event EntityEventHandler? OnCSharpEvent;
|
||||
public ushort NetId;
|
||||
internal EntityEventBus.DirectedEventHandler?[] EvSubs = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -104,7 +92,7 @@ public class RaiseEventBenchmark
|
||||
public int RaiseCompEvent()
|
||||
{
|
||||
var ev = new BenchEv();
|
||||
RaiseComponentEvent(Ent.Owner, Ent.Comp, ref ev);
|
||||
EntityManager.EventBus.RaiseComponentEvent(Ent.Owner, Ent.Comp, ref ev);
|
||||
return ev.N;
|
||||
}
|
||||
|
||||
@@ -112,16 +100,7 @@ public class RaiseEventBenchmark
|
||||
{
|
||||
// Raise with an IComponent instead of concrete type
|
||||
var ev = new BenchEv();
|
||||
RaiseComponentEvent(Ent2.Owner, Ent2.Comp, ref ev);
|
||||
return ev.N;
|
||||
}
|
||||
|
||||
public int RaiseNetIdEvent()
|
||||
{
|
||||
// Raise a "IComponent" event using a net-id index delegate array (for PVS & client game-state events)
|
||||
var ev = new BenchEv();
|
||||
ref var unitEv = ref Unsafe.As<BenchEv, EntityEventBus.Unit>(ref ev);
|
||||
EvSubs[NetId]?.Invoke(Ent2.Owner, Ent2.Comp, ref unitEv);
|
||||
EntityManager.EventBus.RaiseComponentEvent(Ent2.Owner, Ent2.Comp, ref ev);
|
||||
return ev.N;
|
||||
}
|
||||
|
||||
@@ -139,7 +118,6 @@ public class RaiseEventBenchmark
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
[ComponentEvent(Exclusive = false)]
|
||||
public struct BenchEv
|
||||
{
|
||||
public int N;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<GridContainer xmlns="https://spacestation14.io"
|
||||
Columns="4"
|
||||
Columns="5"
|
||||
HorizontalAlignment="Center">
|
||||
</GridContainer>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
MinSize="650 290">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<GridContainer Columns="2">
|
||||
<GridContainer Name="PrivilegedIdGrid" Columns="3" HorizontalExpand="True">
|
||||
<GridContainer Columns="3" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'access-overrider-window-privileged-id'}" />
|
||||
<Button Name="PrivilegedIdButton" Access="Public"/>
|
||||
<Label Name="PrivilegedIdLabel" />
|
||||
|
||||
@@ -53,8 +53,6 @@ namespace Content.Client.Access.UI
|
||||
|
||||
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state)
|
||||
{
|
||||
PrivilegedIdGrid.Visible = state.ShowPrivilegedIdGrid;
|
||||
|
||||
PrivilegedIdLabel.Text = state.PrivilegedIdName;
|
||||
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
|
||||
? Loc.GetString("access-overrider-window-eject-button")
|
||||
@@ -79,9 +77,7 @@ namespace Content.Client.Access.UI
|
||||
missingPrivileges.Add(privilege);
|
||||
}
|
||||
|
||||
MissingPrivilegesLabel.Text = state.ShowPrivilegedIdGrid ?
|
||||
Loc.GetString("access-overrider-window-missing-privileges") :
|
||||
Loc.GetString("access-overrider-window-missing-privileges-no-id");
|
||||
MissingPrivilegesLabel.Text = Loc.GetString("access-overrider-window-missing-privileges");
|
||||
MissingPrivilegesText.Text = string.Join(", ", missingPrivileges);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,12 @@ using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.SSDIndicator; // DeltaV - SSD time indicator
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing; // DeltaV - SSD time indicator
|
||||
|
||||
namespace Content.Client.Administration;
|
||||
|
||||
@@ -28,7 +26,6 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
private readonly SharedRoleSystem _roles;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly IGameTiming _timing; // DeltaV - Add timing
|
||||
private readonly Font _font;
|
||||
private readonly Font _fontBold;
|
||||
private AdminOverlayAntagFormat _overlayFormat;
|
||||
@@ -56,8 +53,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
IUserInterfaceManager userInterfaceManager,
|
||||
IConfigurationManager config,
|
||||
SharedRoleSystem roles,
|
||||
IPrototypeManager prototypeManager,
|
||||
IGameTiming timing) // DeltaV - Add timing
|
||||
IPrototypeManager prototypeManager)
|
||||
{
|
||||
_system = system;
|
||||
_entityManager = entityManager;
|
||||
@@ -66,7 +62,6 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
_userInterfaceManager = userInterfaceManager;
|
||||
_roles = roles;
|
||||
_prototypeManager = prototypeManager;
|
||||
_timing = timing; // DeltaV - Add timing
|
||||
ZIndex = 200;
|
||||
// Setting these to a specific ttf would break the antag symbols
|
||||
_font = resourceCache.NotoStack();
|
||||
@@ -139,7 +134,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
? null
|
||||
: _prototypeManager.Index(playerInfo.RoleProto.Value);
|
||||
|
||||
var roleName = rolePrototype?.Name ?? RoleTypePrototype.FallbackName;
|
||||
var roleName = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
|
||||
var roleColor = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
|
||||
var roleSymbol = rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol;
|
||||
|
||||
@@ -201,22 +196,8 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
// Username
|
||||
color = Color.Yellow;
|
||||
color.A = alpha;
|
||||
var usernameSpace = args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? color : colorDisconnected); // DeltaV - store return value
|
||||
|
||||
// DeltaV - add watchlist suffix START
|
||||
if (playerInfo.Watchlisted)
|
||||
{
|
||||
color = Color.Red;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_fontBold,
|
||||
screenCoordinates + currentOffset + usernameSpace with { Y = 0 },
|
||||
" " + Loc.GetString("admin-overlay-watchlisted-username-suffix"),
|
||||
uiScale,
|
||||
color);
|
||||
}
|
||||
// DeltaV - add watchlist suffix END
|
||||
|
||||
currentOffset += lineoffset; // DeltaV - moved down from username block
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
// Playtime
|
||||
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && _overlayPlaytime)
|
||||
@@ -232,22 +213,10 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
{
|
||||
color = Color.GreenYellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.StartingJob, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
// DeltaV - SSD Time START
|
||||
if (_entityManager.TryGetComponent<SSDIndicatorComponent>(entity, out var ssdIndicator)
|
||||
&& ssdIndicator.SsdSince is {} ssdSince)
|
||||
{
|
||||
color = Color.MediumPurple;
|
||||
color.A = alpha;
|
||||
var ssdText = Loc.GetString("admin-overlay-ssd-time", ("time", (_timing.CurTime - ssdSince).ToString("%hh':'mm':'ss")));
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, ssdText, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
// DeltaV END
|
||||
|
||||
// Determine antag symbol
|
||||
string? symbol;
|
||||
switch (_overlaySymbolStyle)
|
||||
@@ -272,7 +241,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
color = roleColor;
|
||||
symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
|
||||
text = IsFiltered(playerInfo.RoleProto)
|
||||
? Loc.GetString(roleName).ToUpper()
|
||||
? roleName.ToUpper()
|
||||
: string.Empty;
|
||||
break;
|
||||
case AdminOverlayAntagFormat.Subtype:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Administration.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
||||
@@ -5,7 +5,6 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing; // DeltaV - SSD time indicator
|
||||
|
||||
namespace Content.Client.Administration.Systems
|
||||
{
|
||||
@@ -20,7 +19,6 @@ namespace Content.Client.Administration.Systems
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!; // DeltaV - added for SSD time indicator
|
||||
|
||||
private AdminNameOverlay _adminNameOverlay = default!;
|
||||
|
||||
@@ -38,8 +36,7 @@ namespace Content.Client.Administration.Systems
|
||||
_userInterfaceManager,
|
||||
_configurationManager,
|
||||
_roles,
|
||||
_proto,
|
||||
_timing); // DeltaV - Add timing
|
||||
_proto);
|
||||
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Administration.Systems;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class BufferingSystem : SharedBufferingSystem
|
||||
{
|
||||
}
|
||||
@@ -1,77 +1,46 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Administration.Components;
|
||||
using Content.Client.Administration.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class KillSignSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<KillSignComponent, ComponentStartup>(KillSignAdded);
|
||||
SubscribeLocalEvent<KillSignComponent, ComponentShutdown>(KillSignRemoved);
|
||||
SubscribeLocalEvent<KillSignComponent, AfterAutoHandleStateEvent>(AfterAutoHandleState);
|
||||
}
|
||||
|
||||
private void KillSignRemoved(Entity<KillSignComponent> ent, ref ComponentShutdown args)
|
||||
private void KillSignRemoved(EntityUid uid, KillSignComponent component, ComponentShutdown args)
|
||||
{
|
||||
RemoveKillsign(ent);
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!_sprite.LayerMapTryGet((uid, sprite), KillSignKey.Key, out var layer, false))
|
||||
return;
|
||||
|
||||
_sprite.RemoveLayer((uid, sprite), layer);
|
||||
}
|
||||
|
||||
private void KillSignAdded(Entity<KillSignComponent> ent, ref ComponentStartup args)
|
||||
private void KillSignAdded(EntityUid uid, KillSignComponent component, ComponentStartup args)
|
||||
{
|
||||
AddKillsign(ent);
|
||||
}
|
||||
|
||||
private void AfterAutoHandleState(Entity<KillSignComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
// After receiving a new state for the component, we remove the old killsign and build a new one.
|
||||
// This is so changes to the sprite can be displayed live and allowing them to be edited via ViewVariables.
|
||||
// This could just update an existing sprite, but this is both easier and runs rarely anyway.
|
||||
RemoveKillsign(ent);
|
||||
AddKillsign(ent);
|
||||
}
|
||||
|
||||
private void AddKillsign(Entity<KillSignComponent> ent)
|
||||
{
|
||||
// If we hide from owner and we ARE the owner, don't add a killsign.
|
||||
// This could use session specific networking to FULLY hide it, but I am too lazy right now.
|
||||
if (ent.Comp.HideFromOwner && _player.LocalEntity == ent)
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
if (_sprite.LayerMapTryGet((uid, sprite), KillSignKey.Key, out var _, false))
|
||||
return;
|
||||
|
||||
if (_sprite.LayerMapTryGet((ent, sprite), KillSignKey.Key, out var _, false))
|
||||
return;
|
||||
var adj = _sprite.GetLocalBounds((uid, sprite)).Height / 2 + ((1.0f / 32) * 6.0f);
|
||||
|
||||
if (ent.Comp.Sprite == null)
|
||||
return;
|
||||
var layer = _sprite.AddLayer((uid, sprite), new SpriteSpecifier.Rsi(new ResPath("Objects/Misc/killsign.rsi"), "sign"));
|
||||
_sprite.LayerMapSet((uid, sprite), KillSignKey.Key, layer);
|
||||
|
||||
var adj = _sprite.GetLocalBounds((ent, sprite)).Height / 2 + ((1.0f / 32) * 6.0f);
|
||||
|
||||
var layer = _sprite.AddLayer((ent, sprite), ent.Comp.Sprite);
|
||||
_sprite.LayerMapSet((ent, sprite), KillSignKey.Key, layer);
|
||||
_sprite.LayerSetScale((ent, sprite), layer, ent.Comp.Scale);
|
||||
_sprite.LayerSetOffset((ent, sprite), layer, ent.Comp.DoOffset ? new Vector2(0.0f, adj) : new Vector2(0.0f, 0.0f));
|
||||
|
||||
if (ent.Comp.ForceUnshaded)
|
||||
sprite.LayerSetShader(layer, "unshaded");
|
||||
}
|
||||
|
||||
private void RemoveKillsign(Entity<KillSignComponent> ent)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(ent, out var sprite))
|
||||
return;
|
||||
|
||||
if (!_sprite.LayerMapTryGet((ent, sprite), KillSignKey.Key, out var layer, false))
|
||||
return;
|
||||
|
||||
_sprite.RemoveLayer((ent, sprite), layer);
|
||||
_sprite.LayerSetOffset((uid, sprite), layer, new Vector2(0.0f, adj));
|
||||
sprite.LayerSetShader(layer, "unshaded");
|
||||
}
|
||||
|
||||
private enum KillSignKey
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Administration.UI.CustomControls;
|
||||
|
||||
public sealed class AdminLogLabel : RichTextLabel
|
||||
{
|
||||
public AdminLogLabel(ref SharedAdminLog log, HSeparator separator)
|
||||
{
|
||||
Log = log;
|
||||
Separator = separator;
|
||||
|
||||
SetMessage($"{log.Date:HH:mm:ss}: {log.Message}");
|
||||
OnVisibilityChanged += VisibilityChanged;
|
||||
}
|
||||
|
||||
public new SharedAdminLog Log { get; }
|
||||
|
||||
public HSeparator Separator { get; }
|
||||
|
||||
private void VisibilityChanged(Control control)
|
||||
{
|
||||
Separator.Visible = Visible;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
OnVisibilityChanged -= VisibilityChanged;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.Verbs.UI;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.CustomControls;
|
||||
|
||||
@@ -96,13 +95,26 @@ public sealed partial class PlayerListControl : BoxContainer
|
||||
private void FilterList()
|
||||
{
|
||||
_sortedPlayerList.Clear();
|
||||
|
||||
Regex filterRegex;
|
||||
// There is no neat way to handle invalid regex being submitted other than
|
||||
// catching and ignoring the exception which gets thrown when it's invalid.
|
||||
try
|
||||
{
|
||||
filterRegex = new Regex(FilterLineEdit.Text, RegexOptions.IgnoreCase);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var info in _playerList)
|
||||
{
|
||||
var displayName = $"{info.CharacterName} ({info.Username})";
|
||||
if (info.IdentityName != info.CharacterName)
|
||||
displayName += $" [{info.IdentityName}]";
|
||||
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
|
||||
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
|
||||
&& !filterRegex.IsMatch(displayName))
|
||||
continue;
|
||||
_sortedPlayerList.Add(info);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:aui="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
xmlns:aui="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:ui="clr-namespace:Content.Client.Options.UI">
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
@@ -54,9 +55,15 @@
|
||||
<Control HorizontalExpand="True"/>
|
||||
<Label Name="Count" Access="Public"/>
|
||||
<Control HorizontalExpand="True"/>
|
||||
<Button Name="ExportLogs" Access="Public" Text="{Loc admin-logs-export}"/>
|
||||
<Button Name="PopOutButton" Access="Public" Text="{Loc admin-logs-pop-out}"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Button Name="RenderRichTextButton" Access="Public" Text="{Loc admin-logs-render-rich-text}"
|
||||
StyleClasses="OpenRight" ToggleMode="True"/>
|
||||
<Button Name="RemoveMarkupButton" Access="Public" Text="{Loc admin-logs-remove-markup}"
|
||||
StyleClasses="OpenLeft" ToggleMode="True"/>
|
||||
<Control HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<LineEdit Name="LogSearch" Access="Public" StyleClasses="actionSearchBox"
|
||||
HorizontalExpand="true" PlaceHolder="{Loc admin-logs-search-logs-placeholder}"/>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Client.Administration.UI.Logs.Entries;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -38,6 +40,9 @@ public sealed partial class AdminLogsControl : Control
|
||||
SelectAllPlayersButton.OnPressed += SelectAllPlayers;
|
||||
SelectNoPlayersButton.OnPressed += SelectNoPlayers;
|
||||
|
||||
RenderRichTextButton.OnPressed += RenderRichTextChanged;
|
||||
RemoveMarkupButton.OnPressed += RemoveMarkupChanged;
|
||||
|
||||
RoundSpinBox.IsValid = i => i > 0 && i <= CurrentRound;
|
||||
RoundSpinBox.ValueChanged += RoundSpinBoxChanged;
|
||||
RoundSpinBox.InitDefaultButtons();
|
||||
@@ -58,6 +63,8 @@ public sealed partial class AdminLogsControl : Control
|
||||
|
||||
private int CurrentRound { get; set; }
|
||||
|
||||
private Regex LogSearchRegex { get; set; } = new("");
|
||||
|
||||
public int SelectedRoundId => RoundSpinBox.Value;
|
||||
public string Search => LogSearch.Text;
|
||||
private int ShownLogs { get; set; }
|
||||
@@ -77,6 +84,8 @@ public sealed partial class AdminLogsControl : Control
|
||||
/// </summary>
|
||||
private List<SharedAdminLog> RawAdminLogs { get; set; } = new();
|
||||
|
||||
private bool RenderRichText { get; set; }
|
||||
private bool RemoveMarkup { get; set; }
|
||||
public HashSet<LogType> SelectedTypes { get; } = new();
|
||||
|
||||
public HashSet<Guid> SelectedPlayers { get; } = new();
|
||||
@@ -123,6 +132,19 @@ public sealed partial class AdminLogsControl : Control
|
||||
|
||||
private void LogSearchChanged(LineEditEventArgs args)
|
||||
{
|
||||
// This exception is thrown if the regex is invalid, which happens often, so we ignore it.
|
||||
try
|
||||
{
|
||||
LogSearchRegex = new Regex(
|
||||
"(" + LogSearch.Text + ")",
|
||||
RegexOptions.IgnoreCase,
|
||||
TimeSpan.FromSeconds(1));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateLogs();
|
||||
}
|
||||
|
||||
@@ -204,6 +226,26 @@ public sealed partial class AdminLogsControl : Control
|
||||
UpdateLogs();
|
||||
}
|
||||
|
||||
private void RenderRichTextChanged(ButtonEventArgs args)
|
||||
{
|
||||
RenderRichText = args.Button.Pressed;
|
||||
|
||||
RemoveMarkup = RemoveMarkup && !RenderRichText;
|
||||
RemoveMarkupButton.Pressed = RemoveMarkup;
|
||||
|
||||
UpdateLogs();
|
||||
}
|
||||
|
||||
private void RemoveMarkupChanged(ButtonEventArgs args)
|
||||
{
|
||||
RemoveMarkup = args.Button.Pressed;
|
||||
|
||||
RenderRichText = !RemoveMarkup && RenderRichText;
|
||||
RenderRichTextButton.Pressed = RenderRichText;
|
||||
|
||||
UpdateLogs();
|
||||
}
|
||||
|
||||
public void SetTypesSelection(HashSet<LogType> selectedTypes, bool invert = false)
|
||||
{
|
||||
SelectedTypes.Clear();
|
||||
@@ -262,16 +304,15 @@ public sealed partial class AdminLogsControl : Control
|
||||
|
||||
foreach (var child in LogsContainer.Children)
|
||||
{
|
||||
if (child is not AdminLogLabel log)
|
||||
{
|
||||
if (child is not AdminLogEntry log)
|
||||
continue;
|
||||
}
|
||||
|
||||
child.Visible = ShouldShowLog(log);
|
||||
if (child.Visible)
|
||||
{
|
||||
ShownLogs++;
|
||||
}
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
|
||||
log.RenderResults(LogSearchRegex, RenderRichText, RemoveMarkup);
|
||||
ShownLogs++;
|
||||
}
|
||||
|
||||
UpdateCount();
|
||||
@@ -289,30 +330,30 @@ public sealed partial class AdminLogsControl : Control
|
||||
button.Text.Contains(PlayerSearch.Text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool LogMatchesPlayerFilter(AdminLogLabel label)
|
||||
private bool LogMatchesPlayerFilter(AdminLogEntry entry)
|
||||
{
|
||||
if (label.Log.Players.Length == 0)
|
||||
if (entry.Log.Players.Length == 0)
|
||||
return SelectedPlayers.Count == 0 || IncludeNonPlayerLogs;
|
||||
|
||||
return SelectedPlayers.Overlaps(label.Log.Players);
|
||||
return SelectedPlayers.Overlaps(entry.Log.Players);
|
||||
}
|
||||
|
||||
private bool ShouldShowLog(AdminLogLabel label)
|
||||
private bool ShouldShowLog(AdminLogEntry entry)
|
||||
{
|
||||
// Check log type
|
||||
if (!SelectedTypes.Contains(label.Log.Type))
|
||||
if (!SelectedTypes.Contains(entry.Log.Type))
|
||||
return false;
|
||||
|
||||
// Check players
|
||||
if (!LogMatchesPlayerFilter(label))
|
||||
if (!LogMatchesPlayerFilter(entry))
|
||||
return false;
|
||||
|
||||
// Check impact
|
||||
if (!SelectedImpacts.Contains(label.Log.Impact))
|
||||
if (!SelectedImpacts.Contains(entry.Log.Impact))
|
||||
return false;
|
||||
|
||||
// Check search
|
||||
if (!label.Log.Message.Contains(LogSearch.Text, StringComparison.OrdinalIgnoreCase))
|
||||
if (!LogSearchRegex.IsMatch(entry.Log.Message))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -386,15 +427,16 @@ public sealed partial class AdminLogsControl : Control
|
||||
{
|
||||
var log = tempLogs[i];
|
||||
ref var logRef = ref log; // It didn't like me doing this as one line lmao
|
||||
var entry = new AdminLogEntry(ref log);
|
||||
|
||||
var separator = new HSeparator();
|
||||
var label = new AdminLogLabel(ref logRef, separator);
|
||||
label.Visible = ShouldShowLog(label);
|
||||
//var label = new AdminLogLabel(ref logRef, separator);
|
||||
//label.Visible = ShouldShowLog(label);
|
||||
|
||||
if (label.Visible) ShownLogs++;
|
||||
TotalLogs++;
|
||||
//if (label.Visible) ShownLogs++;
|
||||
|
||||
LogsContainer.AddChild(label);
|
||||
LogsContainer.AddChild(separator);
|
||||
//LogsContainer.AddChild(label);
|
||||
LogsContainer.AddChild(entry);
|
||||
}
|
||||
UpdateLogs();
|
||||
}
|
||||
@@ -594,6 +636,7 @@ public sealed partial class AdminLogsControl : Control
|
||||
SelectAllTypesButton.OnPressed -= SelectAllTypes;
|
||||
SelectNoTypesButton.OnPressed -= SelectNoTypes;
|
||||
|
||||
IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
|
||||
IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
|
||||
SelectAllPlayersButton.OnPressed -= SelectAllPlayers;
|
||||
SelectNoPlayersButton.OnPressed -= SelectNoPlayers;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Client.Administration.UI.Logs.Entries;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Eui;
|
||||
@@ -17,16 +17,6 @@ public sealed class AdminLogsEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly IFileDialogManager _dialogManager = default!;
|
||||
[Dependency] private readonly ILogManager _log = default!;
|
||||
|
||||
private const char CsvSeparator = ',';
|
||||
private const string CsvQuote = "\"";
|
||||
private const string CsvHeader = "Date,ID,PlayerID,Severity,Type,Message";
|
||||
|
||||
private ISawmill _sawmill;
|
||||
|
||||
private bool _currentlyExportingLogs = false;
|
||||
|
||||
public AdminLogsEui()
|
||||
{
|
||||
@@ -38,9 +28,6 @@ public sealed class AdminLogsEui : BaseEui
|
||||
LogsControl.RefreshButton.OnPressed += _ => RequestLogs();
|
||||
LogsControl.NextButton.OnPressed += _ => NextLogs();
|
||||
LogsControl.PopOutButton.OnPressed += _ => PopOut();
|
||||
LogsControl.ExportLogs.OnPressed += _ => ExportLogs();
|
||||
|
||||
_sawmill = _log.GetSawmill("admin.logs.ui");
|
||||
}
|
||||
|
||||
private WindowRoot? Root { get; set; }
|
||||
@@ -89,71 +76,6 @@ public sealed class AdminLogsEui : BaseEui
|
||||
SendMessage(request);
|
||||
}
|
||||
|
||||
private async void ExportLogs()
|
||||
{
|
||||
if (_currentlyExportingLogs)
|
||||
return;
|
||||
|
||||
_currentlyExportingLogs = true;
|
||||
LogsControl.ExportLogs.Disabled = true;
|
||||
|
||||
var file = await _dialogManager.SaveFile(new FileDialogFilters(new FileDialogFilters.Group("csv")));
|
||||
|
||||
if (file == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// Buffer is set to 4KB for performance reasons. As the average export of 1000 logs is ~200KB
|
||||
await using var writer = new StreamWriter(file.Value.fileStream, bufferSize: 4096);
|
||||
await writer.WriteLineAsync(CsvHeader);
|
||||
foreach (var child in LogsControl.LogsContainer.Children)
|
||||
{
|
||||
if (child is not AdminLogLabel logLabel || !child.Visible)
|
||||
continue;
|
||||
|
||||
var log = logLabel.Log;
|
||||
|
||||
// Date
|
||||
// I swear to god if someone adds ,s or "s to the other fields...
|
||||
await writer.WriteAsync(log.Date.ToString("s", System.Globalization.CultureInfo.InvariantCulture));
|
||||
await writer.WriteAsync(CsvSeparator);
|
||||
// ID
|
||||
await writer.WriteAsync(log.Id.ToString());
|
||||
await writer.WriteAsync(CsvSeparator);
|
||||
// PlayerID
|
||||
var players = log.Players;
|
||||
for (var i = 0; i < players.Length; i++)
|
||||
{
|
||||
await writer.WriteAsync(players[i] + (i == players.Length - 1 ? "" : " "));
|
||||
}
|
||||
await writer.WriteAsync(CsvSeparator);
|
||||
// Severity
|
||||
await writer.WriteAsync(log.Impact.ToString());
|
||||
await writer.WriteAsync(CsvSeparator);
|
||||
// Type
|
||||
await writer.WriteAsync(log.Type.ToString());
|
||||
await writer.WriteAsync(CsvSeparator);
|
||||
// Message
|
||||
await writer.WriteAsync(CsvQuote);
|
||||
await writer.WriteAsync(log.Message.Replace(CsvQuote, CsvQuote + CsvQuote));
|
||||
await writer.WriteAsync(CsvQuote);
|
||||
|
||||
await writer.WriteLineAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
_sawmill.Error($"Error when exporting admin log:\n{exc.StackTrace}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await file.Value.fileStream.DisposeAsync();
|
||||
_currentlyExportingLogs = false;
|
||||
LogsControl.ExportLogs.Disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void PopOut()
|
||||
{
|
||||
if (LogsWindow == null)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Orientation="Vertical">
|
||||
<BoxContainer Margin="2">
|
||||
<Collapsible>
|
||||
<CollapsibleHeading Name="DetailsHeading" Access="Public">
|
||||
<RichTextLabel Margin="20 0 0 0" Name="Message" MinSize="50 10" VerticalExpand="True" Access="Public" />
|
||||
</CollapsibleHeading>
|
||||
<CollapsibleBody Name="DetailsBody" Access="Public" />
|
||||
</Collapsible>
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Client.Message;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.Logs.Entries;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminLogEntry : BoxContainer
|
||||
{
|
||||
private readonly IConfigurationManager _cfgManager;
|
||||
|
||||
public SharedAdminLog Log { get; }
|
||||
|
||||
private readonly string _rawMessage;
|
||||
|
||||
public AdminLogEntry(ref SharedAdminLog log)
|
||||
{
|
||||
_cfgManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Log = log;
|
||||
|
||||
_rawMessage = $"{log.Date:HH:mm:ss}: {log.Message}";
|
||||
Message.SetMessage(_rawMessage);
|
||||
|
||||
DetailsHeading.OnToggled += DetailsToggled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets text to be highlighted from a search result, and renders rich text, or removes all rich text markup.
|
||||
/// </summary>
|
||||
public void RenderResults(Regex highlightRegex, bool renderRichText, bool removeMarkup)
|
||||
{
|
||||
var color = _cfgManager.GetCVar(CCVars.AdminLogsHighlightColor);
|
||||
var formattedMessage = renderRichText
|
||||
? _rawMessage
|
||||
: removeMarkup
|
||||
? FormattedMessage.RemoveMarkupPermissive(_rawMessage)
|
||||
: FormattedMessage.EscapeText(_rawMessage);
|
||||
|
||||
// Want to avoid highlighting smaller strings
|
||||
if (highlightRegex.ToString().Length > 4)
|
||||
{
|
||||
try
|
||||
{
|
||||
formattedMessage = highlightRegex.Replace(formattedMessage, $"[color={color}]$1[/color]", 3);
|
||||
}
|
||||
catch (RegexMatchTimeoutException)
|
||||
{
|
||||
// if we time out then don't bother highlighting results
|
||||
}
|
||||
}
|
||||
|
||||
if (!FormattedMessage.TryFromMarkup(formattedMessage, out var outputMessage))
|
||||
return;
|
||||
|
||||
Message.SetMessage(outputMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We perform some extra calculations in the dropdown, so we want to render that only when
|
||||
/// the dropdown is actually opened.
|
||||
/// This also removes itself from the event listener so it doesn't trigger again.
|
||||
/// </summary>
|
||||
private void DetailsToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
if (!args.Pressed || DetailsBody.ChildCount > 0)
|
||||
return;
|
||||
DetailsBody.AddChild(new AdminLogEntryDetails(Log));
|
||||
DetailsHeading.OnToggled -= DetailsToggled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
StyleClasses="BackgroundDark">
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Text="{Loc admin-logs-field-type}" Margin="0 0 4 0"/>
|
||||
<Label Name="Type" Text="None" StyleClasses="LabelSecondaryColor" Access="Public"/>
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Text="{Loc admin-logs-field-impact}" Margin="0 0 4 0"/>
|
||||
<Label Name="Impact" Text="None" StyleClasses="LabelSecondaryColor" Access="Public"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<cc:VSeparator/>
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<Label Text="{Loc admin-logs-field-time-header}"/>
|
||||
<cc:HSeparator/>
|
||||
<BoxContainer>
|
||||
<Label Text="{Loc admin-logs-field-time-local}" Margin="0 0 4 0" HorizontalExpand="True"/>
|
||||
<Label Name="LocalTime" Text="None" StyleClasses="LabelSecondaryColor" Access="Public" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-logs-field-time-utc}" Margin="0 0 4 0" HorizontalExpand="True"/>
|
||||
<Label Name="UTCTime" Text="None" StyleClasses="LabelSecondaryColor" Access="Public" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
<cc:HSeparator/>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc admin-logs-field-time-round}" Margin="0 0 4 0" HorizontalExpand="True"/>
|
||||
<Label Name="CurTime" Text="None" StyleClasses="LabelSecondaryColor" Access="Public" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<cc:VSeparator/>
|
||||
<BoxContainer Orientation="Vertical" Margin="4" HorizontalExpand="True">
|
||||
<Label Text="{Loc admin-logs-field-players-header}"/>
|
||||
<cc:HSeparator/>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4" VerticalExpand="True">
|
||||
<controls:ListContainer Name="PlayerListContainer" Access="Public" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,98 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.Verbs.UI;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Client.Administration.UI.Logs.Entries;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminLogEntryDetails : BoxContainer
|
||||
{
|
||||
private readonly AdminSystem _adminSystem;
|
||||
private readonly IUserInterfaceManager _uiManager;
|
||||
private readonly IEntityManager _entManager;
|
||||
|
||||
public AdminLogEntryDetails(SharedAdminLog log)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
_adminSystem = _entManager.System<AdminSystem>();
|
||||
|
||||
Type.Text = log.Type.ToString();
|
||||
Impact.Text = log.Impact.ToString();
|
||||
|
||||
LocalTime.Text = $"{log.Date.ToLocalTime():HH:mm:ss}";
|
||||
UTCTime.Text = $"{log.Date:HH:mm:ss}";
|
||||
// TimeSpan and DateTime use different formatting string conventions for some completely logical reason
|
||||
// that mere mortals such as myself will never be able to understand.
|
||||
CurTime.Text = new TimeSpan(log.CurTime).ToString(@"hh\:mm\:ss");
|
||||
|
||||
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
|
||||
PlayerListContainer.GenerateItem += GenerateButton;
|
||||
PopulateList(log.Players);
|
||||
}
|
||||
|
||||
private void PopulateList(Guid[] players)
|
||||
{
|
||||
if (players.Length == 0)
|
||||
return;
|
||||
|
||||
if (_adminSystem.PlayerList is not { } allPlayers || allPlayers.Count == 0)
|
||||
return;
|
||||
|
||||
var listData = new List<PlayerListData>();
|
||||
foreach (var playerGuid in players)
|
||||
{
|
||||
var netUserId = new NetUserId(playerGuid);
|
||||
|
||||
// Linq here is fine since this runs in response to admin input in the UI and
|
||||
// this loop only tends to go through 1-4 iterations.
|
||||
if (allPlayers.FirstOrDefault(player => player.SessionId == netUserId) is not { } playerInfo)
|
||||
continue;
|
||||
|
||||
listData.Add(new PlayerListData(playerInfo));
|
||||
}
|
||||
|
||||
if (listData.Count == 0)
|
||||
return;
|
||||
|
||||
PlayerListContainer.PopulateList(listData);
|
||||
}
|
||||
|
||||
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
|
||||
{
|
||||
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
|
||||
return;
|
||||
|
||||
if (!(args.Function == EngineKeyFunctions.UIRightClick
|
||||
|| args.Function == EngineKeyFunctions.UIClick)
|
||||
|| selectedPlayer.NetEntity == null)
|
||||
return;
|
||||
|
||||
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
private void GenerateButton(ListData data, ListContainerButton button)
|
||||
{
|
||||
if (data is not PlayerListData { Info: var info })
|
||||
return;
|
||||
|
||||
var entryLabel = new Label();
|
||||
entryLabel.Text = $"{info.CharacterName} ({info.Username})";
|
||||
var entry = new BoxContainer();
|
||||
entry.AddChild(entryLabel);
|
||||
|
||||
button.AddChild(entry);
|
||||
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,6 @@ public sealed partial class PlayerTab : Control
|
||||
private AdminPlayerTabColorOption _playerTabColorSetting;
|
||||
private AdminPlayerTabRoleTypeOption _playerTabRoleSetting;
|
||||
private AdminPlayerTabSymbolOption _playerTabSymbolSetting;
|
||||
private bool _markGhosted; // DeltaV
|
||||
private bool _markWatchlisted; // DeltaV
|
||||
|
||||
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
|
||||
|
||||
@@ -53,9 +51,6 @@ public sealed partial class PlayerTab : Control
|
||||
_config.OnValueChanged(CCVars.AdminPlayerTabRoleSetting, RoleSettingChanged, true);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerTabColorSetting, ColorSettingChanged, true);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerTabSymbolSetting, SymbolSettingChanged, true);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerTabMarkGhosted, MarkGhostedChanged, true); // DeltaV
|
||||
_config.OnValueChanged(CCVars.AdminPlayerTabMarkWatchlisted, MarkWatchlistedChanged, true); // DeltaV
|
||||
|
||||
|
||||
OverlayButton.OnPressed += OverlayButtonPressed;
|
||||
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
||||
@@ -69,23 +64,8 @@ public sealed partial class PlayerTab : Control
|
||||
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
|
||||
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
|
||||
}
|
||||
|
||||
// DeltaV - mark ghosted, watchlisted START
|
||||
private void MarkGhostedChanged(bool value)
|
||||
{
|
||||
_markGhosted = value;
|
||||
RefreshPlayerList(_players);
|
||||
}
|
||||
|
||||
private void MarkWatchlistedChanged(bool value)
|
||||
{
|
||||
_markWatchlisted = value;
|
||||
RefreshPlayerList(_players);
|
||||
}
|
||||
// DeltaV - mark ghosted, watchlisted END
|
||||
|
||||
#region Antag Overlay
|
||||
|
||||
private void OverlayEnabled()
|
||||
@@ -172,25 +152,8 @@ public sealed partial class PlayerTab : Control
|
||||
|
||||
UpdateHeaderSymbols();
|
||||
|
||||
SearchList.PopulateList(sortedPlayers.Select(info =>
|
||||
{
|
||||
// DeltaV - additions START
|
||||
var filteringStringAdditions = "";
|
||||
if (_markGhosted && info.Ghost)
|
||||
{
|
||||
filteringStringAdditions += " (G)";
|
||||
}
|
||||
|
||||
if (_markWatchlisted && info.Watchlisted)
|
||||
{
|
||||
filteringStringAdditions += " (WL)";
|
||||
}
|
||||
// DeltaV - additions END
|
||||
|
||||
return new PlayerListData(info,
|
||||
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}" +
|
||||
filteringStringAdditions); // DeltaV - append filteringStringAdditions
|
||||
})
|
||||
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
|
||||
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
@@ -204,10 +167,7 @@ public sealed partial class PlayerTab : Control
|
||||
new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor),
|
||||
_playerTabColorSetting,
|
||||
_playerTabRoleSetting,
|
||||
_playerTabSymbolSetting,
|
||||
_markGhosted, // DeltaV - Add _markGhosted
|
||||
_markWatchlisted // DeltaV - Add _markWatchlisted
|
||||
);
|
||||
_playerTabSymbolSetting);
|
||||
button.AddChild(entry);
|
||||
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
|
||||
button.StyleClasses.Clear();
|
||||
|
||||
@@ -20,10 +20,7 @@ public sealed partial class PlayerTabEntry : PanelContainer
|
||||
StyleBoxFlat styleBoxFlat,
|
||||
AdminPlayerTabColorOption colorOption,
|
||||
AdminPlayerTabRoleTypeOption roleSetting,
|
||||
AdminPlayerTabSymbolOption symbolSetting,
|
||||
bool markGhosted, // DeltaV - Add markGhosted
|
||||
bool markWatchlisted // DeltaV - Add markWatchlisted
|
||||
)
|
||||
AdminPlayerTabSymbolOption symbolSetting)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -76,17 +73,6 @@ public sealed partial class PlayerTabEntry : PanelContainer
|
||||
if (player.IdentityName != player.CharacterName)
|
||||
CharacterLabel.Text += $" [{player.IdentityName}]";
|
||||
|
||||
// DeltaV - Mark ghosted/watchlisted players START
|
||||
if (player.Ghost && markGhosted)
|
||||
{
|
||||
CharacterLabel.Text = $"(G) {CharacterLabel.Text}";
|
||||
}
|
||||
if (player.Watchlisted && markWatchlisted)
|
||||
{
|
||||
CharacterLabel.Text = $"(WL) {CharacterLabel.Text}";
|
||||
}
|
||||
// DeltaV - Mark ghosted/watchlisted players END
|
||||
|
||||
var roletype = RoleTypeLabel.Text = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
|
||||
var subtype = roles.GetRoleSubtypeLabel(rolePrototype?.Name ?? RoleTypePrototype.FallbackName, player.Subtype);
|
||||
switch (roleSetting)
|
||||
|
||||
@@ -60,24 +60,8 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
if (args.Current is not AlertComponentState cast)
|
||||
return;
|
||||
|
||||
// Save all client-sided alerts to later put back in
|
||||
var clientAlerts = new Dictionary<AlertKey, AlertState>();
|
||||
foreach (var alert in alerts.Comp.Alerts)
|
||||
{
|
||||
if (alert.Key.AlertType != null && TryGet(alert.Key.AlertType.Value, out var alertProto))
|
||||
{
|
||||
if (alertProto.ClientHandled)
|
||||
clientAlerts[alert.Key] = alert.Value;
|
||||
}
|
||||
}
|
||||
|
||||
alerts.Comp.Alerts = new(cast.Alerts);
|
||||
|
||||
foreach (var alert in clientAlerts)
|
||||
{
|
||||
alerts.Comp.Alerts[alert.Key] = alert.Value;
|
||||
}
|
||||
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
public sealed partial class AtmosphereSystem
|
||||
{
|
||||
/*
|
||||
Partial class for operations involving GasMixtures.
|
||||
|
||||
Any method that is overridden here is usually because the server-sided implementation contains
|
||||
code that would escape sandbox. As such these methods are overridden here with a safe
|
||||
implementation.
|
||||
*/
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override float GetHeatCapacityCalculation(float[] moles, bool space)
|
||||
{
|
||||
// Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms.
|
||||
if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f))
|
||||
{
|
||||
return Atmospherics.SpaceHeatCapacity;
|
||||
}
|
||||
|
||||
// explicit stackalloc call is banned on client tragically.
|
||||
// the JIT does not stackalloc this during runtime,
|
||||
// though this isnt the hottest code path so it should be fine
|
||||
// the gc can eat a little as a treat
|
||||
var tmp = new float[moles.Length];
|
||||
NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp);
|
||||
// Adjust heat capacity by speedup, because this is primarily what
|
||||
// determines how quickly gases heat up/cool.
|
||||
return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||
public sealed class AtmosphereSystem : SharedAtmosphereSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Client.Atmos.Components;
|
||||
using Content.Client.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -24,7 +23,6 @@ namespace Content.Client.Atmos.Overlays
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly SharedAtmosphereSystem _atmosphereSystem;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly SharedTransformSystem _xformSys;
|
||||
|
||||
@@ -56,7 +54,6 @@ namespace Content.Client.Atmos.Overlays
|
||||
{
|
||||
_entManager = entManager;
|
||||
_mapManager = IoCManager.Resolve<IMapManager>();
|
||||
_atmosphereSystem = entManager.System<SharedAtmosphereSystem>();
|
||||
_mapSystem = entManager.System<SharedMapSystem>();
|
||||
_xformSys = xformSys;
|
||||
_shader = protoMan.Index(UnshadedShader).Instance();
|
||||
@@ -70,7 +67,7 @@ namespace Content.Client.Atmos.Overlays
|
||||
|
||||
for (var i = 0; i < _gasCount; i++)
|
||||
{
|
||||
var gasPrototype = _atmosphereSystem.GetGas(system.VisibleGasId[i]);
|
||||
var gasPrototype = protoMan.Index<GasPrototype>(system.VisibleGasId[i].ToString());
|
||||
|
||||
SpriteSpecifier overlay;
|
||||
|
||||
|
||||
@@ -52,18 +52,14 @@ namespace Content.Client.Atmos.UI
|
||||
|
||||
private void OnSelectGasPressed()
|
||||
{
|
||||
if (_window is null)
|
||||
return;
|
||||
|
||||
if (_window is null) return;
|
||||
if (_window.SelectedGas is null)
|
||||
{
|
||||
SendMessage(new GasFilterSelectGasMessage(null));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Enum.TryParse<Gas>(_window.SelectedGas, out var gas))
|
||||
return;
|
||||
|
||||
if (!int.TryParse(_window.SelectedGas, out var gas)) return;
|
||||
SendMessage(new GasFilterSelectGasMessage(gas));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using Content.Client.BarSign.Ui;
|
||||
using Content.Shared.BarSign;
|
||||
using Content.Shared.Power;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.BarSign;
|
||||
|
||||
public sealed class BarSignSystem : VisualizerSystem<BarSignComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<BarSignComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
|
||||
}
|
||||
|
||||
private void OnAfterAutoHandleState(EntityUid uid, BarSignComponent component, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (_ui.TryGetOpenUi<BarSignBoundUserInterface>(uid, BarSignUiKey.Key, out var bui))
|
||||
bui.Update(component.Current);
|
||||
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, BarSignComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
UpdateAppearance(uid, component, args.Component, args.Sprite);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid id, BarSignComponent sign, AppearanceComponent? appearance = null, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (!Resolve(id, ref appearance, ref sprite))
|
||||
return;
|
||||
|
||||
AppearanceSystem.TryGetData<bool>(id, PowerDeviceVisuals.Powered, out var powered, appearance);
|
||||
|
||||
if (powered
|
||||
&& sign.Current != null
|
||||
&& _prototypeManager.Resolve(sign.Current, out var proto))
|
||||
{
|
||||
SpriteSystem.LayerSetSprite((id, sprite), 0, proto.Icon);
|
||||
sprite.LayerSetShader(0, "unshaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
SpriteSystem.LayerSetRsiState((id, sprite), 0, "empty");
|
||||
sprite.LayerSetShader(0, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Content.Shared.BarSign;
|
||||
using Content.Shared.Power;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.BarSign;
|
||||
|
||||
public sealed class BarSignVisualizerSystem : VisualizerSystem<BarSignComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, BarSignComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
AppearanceSystem.TryGetData<bool>(uid, PowerDeviceVisuals.Powered, out var powered, args.Component);
|
||||
AppearanceSystem.TryGetData<string>(uid, BarSignVisuals.BarSignPrototype, out var currentSign, args.Component);
|
||||
|
||||
if (powered
|
||||
&& currentSign != null
|
||||
&& _prototypeManager.Resolve<BarSignPrototype>(currentSign, out var proto))
|
||||
{
|
||||
SpriteSystem.LayerSetSprite((uid, args.Sprite), 0, proto.Icon);
|
||||
args.Sprite?.LayerSetShader(0, "unshaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), 0, "empty");
|
||||
args.Sprite?.LayerSetShader(0, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,27 +19,32 @@ public sealed class BarSignBoundUserInterface(EntityUid owner, Enum uiKey) : Bou
|
||||
var sign = EntMan.GetComponentOrNull<BarSignComponent>(Owner)?.Current is { } current
|
||||
? _prototype.Index(current)
|
||||
: null;
|
||||
var allSigns = BarSignSystem.GetAllBarSigns(_prototype)
|
||||
var allSigns = Shared.BarSign.BarSignSystem.GetAllBarSigns(_prototype)
|
||||
.OrderBy(p => Loc.GetString(p.Name))
|
||||
.ToList();
|
||||
_menu = new(sign, allSigns);
|
||||
|
||||
_menu.OnSignSelected += id =>
|
||||
{
|
||||
SendPredictedMessage(new SetBarSignMessage(id));
|
||||
SendMessage(new SetBarSignMessage(id));
|
||||
};
|
||||
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
public void Update(ProtoId<BarSignPrototype>? sign)
|
||||
{
|
||||
if (!EntMan.TryGetComponent<BarSignComponent>(Owner, out var signComp))
|
||||
return;
|
||||
|
||||
if (_prototype.Resolve(signComp.Current, out var signPrototype))
|
||||
if (_prototype.Resolve(sign, out var signPrototype))
|
||||
_menu?.UpdateState(signPrototype);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Bed;
|
||||
|
||||
namespace Content.Client.Bed;
|
||||
|
||||
public sealed class BedSystem : SharedBedSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,7 +1,76 @@
|
||||
using Content.Shared.Body.Systems;
|
||||
// Shitmed Change Start
|
||||
using Content.Shared._Shitmed.Body.Part;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Body.Components;
|
||||
// Shitmed Change End
|
||||
|
||||
namespace Content.Client.Body.Systems;
|
||||
|
||||
public sealed class BodySystem : SharedBodySystem
|
||||
{
|
||||
// Shitmed Change Start
|
||||
[Dependency] private readonly MarkingManager _markingManager = default!;
|
||||
|
||||
private void ApplyMarkingToPart(MarkingPrototype markingPrototype,
|
||||
IReadOnlyList<Color>? colors,
|
||||
bool visible,
|
||||
SpriteComponent sprite)
|
||||
{
|
||||
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
|
||||
{
|
||||
var markingSprite = markingPrototype.Sprites[j];
|
||||
|
||||
if (markingSprite is not SpriteSpecifier.Rsi rsi)
|
||||
continue;
|
||||
|
||||
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
|
||||
|
||||
if (!sprite.LayerMapTryGet(layerId, out _))
|
||||
{
|
||||
var layer = sprite.AddLayer(markingSprite, j + 1);
|
||||
sprite.LayerMapSet(layerId, layer);
|
||||
sprite.LayerSetSprite(layerId, rsi);
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(layerId, visible);
|
||||
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
|
||||
// and we need to check the index is correct. So if that happens just default to white?
|
||||
if (colors != null && j < colors.Count)
|
||||
sprite.LayerSetColor(layerId, colors[j]);
|
||||
else
|
||||
sprite.LayerSetColor(layerId, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyPartMarkings(EntityUid target, BodyPartAppearanceComponent component)
|
||||
{
|
||||
if (!TryComp(target, out SpriteComponent? sprite))
|
||||
return;
|
||||
|
||||
if (component.Color != null)
|
||||
sprite.Color = component.Color.Value;
|
||||
|
||||
foreach (var (visualLayer, markingList) in component.Markings)
|
||||
foreach (var marking in markingList)
|
||||
{
|
||||
if (!_markingManager.TryGetMarking(marking, out var markingPrototype))
|
||||
continue;
|
||||
|
||||
ApplyMarkingToPart(markingPrototype, marking.MarkingColors, marking.Visible, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveBodyMarkings(EntityUid target, BodyPartAppearanceComponent partAppearance, HumanoidAppearanceComponent bodyAppearance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Shitmed Change End
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Serilog;
|
||||
|
||||
namespace Content.Client.Cargo.UI;
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.RichText;
|
||||
using Content.Client.UserInterface.RichText;
|
||||
using Content.Client.UserInterface.RichText; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
using Content.Shared.MassMedia.Systems;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Client.UserInterface.RichText; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Utility; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
|
||||
namespace Content.Client.CartridgeLoader.Cartridges;
|
||||
|
||||
@@ -18,6 +17,18 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
|
||||
|
||||
public event Action? OnNotificationSwithPressed;
|
||||
|
||||
// DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
private static readonly Type[] AllowedTags =
|
||||
[
|
||||
typeof(BoldItalicTag),
|
||||
typeof(BoldTag),
|
||||
typeof(BulletTag),
|
||||
typeof(ColorTag),
|
||||
typeof(HeadingTag),
|
||||
typeof(ItalicTag),
|
||||
typeof(MonoTag),
|
||||
];
|
||||
|
||||
public NewsReaderUiFragment()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -35,7 +46,7 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
|
||||
Author.Visible = true;
|
||||
|
||||
PageName.Text = article.Title;
|
||||
PageText.SetMessage(FormattedMessage.FromMarkupPermissive(article.Content), UserFormattableTags.BaseAllowedTags);
|
||||
PageText.SetMessage(FormattedMessage.FromMarkupPermissive(article.Content), AllowedTags); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
|
||||
PageNum.Text = $"{targetNum}/{totalNum}";
|
||||
|
||||
@@ -44,8 +55,8 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
|
||||
var shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
|
||||
ShareTime.SetMarkup(Loc.GetString("news-read-ui-time-prefix-text") + " " + shareTime);
|
||||
|
||||
var author = Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author ?? Loc.GetString("news-read-ui-no-author"));
|
||||
Author.SetMessage(FormattedMessage.FromMarkupPermissive(author), UserFormattableTags.BaseAllowedTags);
|
||||
var author = Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author ?? Loc.GetString("news-read-ui-no-author")); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
Author.SetMessage(FormattedMessage.FromMarkupPermissive(author), AllowedTags); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
|
||||
|
||||
Prev.Disabled = targetNum <= 1;
|
||||
Next.Disabled = targetNum >= totalNum;
|
||||
|
||||
@@ -44,10 +44,6 @@ namespace Content.Client.Chemistry.UI
|
||||
(uint) _window.BottleDosage.Value, _window.LabelLine));
|
||||
_window.BufferSortButton.OnPressed += _ => SendMessage(
|
||||
new ChemMasterSortingTypeCycleMessage());
|
||||
_window.OutputBufferDraw.OnPressed += _ => SendMessage(
|
||||
new ChemMasterOutputDrawSourceMessage(ChemMasterDrawSource.Internal));
|
||||
_window.OutputBeakerDraw.OnPressed += _ => SendMessage(
|
||||
new ChemMasterOutputDrawSourceMessage(ChemMasterDrawSource.External));
|
||||
|
||||
for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
|
||||
{
|
||||
|
||||
@@ -83,13 +83,10 @@
|
||||
|
||||
<!-- Packaging -->
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'chem-master-output-source'}" StyleClasses="LabelSecondaryColor" Margin="0 0 5 0"/>
|
||||
<Button MinSize="80 0" Name="OutputBufferDraw" Access="Public" Text="{Loc 'chem-master-output-buffer-draw'}" ToggleMode="True" StyleClasses="OpenRight" />
|
||||
<Button MinSize="80 0" Name="OutputBeakerDraw" Access="Public" Text="{Loc 'chem-master-output-beaker-draw'}" ToggleMode="True" StyleClasses="OpenLeft" />
|
||||
<Label Text="{Loc 'chem-master-window-packaging-text'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<!-- Output Draw Source -->
|
||||
<Label Name="DrawSource"/>
|
||||
<Label Name="BufferCurrentVolume" StyleClasses="LabelSecondaryColor" />
|
||||
<Label Text="{Loc 'chem-master-window-container-label'}" /> <!-- DeltaV-->
|
||||
<Label Name="BufferCurrentVolume" StyleClasses="LabelWeak" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Wrap the packaging info-->
|
||||
|
||||
@@ -150,17 +150,7 @@ namespace Content.Client.Chemistry.UI
|
||||
// Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on
|
||||
UpdatePanelInfo(castState);
|
||||
|
||||
switch (castState.DrawSource)
|
||||
{
|
||||
case ChemMasterDrawSource.Internal:
|
||||
SetBufferText(castState.BufferCurrentVolume, "chem-master-output-buffer-draw");
|
||||
break;
|
||||
case ChemMasterDrawSource.External:
|
||||
SetBufferText(castState.InputContainerInfo?.CurrentVolume, "chem-master-output-beaker-draw");
|
||||
break;
|
||||
default:
|
||||
throw new($"Chemmaster {castState.OutputContainerInfo} draw source is not set");
|
||||
}
|
||||
BufferCurrentVolume.Text = $" {castState.InputContainerInfo?.CurrentVolume.Int() ?? 0}u"; // DeltaV
|
||||
|
||||
InputEjectButton.Disabled = castState.InputContainerInfo is null;
|
||||
OutputEjectButton.Disabled = castState.OutputContainerInfo is null;
|
||||
@@ -178,14 +168,9 @@ namespace Content.Client.Chemistry.UI
|
||||
var holdsReagents = output?.Reagents != null;
|
||||
var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
|
||||
var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
|
||||
var outputVolume = castState.DrawSource switch
|
||||
{
|
||||
ChemMasterDrawSource.Internal => castState.BufferCurrentVolume?.Int() ?? 0,
|
||||
ChemMasterDrawSource.External => castState.InputContainerInfo?.CurrentVolume.Int() ?? 0,
|
||||
_ => 0,
|
||||
};
|
||||
var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0;
|
||||
|
||||
PillDosage.Value = (int)Math.Min(outputVolume, castState.PillDosageLimit);
|
||||
PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit);
|
||||
|
||||
PillTypeButtons[castState.SelectedPillType].Pressed = true;
|
||||
|
||||
@@ -201,35 +186,25 @@ namespace Content.Client.Chemistry.UI
|
||||
// Avoid division by zero
|
||||
if (PillDosage.Value > 0)
|
||||
{
|
||||
PillNumber.Value = Math.Min(outputVolume / PillDosage.Value, pillNumberMax);
|
||||
PillNumber.Value = Math.Min(bufferVolume / PillDosage.Value, pillNumberMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
PillNumber.Value = 0;
|
||||
}
|
||||
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, outputVolume);
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, bufferVolume);
|
||||
}
|
||||
/// <summary>
|
||||
/// Generate a product label based on reagents in the buffer or beaker.
|
||||
/// Generate a product label based on reagents in the buffer.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
private string GenerateLabel(ChemMasterBoundUserInterfaceState state)
|
||||
{
|
||||
if (
|
||||
state.BufferCurrentVolume == 0 && state.DrawSource == ChemMasterDrawSource.Internal ||
|
||||
state.InputContainerInfo?.CurrentVolume == 0 && state.DrawSource == ChemMasterDrawSource.External ||
|
||||
state.InputContainerInfo?.Reagents == null
|
||||
)
|
||||
if (state.BufferCurrentVolume == 0)
|
||||
return "";
|
||||
|
||||
var reagent = (state.DrawSource switch
|
||||
{
|
||||
ChemMasterDrawSource.Internal => state.BufferReagents,
|
||||
ChemMasterDrawSource.External => state.InputContainerInfo.Reagents ?? [],
|
||||
_ => throw new($"Chemmaster {state.OutputContainerInfo} draw source is not set"),
|
||||
}).MinBy(r => r.Quantity)
|
||||
.Reagent;
|
||||
var reagent = state.BufferReagents.OrderBy(r => r.Quantity).First().Reagent;
|
||||
_prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto);
|
||||
return proto?.LocalizedName ?? "";
|
||||
}
|
||||
@@ -255,8 +230,6 @@ namespace Content.Client.Chemistry.UI
|
||||
_ => Loc.GetString("chem-master-window-sort-type-none")
|
||||
};
|
||||
|
||||
OutputBufferDraw.Pressed = state.DrawSource == ChemMasterDrawSource.Internal;
|
||||
OutputBeakerDraw.Pressed = state.DrawSource == ChemMasterDrawSource.External;
|
||||
|
||||
if (!state.BufferReagents.Any())
|
||||
{
|
||||
@@ -438,12 +411,6 @@ namespace Content.Client.Chemistry.UI
|
||||
get => LabelLineEdit.Text;
|
||||
set => LabelLineEdit.Text = value;
|
||||
}
|
||||
|
||||
private void SetBufferText(FixedPoint2? volume, string text)
|
||||
{
|
||||
BufferCurrentVolume.Text = $" {volume ?? FixedPoint2.Zero}u";
|
||||
DrawSource.Text = Loc.GetString(text);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReagentButton : Button
|
||||
|
||||
@@ -2,31 +2,41 @@ using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class TransferAmountBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
|
||||
namespace Content.Client.Chemistry.UI
|
||||
{
|
||||
[ViewVariables]
|
||||
private TransferAmountWindow? _window;
|
||||
|
||||
protected override void Open()
|
||||
[UsedImplicitly]
|
||||
public sealed class TransferAmountBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.Open();
|
||||
_window = this.CreateWindow<TransferAmountWindow>();
|
||||
private IEntityManager _entManager;
|
||||
private EntityUid _owner;
|
||||
[ViewVariables]
|
||||
private TransferAmountWindow? _window;
|
||||
|
||||
if (EntMan.TryGetComponent<SolutionTransferComponent>(Owner, out var comp))
|
||||
_window.SetBounds(comp.MinimumTransferAmount.Int(), comp.MaximumTransferAmount.Int());
|
||||
|
||||
_window.ApplyButton.OnPressed += _ =>
|
||||
public TransferAmountBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
if (int.TryParse(_window.AmountLineEdit.Text, out var i))
|
||||
_owner = owner;
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = this.CreateWindow<TransferAmountWindow>();
|
||||
|
||||
if (_entManager.TryGetComponent<SolutionTransferComponent>(_owner, out var comp))
|
||||
_window.SetBounds(comp.MinimumTransferAmount.Int(), comp.MaximumTransferAmount.Int());
|
||||
|
||||
_window.ApplyButton.OnPressed += _ =>
|
||||
{
|
||||
SendPredictedMessage(new TransferAmountSetValueMessage(FixedPoint2.New(i)));
|
||||
_window.Close();
|
||||
}
|
||||
};
|
||||
if (int.TryParse(_window.AmountLineEdit.Text, out var i))
|
||||
{
|
||||
SendMessage(new TransferAmountSetValueMessage(FixedPoint2.New(i)));
|
||||
_window.Close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,33 +3,34 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TransferAmountWindow : DefaultWindow
|
||||
namespace Content.Client.Chemistry.UI
|
||||
{
|
||||
private int _max = Int32.MaxValue;
|
||||
private int _min = 1;
|
||||
|
||||
public TransferAmountWindow()
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TransferAmountWindow : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
AmountLineEdit.OnTextChanged += OnValueChanged;
|
||||
}
|
||||
private int _max = Int32.MaxValue;
|
||||
private int _min = 1;
|
||||
|
||||
public void SetBounds(int min, int max)
|
||||
{
|
||||
_min = min;
|
||||
_max = max;
|
||||
MinimumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-min", ("amount", _min));
|
||||
MaximumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-max", ("amount", _max));
|
||||
}
|
||||
public TransferAmountWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
AmountLineEdit.OnTextChanged += OnValueChanged;
|
||||
}
|
||||
|
||||
private void OnValueChanged(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount > _max || amount < _min)
|
||||
ApplyButton.Disabled = true;
|
||||
else
|
||||
ApplyButton.Disabled = false;
|
||||
public void SetBounds(int min, int max)
|
||||
{
|
||||
_min = min;
|
||||
_max = max;
|
||||
MinimumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-min", ("amount", _min));
|
||||
MaximumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-max", ("amount", _max));
|
||||
}
|
||||
|
||||
private void OnValueChanged(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount > _max || amount < _min)
|
||||
ApplyButton.Disabled = true;
|
||||
else
|
||||
ApplyButton.Disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Client.DisplacementMap;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Shared._DV.Silicon.IPC; // DeltaV - IPC Snouts
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Clothing.EntitySystems;
|
||||
@@ -102,8 +103,12 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
|
||||
List<PrototypeLayerData>? layers = null;
|
||||
|
||||
// Begin DeltaV Additions - IPC snouts
|
||||
var speciesId = inventory.SpeciesId;
|
||||
|
||||
if (TryComp(args.Equipee, out SnoutHelmetComponent? helmetComponent) && helmetComponent.EnableAlternateHelmet)
|
||||
speciesId = helmetComponent.ReplacementRace;
|
||||
|
||||
// first attempt to get species specific data.
|
||||
if (speciesId != null)
|
||||
item.ClothingVisuals.TryGetValue($"{args.Slot}-{speciesId}", out layers);
|
||||
@@ -115,6 +120,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
if (!TryGetDefaultVisuals(uid, item, args.Slot, speciesId, out layers))
|
||||
return;
|
||||
}
|
||||
// End DeltaV Additions
|
||||
|
||||
// add each layer to the visuals
|
||||
var i = 0;
|
||||
|
||||
@@ -4,8 +4,10 @@ using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Serialization;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.CombatMode;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Client.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the a <see cref="CCVars.DebugQuickInspect"/> CVar to the name of a component, which allows the client to quickly open a VV window for that component
|
||||
/// by using the Alt+C or Alt+B hotkeys.
|
||||
/// </summary>
|
||||
public sealed class QuickInspectCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
public override string Command => "quickinspect";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("shell-wrong-arguments-number"));
|
||||
return;
|
||||
}
|
||||
|
||||
_configurationManager.SetCVar(CCVars.DebugQuickInspect, args[0]);
|
||||
|
||||
var serverKey = _inputManager.GetKeyFunctionButtonString(ContentKeyFunctions.InspectServerComponent);
|
||||
var clientKey = _inputManager.GetKeyFunctionButtonString(ContentKeyFunctions.InspectClientComponent);
|
||||
shell.WriteLine(Loc.GetString($"cmd-quickinspect-success", ("component", args[0]), ("serverKeybind", serverKey), ("clientKeybind", clientKey)));
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
// Not ideal since it only shows client-side components, but you can still type in any name you want.
|
||||
// If you know how to get server component names on the client then please fix this.
|
||||
var options = EntityManager.ComponentFactory.AllRegisteredTypes
|
||||
.Select(p => new CompletionOption(
|
||||
EntityManager.ComponentFactory.GetComponentName(p)
|
||||
));
|
||||
|
||||
return CompletionResult.FromOptions(options);
|
||||
}
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Client.Wall;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Client.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the area in which entities with <see cref="Content.Shared.Wall.WallMountComponent" /> can be interacted from.
|
||||
/// </summary>
|
||||
public sealed class ShowWallmountsCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
|
||||
public override string Command => "showwallmounts";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var existing = _overlay.RemoveOverlay<WallmountDebugOverlay>();
|
||||
if (!existing)
|
||||
_overlay.AddOverlay(new WallmountDebugOverlay());
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-showwallmounts-status", ("status", !existing)));
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,7 @@ namespace Content.Client.Construction.UI
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
private readonly IConstructionMenuView _constructionView;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
@@ -93,7 +90,6 @@ namespace Content.Client.Construction.UI
|
||||
_constructionView = new ConstructionMenu();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
_sawmill = _logManager.GetSawmill("construction.ui");
|
||||
|
||||
// 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))
|
||||
@@ -288,7 +284,7 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
if (!_constructionSystem!.TryGetRecipePrototype(recipe.ID, out var targetProtoId))
|
||||
{
|
||||
_sawmill.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.",
|
||||
Logger.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.",
|
||||
recipe.ID,
|
||||
nameof(ConstructionPrototype));
|
||||
continue;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True" Margin="10">
|
||||
<BoxContainer SizeFlagsStretchRatio="2" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<EntityPrototypeView Name="MachineSprite" Scale="4 4" HorizontalAlignment="Center" VerticalExpand="True" MinSize="128 128"/>
|
||||
<SpriteView Name="MachineSprite" Scale="4 4" HorizontalAlignment="Center" VerticalExpand="True" MinSize="128 128"/>
|
||||
<RichTextLabel Name="MachineNameLabel" HorizontalAlignment="Center" StyleClasses="LabelKeyText"/>
|
||||
</BoxContainer>
|
||||
<Control MinHeight="10"/>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Materials;
|
||||
using Content.Client.Materials.UI;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Materials;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -22,12 +24,14 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
||||
private readonly ItemSlotsSystem _itemSlots;
|
||||
private readonly FlatpackSystem _flatpack;
|
||||
private readonly MaterialStorageSystem _materialStorage;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
private EntityUid _owner;
|
||||
|
||||
public static readonly EntProtoId NoBoardEffectId = "FlatpackerNoBoardEffect";
|
||||
|
||||
private EntityUid? _currentBoard = EntityUid.Invalid;
|
||||
private EntityUid? _machinePreview;
|
||||
|
||||
public event Action? PackButtonPressed;
|
||||
|
||||
@@ -39,6 +43,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
||||
_itemSlots = _entityManager.System<ItemSlotsSystem>();
|
||||
_flatpack = _entityManager.System<FlatpackSystem>();
|
||||
_materialStorage = _entityManager.System<MaterialStorageSystem>();
|
||||
_spriteSystem = _entityManager.System<SpriteSystem>();
|
||||
|
||||
PackButton.OnPressed += _ => PackButtonPressed?.Invoke();
|
||||
|
||||
@@ -55,54 +60,76 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_machinePreview is not { } && _entityManager.Deleted(_machinePreview))
|
||||
{
|
||||
_machinePreview = null;
|
||||
MachineSprite.SetEntity(_machinePreview);
|
||||
}
|
||||
|
||||
if (!_entityManager.TryGetComponent<FlatpackCreatorComponent>(_owner, out var flatpacker) ||
|
||||
!_itemSlots.TryGetSlot(_owner, flatpacker.SlotId, out var itemSlot))
|
||||
return;
|
||||
|
||||
var flatpackerEnt = (_owner, flatpacker);
|
||||
|
||||
MachineBoardComponent? machineBoardComp = null;
|
||||
if (flatpacker.Packing)
|
||||
{
|
||||
PackButton.Disabled = true;
|
||||
}
|
||||
else if (_currentBoard != null)
|
||||
{
|
||||
PackButton.Disabled = !_flatpack.TryGetFlatpackCreationCost(flatpackerEnt, _currentBoard.Value, out var curCost)
|
||||
|| !_materialStorage.CanChangeMaterialAmount(_owner, curCost);
|
||||
Dictionary<string, int> cost;
|
||||
if (_entityManager.TryGetComponent(_currentBoard, out machineBoardComp))
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp));
|
||||
else
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null);
|
||||
|
||||
PackButton.Disabled = !_materialStorage.CanChangeMaterialAmount(_owner, cost);
|
||||
}
|
||||
|
||||
if (_currentBoard == itemSlot.Item)
|
||||
return;
|
||||
|
||||
if (_machinePreview != null)
|
||||
_entityManager.DeleteEntity(_machinePreview);
|
||||
|
||||
_currentBoard = itemSlot.Item;
|
||||
CostHeaderLabel.Visible = false;
|
||||
CostHeaderLabel.Visible = _currentBoard != null;
|
||||
InsertLabel.Visible = _currentBoard == null;
|
||||
|
||||
if (_currentBoard is null)
|
||||
if (_currentBoard is not null)
|
||||
{
|
||||
MachineSprite.SetPrototype(NoBoardEffectId);
|
||||
CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label"));
|
||||
MachineNameLabel.SetMessage(string.Empty);
|
||||
PackButton.Disabled = true;
|
||||
return;
|
||||
}
|
||||
string? prototype = null;
|
||||
Dictionary<string, int>? cost = null;
|
||||
|
||||
if (_flatpack.TryGetFlatpackResultPrototype(_currentBoard.Value, out var prototype) &&
|
||||
_flatpack.TryGetFlatpackCreationCost(flatpackerEnt, _currentBoard.Value, out var cost))
|
||||
{
|
||||
var proto = _prototypeManager.Index<EntityPrototype>(prototype);
|
||||
MachineSprite.SetPrototype(prototype);
|
||||
MachineNameLabel.SetMessage(proto.Name);
|
||||
CostLabel.SetMarkup(GetCostString(cost));
|
||||
CostHeaderLabel.Visible = true;
|
||||
if (machineBoardComp != null || _entityManager.TryGetComponent(_currentBoard, out machineBoardComp))
|
||||
{
|
||||
prototype = machineBoardComp.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp));
|
||||
}
|
||||
else if (_entityManager.TryGetComponent<ComputerBoardComponent>(_currentBoard, out var computerBoard))
|
||||
{
|
||||
prototype = computerBoard.Prototype;
|
||||
cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null);
|
||||
}
|
||||
|
||||
if (prototype is not null && cost is not null)
|
||||
{
|
||||
var proto = _prototypeManager.Index<EntityPrototype>(prototype);
|
||||
_machinePreview = _entityManager.Spawn(proto.ID);
|
||||
_spriteSystem.ForceUpdate(_machinePreview.Value);
|
||||
MachineNameLabel.SetMessage(proto.Name);
|
||||
CostLabel.SetMarkup(GetCostString(cost));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MachineSprite.SetPrototype(NoBoardEffectId);
|
||||
CostLabel.SetMarkup(Loc.GetString("flatpacker-ui-board-invalid-label"));
|
||||
MachineNameLabel.SetMessage(string.Empty);
|
||||
_machinePreview = _entityManager.Spawn(NoBoardEffectId);
|
||||
CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label"));
|
||||
MachineNameLabel.SetMessage(" ");
|
||||
PackButton.Disabled = true;
|
||||
}
|
||||
|
||||
MachineSprite.SetEntity(_machinePreview);
|
||||
}
|
||||
|
||||
private string GetCostString(Dictionary<string, int> costs)
|
||||
@@ -124,7 +151,7 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
||||
("amount", amountText),
|
||||
("material", Loc.GetString(matProto.Name)));
|
||||
|
||||
msg.TryAddMarkup(text, out _);
|
||||
msg.AddMarkup(text);
|
||||
|
||||
if (i != orderedCosts.Length - 1)
|
||||
msg.PushNewline();
|
||||
@@ -132,4 +159,12 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow
|
||||
|
||||
return msg.ToMarkup();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
|
||||
_entityManager.DeleteEntity(_machinePreview);
|
||||
_machinePreview = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
|
||||
<WarningsAsErrors>RA0032;nullable</WarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<Configurations>Debug;Release;Tools;DebugOpt</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nett" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="Pidgin" />
|
||||
<PackageReference Include="Robust.Shared.AuthLib" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.DeviceLinking.Systems;
|
||||
|
||||
namespace Content.Client.DeviceLinking.Systems;
|
||||
|
||||
public sealed class RandomGateSystem : SharedRandomGateSystem;
|
||||
@@ -1,37 +0,0 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.DeviceLinking.UI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class RandomGateBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private RandomGateSetupWindow? _window;
|
||||
|
||||
public RandomGateBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = this.CreateWindow<RandomGateSetupWindow>();
|
||||
_window.OnApplyPressed += OnProbabilityChanged;
|
||||
}
|
||||
|
||||
private void OnProbabilityChanged(string value)
|
||||
{
|
||||
if (!float.TryParse(value, out var probability))
|
||||
return;
|
||||
|
||||
SendPredictedMessage(new RandomGateProbabilityChangedMessage(probability));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (state is not RandomGateBoundUserInterfaceState castState || _window == null)
|
||||
return;
|
||||
|
||||
_window.SetProbability(castState.SuccessProbability * 100);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'random-gate-menu-setup'}"
|
||||
MinSize="260 115">
|
||||
<BoxContainer Orientation="Vertical" Margin="5" SeparationOverride="10">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc 'random-gate-menu-settings'}" VerticalAlignment="Center" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<LineEdit Name="ProbabilityInput" MinSize="70 0" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Control HorizontalExpand="True" />
|
||||
<Button Name="ApplyButton"
|
||||
Text="{Loc 'random-gate-menu-apply'}"
|
||||
HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -1,28 +0,0 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.DeviceLinking.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Window for setting up the random gate probability.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RandomGateSetupWindow : FancyWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the "Apply" button is pressed.
|
||||
/// </summary>
|
||||
public event Action<string>? OnApplyPressed;
|
||||
|
||||
public RandomGateSetupWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
ApplyButton.OnPressed += _ => OnApplyPressed?.Invoke(ProbabilityInput.Text);
|
||||
}
|
||||
|
||||
public void SetProbability(float probability)
|
||||
{
|
||||
ProbabilityInput.Text = probability.ToString("0.00");
|
||||
}
|
||||
}
|
||||
@@ -18,36 +18,8 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DoorComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
// ES START
|
||||
// handle open/close state change on client after animation end, not when server says door is open
|
||||
SubscribeLocalEvent<DoorComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<DoorComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(entity, out var sprite) || args.Key != DoorComponent.OpenCloseKey)
|
||||
return;
|
||||
|
||||
switch (entity.Comp.State)
|
||||
{
|
||||
case DoorState.Open:
|
||||
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
|
||||
{
|
||||
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
|
||||
}
|
||||
|
||||
break;
|
||||
case DoorState.Closed:
|
||||
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
|
||||
{
|
||||
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ES END
|
||||
|
||||
protected override void OnComponentInit(Entity<DoorComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
var comp = ent.Comp;
|
||||
@@ -115,13 +87,10 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
state = DoorState.Closed;
|
||||
|
||||
if (AppearanceSystem.TryGetData<string>(entity, PaintableVisuals.Prototype, out var prototype, args.Component))
|
||||
UpdateSpriteLayers((entity.Owner, args.Sprite), prototype);
|
||||
UpdateSpriteLayers((entity.Owner, args.Sprite), prototype);
|
||||
|
||||
// ES START
|
||||
// dont stop all animations for no reason
|
||||
//if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
||||
// _animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
||||
// ES END
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
||||
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
||||
|
||||
// We are checking beforehand since some doors may not have an emagging visual layer, and we don't want LayerSetVisible to throw an error.
|
||||
if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false))
|
||||
@@ -137,12 +106,6 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
switch (state)
|
||||
{
|
||||
case DoorState.Open:
|
||||
// ES START
|
||||
// If we are already animating the close just let that do its job
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
|
||||
return;
|
||||
// ES END
|
||||
|
||||
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
|
||||
{
|
||||
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
|
||||
@@ -150,12 +113,6 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
|
||||
return;
|
||||
case DoorState.Closed:
|
||||
// ES START
|
||||
// If we are already animating the close just let that do its job
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
|
||||
return;
|
||||
// ES END
|
||||
|
||||
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
|
||||
{
|
||||
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
|
||||
@@ -166,47 +123,24 @@ public sealed class DoorSystem : SharedDoorSystem
|
||||
if (entity.Comp.OpeningAnimationTime == TimeSpan.Zero)
|
||||
return;
|
||||
|
||||
// ES START
|
||||
// since we dont stop them earlier we check here
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
|
||||
return;
|
||||
// ES END
|
||||
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.OpenCloseKey);
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.AnimationKey);
|
||||
|
||||
return;
|
||||
case DoorState.Closing:
|
||||
if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero || entity.Comp.CurrentlyCrushing.Count != 0)
|
||||
return;
|
||||
|
||||
// ES START
|
||||
// since we dont stop them earlier we check here
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey))
|
||||
return;
|
||||
// ES END
|
||||
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.OpenCloseKey);
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.AnimationKey);
|
||||
|
||||
return;
|
||||
case DoorState.Denying:
|
||||
// ES START
|
||||
// AnimationKey -> DenyKey
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.DenyKey))
|
||||
return;
|
||||
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.DenyKey);
|
||||
// ES END
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.AnimationKey);
|
||||
|
||||
return;
|
||||
case DoorState.Emagging:
|
||||
// ES START
|
||||
// AnimationKey -> DenyKey
|
||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.EmagKey))
|
||||
return;
|
||||
|
||||
// We are checking beforehand since some doors may not have an emagging visual layer.
|
||||
if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false))
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.EmagKey);
|
||||
// ES END
|
||||
_animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,9 +36,6 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
UpdatesAfter.Add(typeof(TransformSystem));
|
||||
UpdatesAfter.Add(typeof(Robust.Client.Physics.PhysicsSystem));
|
||||
UpdatesBefore.Add(typeof(SharedEyeSystem));
|
||||
// ES START
|
||||
UpdatesBefore.Add(typeof(EyeSystem));
|
||||
// ES END
|
||||
UpdatesOutsidePrediction = true;
|
||||
}
|
||||
|
||||
@@ -71,11 +68,6 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
{
|
||||
_eye.SetRotation(uid, lerpInfo.TargetRotation, component);
|
||||
_eye.SetZoom(uid, lerpInfo.TargetZoom, component);
|
||||
|
||||
// Starlight begin - fix ES Screenshake triggering on spawn
|
||||
if (TryComp<ContentEyeComponent>(uid, out var contentEye))
|
||||
contentEye.BaseRotation = lerpInfo.TargetRotation;
|
||||
// Starlight end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,15 +175,9 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
{
|
||||
var tickFraction = (float) _gameTiming.TickFraction / ushort.MaxValue;
|
||||
const double lerpMinimum = 0.00001;
|
||||
// ES START
|
||||
// add contenteye bc we only want to modify the fuckin base rotation
|
||||
var query = AllEntityQuery<LerpingEyeComponent, EyeComponent, ContentEyeComponent, TransformComponent>();
|
||||
// ES END
|
||||
var query = AllEntityQuery<LerpingEyeComponent, EyeComponent, TransformComponent>();
|
||||
|
||||
// ES START
|
||||
// contenteye
|
||||
while (query.MoveNext(out var entity, out var lerpInfo, out var eye, out var contentEye, out var xform))
|
||||
// ES END
|
||||
while (query.MoveNext(out var entity, out var lerpInfo, out var eye, out var xform))
|
||||
{
|
||||
// Handle zoom
|
||||
var zoomDiff = Vector2.Lerp(lerpInfo.LastZoom, lerpInfo.TargetZoom, tickFraction);
|
||||
@@ -205,14 +191,6 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
_eye.SetZoom(entity, zoomDiff, eye);
|
||||
}
|
||||
|
||||
// ES START
|
||||
// UHHHHHHHHHHHh
|
||||
// shit is fucked idk
|
||||
// we need to alter baserotation not regular eye rotation and also i added baserotation because uhh
|
||||
// there was no good way to separate 'how the eye should be oriented because of the grid/turning/etc' and 'the eye rotating on top of that'
|
||||
// had to add it
|
||||
// ES END
|
||||
|
||||
// Handle Rotation
|
||||
TryComp<InputMoverComponent>(entity, out var mover);
|
||||
|
||||
@@ -221,10 +199,7 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
|
||||
if (!NeedsLerp(mover))
|
||||
{
|
||||
// ES START
|
||||
// contenteye
|
||||
contentEye.BaseRotation = lerpInfo.TargetRotation;
|
||||
// ES END
|
||||
_eye.SetRotation(entity, lerpInfo.TargetRotation, eye);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -232,17 +207,11 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
|
||||
if (Math.Abs(shortest.Theta) < lerpMinimum)
|
||||
{
|
||||
// ES START
|
||||
// contenteye
|
||||
contentEye.BaseRotation = lerpInfo.TargetRotation;
|
||||
// ES END
|
||||
_eye.SetRotation(entity, lerpInfo.TargetRotation, eye);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ES START
|
||||
// contenteye
|
||||
contentEye.BaseRotation = shortest * tickFraction + lerpInfo.LastRotation;
|
||||
// ES END
|
||||
_eye.SetRotation(entity, shortest * tickFraction + lerpInfo.LastRotation, eye);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,19 +73,7 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
// Maybe someday we'll have clientside prediction for entity spawning, but not today.
|
||||
// Until then, these methods do nothing on the client.
|
||||
/// <inheritdoc/>
|
||||
public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity, EntityCoordinates coordinates, out EntityUid puddleUid, out Solution solution, bool sound = true, EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
solution = new Solution();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TrySplashSpillAt(EntityUid entity,
|
||||
EntityCoordinates coordinates,
|
||||
Solution spilled,
|
||||
out EntityUid puddleUid,
|
||||
bool sound = true,
|
||||
EntityUid? user = null)
|
||||
public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
return false;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Fluids.EntitySystems;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.Fluids;
|
||||
|
||||
public sealed class SpraySystem : SharedSpraySystem;
|
||||
@@ -3,7 +3,6 @@ using System.Numerics;
|
||||
using Content.Client.Clickable;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.ComponentTrees;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -14,7 +13,6 @@ using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Input;
|
||||
@@ -42,7 +40,6 @@ namespace Content.Client.Gameplay
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IViewVariablesManager _vvm = default!;
|
||||
[Dependency] private readonly IConsoleHost _conHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
private ClickableEntityComparer _comparer = default!;
|
||||
|
||||
@@ -86,8 +83,6 @@ namespace Content.Client.Gameplay
|
||||
_comparer = new ClickableEntityComparer();
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.InspectEntity, new PointerInputCmdHandler(HandleInspect, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.InspectServerComponent, new PointerInputCmdHandler(HandleInspectServerComponent, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.InspectClientComponent, new PointerInputCmdHandler(HandleInspectClientComponent, outsidePrediction: true))
|
||||
.Register<GameplayStateBase>();
|
||||
}
|
||||
|
||||
@@ -104,21 +99,6 @@ namespace Content.Client.Gameplay
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HandleInspectServerComponent(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var component = _configurationManager.GetCVar(CCVars.DebugQuickInspect);
|
||||
if (_entityManager.TryGetNetEntity(uid, out var net))
|
||||
_conHost.ExecuteCommand($"vv /entity/{net.Value.Id}/{component}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HandleInspectClientComponent(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||
{
|
||||
var component = _configurationManager.GetCVar(CCVars.DebugQuickInspect);
|
||||
_conHost.ExecuteCommand($"vv /c/entity/{uid}/{component}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public EntityUid? GetClickedEntity(MapCoordinates coordinates)
|
||||
{
|
||||
return GetClickedEntity(coordinates, _eyeManager.CurrentEye);
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class GuidebookRichPrototypeLink : Control, IPrototypeLinkControl
|
||||
public void SetMessage(FormattedMessage message)
|
||||
{
|
||||
_message = message;
|
||||
_richTextLabel.SetMessage(_message, tagsAllowed: null);
|
||||
_richTextLabel.SetMessage(_message);
|
||||
}
|
||||
|
||||
public IPrototype? LinkedPrototype { get; set; }
|
||||
|
||||
@@ -82,7 +82,7 @@ public sealed partial class DocumentParsingManager
|
||||
}
|
||||
|
||||
msg.Pop();
|
||||
rt.SetMessage(msg, tagsAllowed: null);
|
||||
rt.SetMessage(msg);
|
||||
return rt;
|
||||
},
|
||||
TextParser)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared._Shitmed.Body.Events; // Shitmed Change
|
||||
using Content.Client.DisplacementMap;
|
||||
using Content.Client.Examine;
|
||||
using Content.Client.Strip;
|
||||
using Content.Client.Verbs.UI;
|
||||
using Content.Shared.Body.Part; // Shitmed Change
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
@@ -49,6 +51,8 @@ namespace Content.Client.Hands.Systems
|
||||
SubscribeLocalEvent<HandsComponent, ComponentShutdown>(OnHandsShutdown);
|
||||
SubscribeLocalEvent<HandsComponent, ComponentHandleState>(HandleComponentState);
|
||||
SubscribeLocalEvent<HandsComponent, VisualsChangedEvent>(OnVisualsChanged);
|
||||
SubscribeLocalEvent<HandsComponent, BodyPartRemovedEvent>(HandleBodyPartRemoved); // Shitmed Change
|
||||
SubscribeLocalEvent<HandsComponent, BodyPartDisabledEvent>(HandleBodyPartDisabled); // Shitmed Change
|
||||
|
||||
OnHandSetActive += OnHandActivated;
|
||||
}
|
||||
@@ -202,6 +206,35 @@ namespace Content.Client.Hands.Systems
|
||||
|
||||
#region visuals
|
||||
|
||||
// Shitmed Change Start
|
||||
private void HideLayers(EntityUid uid, HandsComponent component, Entity<BodyPartComponent> part, SpriteComponent? sprite = null)
|
||||
{
|
||||
if (part.Comp.PartType != BodyPartType.Hand || !Resolve(uid, ref sprite, logMissing: false))
|
||||
return;
|
||||
|
||||
var location = part.Comp.Symmetry switch
|
||||
{
|
||||
BodyPartSymmetry.None => HandLocation.Middle,
|
||||
BodyPartSymmetry.Left => HandLocation.Left,
|
||||
BodyPartSymmetry.Right => HandLocation.Right,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(part.Comp.Symmetry))
|
||||
};
|
||||
|
||||
if (component.RevealedLayers.TryGetValue(location, out var revealedLayers))
|
||||
{
|
||||
foreach (var key in revealedLayers)
|
||||
sprite.RemoveLayer(key);
|
||||
|
||||
revealedLayers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args) => HideLayers(uid, component, args.Part);
|
||||
|
||||
private void HandleBodyPartDisabled(EntityUid uid, HandsComponent component, ref BodyPartDisabledEvent args) => HideLayers(uid, component, args.Part);
|
||||
|
||||
// Shitmed Change End
|
||||
|
||||
protected override void HandleEntityInserted(EntityUid uid, HandsComponent hands, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
base.HandleEntityInserted(uid, hands, args);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared._DV.MedicalRecords;
|
||||
using JetBrains.Annotations; // DeltaV - Medical Records
|
||||
using Content.Shared._Shitmed.Targeting; // Shitmed Change
|
||||
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.HealthAnalyzer.UI
|
||||
@@ -20,11 +21,13 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<HealthAnalyzerWindow>();
|
||||
_window.HealthAnalyzer.OnTriageStatusChanged += SendTriageStatusMessage; // DeltaV - Medical Records
|
||||
_window.HealthAnalyzer.OnClaimPatient += SendTriageClaimMessage; // DeltaV - Medical Records
|
||||
_window.OnBodyPartSelected += SendBodyPartMessage; // Shitmed Change
|
||||
_window.OnTriageStatusChanged += SendTriageStatusMessage; // DeltaV - Medical Records
|
||||
_window.OnClaimPatient += SendTriageClaimMessage; // DeltaV - Medical Records
|
||||
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||
}
|
||||
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (_window == null)
|
||||
@@ -36,6 +39,22 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
_window.Populate(cast);
|
||||
}
|
||||
|
||||
// Shitmed Change Start
|
||||
private void SendBodyPartMessage(TargetBodyPart? part, EntityUid target) => SendMessage(new HealthAnalyzerPartMessage(EntMan.GetNetEntity(target), part ?? null));
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnBodyPartSelected -= SendBodyPartMessage;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
|
||||
// Shitmed Change End
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
private void SendTriageStatusMessage(TriageStatus status)
|
||||
=> SendMessage(new HealthAnalyzerTriageStatusMessage(status));
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<BoxContainer
|
||||
xmlns="https://spacestation14.io"
|
||||
VerticalExpand="True"
|
||||
Orientation="Vertical">
|
||||
<Label
|
||||
Name="NoPatientDataText"
|
||||
Text="{Loc health-analyzer-window-no-patient-data-text}" />
|
||||
|
||||
<BoxContainer
|
||||
Name="PatientDataContainer"
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 5">
|
||||
<TextureRect Name="NoDataTex" Access="Public" SetSize="96 96" Visible="false" Stretch="KeepAspectCentered" TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/> <!-- DeltaV - Size changed to 96-->
|
||||
<BoxContainer Margin="5 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
|
||||
<RichTextLabel Name="NameLabel" SetWidth="150" />
|
||||
<Label Name="SpeciesLabel" VerticalAlignment="Top" StyleClasses="LabelSubText" />
|
||||
<Label Name="PartNameLabel"
|
||||
VerticalAlignment="Bottom"
|
||||
StyleClasses="LabelSubText"/>
|
||||
</BoxContainer>
|
||||
<Label Margin="0 0 5 0" HorizontalExpand="True" HorizontalAlignment="Right" VerticalExpand="True"
|
||||
VerticalAlignment="Top" Name="ScanModeLabel"
|
||||
Text="{Loc 'health-analyzer-window-entity-unknown-text'}" />
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Begin DeltaV - Medical Records -->
|
||||
<BoxContainer Name="TriageControls" Orientation="Vertical">
|
||||
<BoxContainer Name="StatusBox"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 8 0 0">
|
||||
</BoxContainer>
|
||||
|
||||
<Button Name="ClaimButton"
|
||||
Text="{Loc 'health-analyzer-window-triage-claim'}"
|
||||
MinSize="60 0" />
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 8 0 0"/>
|
||||
</BoxContainer>
|
||||
<!-- End DeltaV - Medical Records -->
|
||||
|
||||
<GridContainer Margin="0 5 0 0" Columns="2">
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-status-text'}" />
|
||||
<Label Name="StatusLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" />
|
||||
<Label Name="TemperatureLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" />
|
||||
<Label Name="BloodLabel" />
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" />
|
||||
<Label Name="DamageLabel" />
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="AlertsDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<BoxContainer Name="AlertsContainer" Visible="False" Margin="0 5" Orientation="Vertical" HorizontalAlignment="Center">
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
||||
<BoxContainer
|
||||
Name="GroupsContainer"
|
||||
Margin="0 5 0 5"
|
||||
Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
@@ -1,328 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
namespace Content.Client.HealthAnalyzer.UI;
|
||||
|
||||
using Content.Client._DV.Traits.Assorted; // DeltaV
|
||||
using Content.Shared._DV.Traits.Assorted; // DeltaV
|
||||
using Content.Shared._DV.Medical; // DeltaV - Uncloneable
|
||||
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
|
||||
|
||||
// Health analyzer UI is split from its window because it's used by both the
|
||||
// health analyzer item and the cryo pod UI.
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HealthAnalyzerControl : BoxContainer
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly IPrototypeManager _prototypes;
|
||||
private readonly IResourceCache _cache;
|
||||
|
||||
private readonly UnborgableSystem _unborgable; // DeltaV
|
||||
private readonly RedshirtSystem _redshirt; // DeltaV
|
||||
private readonly UncloneableSystem _uncloneable; // DeltaV
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
private readonly ButtonGroup _triageStatusGroup = new();
|
||||
private readonly Dictionary<TriageStatus, Button> _triageControls = new();
|
||||
|
||||
public event Action<TriageStatus>? OnTriageStatusChanged;
|
||||
public event Action? OnClaimPatient;
|
||||
// End DeltaV - Medical Records
|
||||
|
||||
public HealthAnalyzerControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
var dependencies = IoCManager.Instance!;
|
||||
_entityManager = dependencies.Resolve<IEntityManager>();
|
||||
_spriteSystem = _entityManager.System<SpriteSystem>();
|
||||
_prototypes = dependencies.Resolve<IPrototypeManager>();
|
||||
_cache = dependencies.Resolve<IResourceCache>();
|
||||
|
||||
_unborgable = _entityManager.System<UnborgableSystem>(); // DeltaV
|
||||
_redshirt = _entityManager.System<RedshirtSystem>(); // DeltaV
|
||||
_uncloneable = _entityManager.System<UncloneableSystem>(); // DeltaV
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
foreach (var item in Enum.GetValues<TriageStatus>())
|
||||
{
|
||||
var btn = new Button();
|
||||
var ftlKey = item.ToString();
|
||||
btn.Group = _triageStatusGroup;
|
||||
btn.Text = Loc.GetString($"health-analyzer-window-triage-status-{ftlKey}");
|
||||
btn.ToolTip = Loc.GetString($"health-analyzer-window-triage-status-{ftlKey}.ToolTip");
|
||||
btn.OnPressed += _ => OnTriageStatusChanged?.Invoke(item);
|
||||
btn.AddStyleClass("ButtonSquare");
|
||||
StatusBox.AddChild(btn);
|
||||
_triageControls[item] = btn;
|
||||
}
|
||||
StatusBox.Children.First().AddStyleClass("OpenRight");
|
||||
StatusBox.Children.First().RemoveStyleClass("ButtonSquare");
|
||||
StatusBox.Children.Last().AddStyleClass("OpenLeft");
|
||||
StatusBox.Children.Last().RemoveStyleClass("ButtonSquare");
|
||||
ClaimButton.OnPressed += _ => OnClaimPatient?.Invoke();
|
||||
// End DeltaV - Medical Records
|
||||
}
|
||||
|
||||
public void Populate(HealthAnalyzerUiState state)
|
||||
{
|
||||
var target = _entityManager.GetEntity(state.TargetEntity);
|
||||
|
||||
if (target == null
|
||||
|| !_entityManager.TryGetComponent<DamageableComponent>(target, out var damageable))
|
||||
{
|
||||
NoPatientDataText.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
NoPatientDataText.Visible = false;
|
||||
|
||||
// Scan Mode
|
||||
|
||||
ScanModeLabel.Text = state.ScanMode.HasValue
|
||||
? state.ScanMode.Value
|
||||
? Loc.GetString("health-analyzer-window-scan-mode-active")
|
||||
: Loc.GetString("health-analyzer-window-scan-mode-inactive")
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text");
|
||||
|
||||
ScanModeLabel.FontColorOverride = state.ScanMode.HasValue && state.ScanMode.Value ? Color.Green : Color.Red;
|
||||
|
||||
// Patient Information
|
||||
|
||||
var name = new FormattedMessage();
|
||||
name.PushColor(Color.White);
|
||||
name.AddText(_entityManager.HasComponent<MetaDataComponent>(target.Value)
|
||||
? Identity.Name(target.Value, _entityManager)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text"));
|
||||
NameLabel.SetMessage(name);
|
||||
|
||||
SpeciesLabel.Text =
|
||||
_entityManager.TryGetComponent<HumanoidAppearanceComponent>(target.Value,
|
||||
out var humanoidAppearanceComponent)
|
||||
? Loc.GetString(_prototypes.Index<SpeciesPrototype>(humanoidAppearanceComponent.Species).Name)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-species-text");
|
||||
|
||||
// Basic Diagnostic
|
||||
|
||||
TemperatureLabel.Text = !float.IsNaN(state.Temperature)
|
||||
? $"{state.Temperature - Atmospherics.T0C:F1} °C ({state.Temperature:F1} K)"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
BloodLabel.Text = !float.IsNaN(state.BloodLevel)
|
||||
? $"{state.BloodLevel * 100:F1} %"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
StatusLabel.Text =
|
||||
_entityManager.TryGetComponent<MobStateComponent>(target.Value, out var mobStateComponent)
|
||||
? GetStatus(mobStateComponent.CurrentState)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text");
|
||||
|
||||
// Total Damage
|
||||
|
||||
DamageLabel.Text = damageable.TotalDamage.ToString();
|
||||
|
||||
// Alerts
|
||||
// DeltaV traits - This is going to be horrid if we just keep adding things like this.
|
||||
var unborgable = _unborgable.IsUnborgable(target.Value);
|
||||
var redshirt = _redshirt.IsRedshirt(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Redshirt
|
||||
var uncloneable = _uncloneable.IsUncloneable(target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Unclonable
|
||||
// END DeltaV
|
||||
|
||||
var showAlerts = state.Unrevivable == true || state.Bleeding == true;
|
||||
|
||||
AlertsDivider.Visible = showAlerts;
|
||||
AlertsContainer.Visible = showAlerts;
|
||||
|
||||
if (showAlerts)
|
||||
AlertsContainer.RemoveAllChildren();
|
||||
|
||||
if (state.Unrevivable == true)
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-unrevivable-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (state.Bleeding == true)
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-bleeding-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (unborgable) // DeltaV
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-unborgable-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (redshirt) // DeltaV
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-redshirt-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (uncloneable) // DeltaV - Uncloneable
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-uncloneable-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
// Damage Groups
|
||||
|
||||
var damageSortedGroups =
|
||||
damageable.DamagePerGroup.OrderByDescending(damage => damage.Value)
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
|
||||
|
||||
DrawDiagnosticGroups(damageSortedGroups, damagePerType);
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
if (state.MedicalRecord is not { } records)
|
||||
{
|
||||
TriageControls.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
TriageControls.Visible = true;
|
||||
_triageControls[records.Status].Pressed = true;
|
||||
|
||||
// Update claim button based on claimed status
|
||||
if (records.ClaimedName != null)
|
||||
{
|
||||
ClaimButton.Text = Loc.GetString("health-analyzer-window-triage-unclaim", ("claimedBy", records.ClaimedName));
|
||||
}
|
||||
else
|
||||
{
|
||||
ClaimButton.Text = Loc.GetString("health-analyzer-window-triage-claim");
|
||||
}
|
||||
// End DeltaV - Medical Records
|
||||
}
|
||||
|
||||
private static string GetStatus(MobState mobState)
|
||||
{
|
||||
return mobState switch
|
||||
{
|
||||
MobState.Alive => Loc.GetString("health-analyzer-window-entity-alive-text"),
|
||||
MobState.Critical => Loc.GetString("health-analyzer-window-entity-critical-text"),
|
||||
MobState.Dead => Loc.GetString("health-analyzer-window-entity-dead-text"),
|
||||
_ => Loc.GetString("health-analyzer-window-entity-unknown-text"),
|
||||
};
|
||||
}
|
||||
|
||||
private void DrawDiagnosticGroups(
|
||||
Dictionary<string, FixedPoint2> groups,
|
||||
IReadOnlyDictionary<string, FixedPoint2> damageDict)
|
||||
{
|
||||
GroupsContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var (damageGroupId, damageAmount) in groups)
|
||||
{
|
||||
if (damageAmount == 0)
|
||||
continue;
|
||||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
var groupContainer = new BoxContainer
|
||||
{
|
||||
Align = AlignMode.Begin,
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
};
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId));
|
||||
|
||||
GroupsContainer.AddChild(groupContainer);
|
||||
|
||||
// Show the damage for each type in that group.
|
||||
var group = _prototypes.Index<DamageGroupPrototype>(damageGroupId);
|
||||
|
||||
foreach (var type in group.DamageTypes)
|
||||
{
|
||||
if (!damageDict.TryGetValue(type, out var typeAmount) || typeAmount <= 0)
|
||||
continue;
|
||||
|
||||
var damageString = Loc.GetString(
|
||||
"health-analyzer-window-damage-type-text",
|
||||
("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
|
||||
("amount", typeAmount)
|
||||
);
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · ")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Texture GetTexture(string texture)
|
||||
{
|
||||
var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
|
||||
var rsiSprite = new SpriteSpecifier.Rsi(rsiPath, texture);
|
||||
|
||||
var rsi = _cache.GetResource<RSIResource>(rsiSprite.RsiPath).RSI;
|
||||
if (!rsi.TryGetState(rsiSprite.RsiState, out var state))
|
||||
{
|
||||
rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown");
|
||||
}
|
||||
|
||||
return _spriteSystem.Frame0(rsiSprite);
|
||||
}
|
||||
|
||||
private static Label CreateDiagnosticItemLabel(string text)
|
||||
{
|
||||
return new Label
|
||||
{
|
||||
Text = text,
|
||||
};
|
||||
}
|
||||
|
||||
private BoxContainer CreateDiagnosticGroupTitle(string text, string id)
|
||||
{
|
||||
var rootContainer = new BoxContainer
|
||||
{
|
||||
Margin = new Thickness(0, 6, 0, 0),
|
||||
VerticalAlignment = VAlignment.Bottom,
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
};
|
||||
|
||||
rootContainer.AddChild(new TextureRect
|
||||
{
|
||||
SetSize = new Vector2(30, 30),
|
||||
Texture = GetTexture(id.ToLower())
|
||||
});
|
||||
|
||||
rootContainer.AddChild(CreateDiagnosticItemLabel(text));
|
||||
|
||||
return rootContainer;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,253 @@
|
||||
<controls:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:ui="clr-namespace:Content.Client.HealthAnalyzer.UI"
|
||||
MaxHeight="525"
|
||||
MinWidth="300">
|
||||
MinWidth="350"> <!-- Shitmed Change -->
|
||||
<ScrollContainer
|
||||
Margin="5 5 5 5"
|
||||
ReturnMeasure="True"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RootContainer"
|
||||
VerticalExpand="True"
|
||||
Orientation="Vertical">
|
||||
<Label
|
||||
Name="NoPatientDataText"
|
||||
Text="{Loc health-analyzer-window-no-patient-data-text}"/>
|
||||
<!-- Shitmed Change Start -->
|
||||
<Button Name="ReturnButton"
|
||||
Text="{Loc 'health-analyzer-window-return-button-text'}"
|
||||
Margin="0 0 0 10"
|
||||
HorizontalExpand="False"/>
|
||||
<!-- Shitmed Change End -->
|
||||
<BoxContainer
|
||||
Name="PatientDataContainer"
|
||||
Margin="0 0 0 5"
|
||||
Orientation="Vertical">
|
||||
<!-- Shitmed Change Start -->
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
Margin="0 0 0 5">
|
||||
<PanelContainer>
|
||||
<SpriteView OverrideDirection="South"
|
||||
Name="SpriteView"
|
||||
Access="Public"
|
||||
SetSize="96 96"/>
|
||||
<PanelContainer
|
||||
Name="PartView"
|
||||
SetSize="57 96"
|
||||
Margin="18 0 0 0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left">
|
||||
<PanelContainer
|
||||
SetSize="15 33"
|
||||
Margin="4 0 0 0"
|
||||
HorizontalAlignment="Left">
|
||||
<TextureButton
|
||||
Name="RightArmButton"
|
||||
MinSize="15 25"
|
||||
StyleClasses="TargetDollButtonRightArm"
|
||||
VerticalAlignment="Top">
|
||||
</TextureButton>
|
||||
<TextureButton
|
||||
Name="RightHandButton"
|
||||
MinSize="15 15"
|
||||
VerticalAlignment="Bottom"
|
||||
StyleClasses="TargetDollButtonRightHand">
|
||||
</TextureButton>
|
||||
</PanelContainer>
|
||||
<PanelContainer
|
||||
SetSize="43 75"
|
||||
Margin="0 0 0 0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<TextureButton
|
||||
Name="HeadButton"
|
||||
MinSize="28 23"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Center"
|
||||
StyleClasses="TargetDollButtonHead">
|
||||
<!--<PanelContainer
|
||||
SetSize="15 15"
|
||||
Margin="0 9 0 0"
|
||||
HorizontalAlignment="Center">
|
||||
<TextureButton
|
||||
Name="EyesButton"
|
||||
MinSize="15 9"
|
||||
VerticalAlignment="Top"
|
||||
StyleClasses="TargetDollButtonEyes">
|
||||
<TextureRect
|
||||
TexturePath="/Textures/Interface/Targeting/Doll/eyes.png"
|
||||
Stretch="KeepAspectCentered"
|
||||
SetSize="15 9"/>
|
||||
</TextureButton>
|
||||
<TextureButton
|
||||
Name="MouthButton"
|
||||
SetSize="9 6"
|
||||
VerticalAlignment="Bottom"
|
||||
StyleClasses="TargetDollButtonMouth">
|
||||
<TextureRect
|
||||
TexturePath="/Textures/Interface/Targeting/Doll/mouth.png"
|
||||
Stretch="KeepAspectCentered"
|
||||
SetSize="9 6"/>
|
||||
</TextureButton>
|
||||
</PanelContainer>-->
|
||||
</TextureButton>
|
||||
<TextureButton
|
||||
Name="ChestButton"
|
||||
SetSize="28 30"
|
||||
Margin="0 18 0 0"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Center"
|
||||
StyleClasses="TargetDollButtonChest">
|
||||
</TextureButton>
|
||||
<PanelContainer
|
||||
MinSize="38 35"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Center">
|
||||
<TextureButton
|
||||
Name="GroinButton"
|
||||
MinSize="28 15"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Center"
|
||||
StyleClasses="TargetDollButtonGroin">
|
||||
</TextureButton>
|
||||
<PanelContainer
|
||||
MinSize="20 30"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right">
|
||||
<TextureButton
|
||||
Name="LeftLegButton"
|
||||
MinSize="15 28"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Left"
|
||||
StyleClasses="TargetDollButtonLeftLeg">
|
||||
</TextureButton>
|
||||
<TextureButton
|
||||
Name="LeftFootButton"
|
||||
MinSize="20 10"
|
||||
VerticalAlignment="Bottom"
|
||||
StyleClasses="TargetDollButtonLeftFoot">
|
||||
</TextureButton>
|
||||
</PanelContainer>
|
||||
<PanelContainer
|
||||
MinSize="20 30"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Left">
|
||||
<TextureButton
|
||||
Name="RightLegButton"
|
||||
MinSize="15 28"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right"
|
||||
StyleClasses="TargetDollButtonRightLeg">
|
||||
</TextureButton>
|
||||
<TextureButton
|
||||
Name="RightFootButton"
|
||||
MinSize="20 10"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Center"
|
||||
StyleClasses="TargetDollButtonRightFoot">
|
||||
</TextureButton>
|
||||
</PanelContainer>
|
||||
</PanelContainer>
|
||||
</PanelContainer>
|
||||
<PanelContainer
|
||||
SetSize="15 33"
|
||||
Margin="0 0 4 0"
|
||||
HorizontalAlignment="Right">
|
||||
<TextureButton
|
||||
Name="LeftArmButton"
|
||||
MinSize="15 25"
|
||||
StyleClasses="TargetDollButtonLeftArm"
|
||||
VerticalAlignment="Top">
|
||||
</TextureButton>
|
||||
<TextureButton
|
||||
Name="LeftHandButton"
|
||||
MinSize="15 15"
|
||||
VerticalAlignment="Bottom"
|
||||
StyleClasses="TargetDollButtonLeftHand">
|
||||
</TextureButton>
|
||||
</PanelContainer>
|
||||
</PanelContainer>
|
||||
</PanelContainer>
|
||||
<TextureRect Name="NoDataTex"
|
||||
Access="Public"
|
||||
SetSize="96 96"
|
||||
Visible="false"
|
||||
Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/Misc/health_analyzer_out_of_range.png"/> <!-- DeltaV - Size changed to 96 96 to be consistent with shitmed -->
|
||||
<BoxContainer Margin="5 0 0 0"
|
||||
Orientation="Vertical"
|
||||
VerticalAlignment="Top">
|
||||
<RichTextLabel Name="NameLabel"
|
||||
SetWidth="150"/>
|
||||
<Label Name="SpeciesLabel"
|
||||
VerticalAlignment="Top"
|
||||
StyleClasses="LabelSubText"/>
|
||||
<Label Name="PartNameLabel"
|
||||
VerticalAlignment="Bottom"
|
||||
StyleClasses="LabelSubText"/>
|
||||
</BoxContainer>
|
||||
<Label Margin="0 0 5 0"
|
||||
HorizontalExpand="True"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalExpand="True"
|
||||
VerticalAlignment="Top"
|
||||
Name="ScanModeLabel"
|
||||
Text="{Loc 'health-analyzer-window-entity-unknown-text'}"/>
|
||||
</BoxContainer>
|
||||
|
||||
<ui:HealthAnalyzerControl
|
||||
Name="HealthAnalyzer"
|
||||
Access="Public" /> <!-- DeltaV -->
|
||||
<PanelContainer StyleClasses="LowDivider"/>
|
||||
|
||||
<!-- Begin DeltaV - Medical Records -->
|
||||
<BoxContainer Name="TriageControls" Orientation="Vertical">
|
||||
<BoxContainer Name="StatusBox"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 8 0 0">
|
||||
</BoxContainer>
|
||||
|
||||
<Button Name="ClaimButton"
|
||||
Text="{Loc 'health-analyzer-window-triage-claim'}"
|
||||
MinSize="60 0" />
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" Margin="0 8 0 0"/>
|
||||
</BoxContainer>
|
||||
<!-- End DeltaV - Medical Records -->
|
||||
|
||||
<GridContainer Margin="0 5 0 0"
|
||||
Columns="2">
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-status-text'}"/>
|
||||
<Label Name="StatusLabel"/>
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}"/>
|
||||
<Label Name="TemperatureLabel"/>
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}"/>
|
||||
<Label Name="BloodLabel"/>
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}"/>
|
||||
<Label Name="DamageLabel"/>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="AlertsDivider"
|
||||
Visible="False"
|
||||
StyleClasses="LowDivider"/>
|
||||
|
||||
<BoxContainer Name="AlertsContainer"
|
||||
Visible="False"
|
||||
Margin="0 5"
|
||||
Orientation="Vertical"
|
||||
HorizontalAlignment="Center">
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider"/>
|
||||
<!-- Shitmed Change End -->
|
||||
|
||||
<BoxContainer
|
||||
Name="GroupsContainer"
|
||||
Margin="0 5 0 5"
|
||||
Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -1,20 +1,414 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client._DV.Traits.Assorted; // DeltaV
|
||||
using Content.Shared._DV.Traits.Assorted; // DeltaV
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._DV.MedicalRecords; // DeltaV - Medical Records
|
||||
using Content.Shared._Shitmed.Targeting; // Shitmed
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.HealthAnalyzer.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HealthAnalyzerWindow : FancyWindow
|
||||
namespace Content.Client.HealthAnalyzer.UI
|
||||
{
|
||||
public HealthAnalyzerWindow()
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HealthAnalyzerWindow : FancyWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly IPrototypeManager _prototypes;
|
||||
private readonly IResourceCache _cache;
|
||||
private readonly UnborgableSystem _unborgable; // DeltaV
|
||||
private readonly RedshirtSystem _redshirt; // DeltaV
|
||||
|
||||
public void Populate(HealthAnalyzerScannedUserMessage msg)
|
||||
{
|
||||
HealthAnalyzer.Populate(msg.State);
|
||||
// Shitmed Change Start
|
||||
public event Action<TargetBodyPart?, EntityUid>? OnBodyPartSelected;
|
||||
private EntityUid _spriteViewEntity;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private readonly EntProtoId _bodyView = "AlertSpriteView";
|
||||
|
||||
private readonly Dictionary<TargetBodyPart, TextureButton> _bodyPartControls;
|
||||
private EntityUid? _target;
|
||||
// Shitmed Change End
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
private readonly ButtonGroup _triageStatusGroup = new();
|
||||
private readonly Dictionary<TriageStatus, Button> _triageControls = new();
|
||||
|
||||
public event Action<TriageStatus>? OnTriageStatusChanged;
|
||||
public event Action? OnClaimPatient;
|
||||
// End DeltaV - Medical Records
|
||||
|
||||
public HealthAnalyzerWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
var dependencies = IoCManager.Instance!;
|
||||
_entityManager = dependencies.Resolve<IEntityManager>();
|
||||
_spriteSystem = _entityManager.System<SpriteSystem>();
|
||||
_prototypes = dependencies.Resolve<IPrototypeManager>();
|
||||
_cache = dependencies.Resolve<IResourceCache>();
|
||||
_unborgable = _entityManager.System<UnborgableSystem>(); // DeltaV
|
||||
_redshirt = _entityManager.System<RedshirtSystem>(); // DeltaV
|
||||
// Shitmed Change Start
|
||||
_bodyPartControls = new Dictionary<TargetBodyPart, TextureButton>
|
||||
{
|
||||
{ TargetBodyPart.Head, HeadButton },
|
||||
{ TargetBodyPart.Torso, ChestButton },
|
||||
{ TargetBodyPart.Groin, GroinButton },
|
||||
{ TargetBodyPart.LeftArm, LeftArmButton },
|
||||
{ TargetBodyPart.LeftHand, LeftHandButton },
|
||||
{ TargetBodyPart.RightArm, RightArmButton },
|
||||
{ TargetBodyPart.RightHand, RightHandButton },
|
||||
{ TargetBodyPart.LeftLeg, LeftLegButton },
|
||||
{ TargetBodyPart.LeftFoot, LeftFootButton },
|
||||
{ TargetBodyPart.RightLeg, RightLegButton },
|
||||
{ TargetBodyPart.RightFoot, RightFootButton },
|
||||
};
|
||||
|
||||
foreach (var bodyPartButton in _bodyPartControls)
|
||||
{
|
||||
bodyPartButton.Value.MouseFilter = MouseFilterMode.Stop;
|
||||
bodyPartButton.Value.OnPressed += _ => SetActiveBodyPart(bodyPartButton.Key, bodyPartButton.Value);
|
||||
}
|
||||
ReturnButton.OnPressed += _ => ResetBodyPart();
|
||||
// Shitmed Change End
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
foreach (var item in Enum.GetValues<TriageStatus>())
|
||||
{
|
||||
var btn = new Button();
|
||||
var ftlKey = item.ToString();
|
||||
btn.Group = _triageStatusGroup;
|
||||
btn.Text = Loc.GetString($"health-analyzer-window-triage-status-{ftlKey}");
|
||||
btn.ToolTip = Loc.GetString($"health-analyzer-window-triage-status-{ftlKey}.ToolTip");
|
||||
btn.OnPressed += _ => OnTriageStatusChanged?.Invoke(item);
|
||||
btn.AddStyleClass("ButtonSquare");
|
||||
StatusBox.AddChild(btn);
|
||||
_triageControls[item] = btn;
|
||||
}
|
||||
StatusBox.Children.First().AddStyleClass("OpenRight");
|
||||
StatusBox.Children.First().RemoveStyleClass("ButtonSquare");
|
||||
StatusBox.Children.Last().AddStyleClass("OpenLeft");
|
||||
StatusBox.Children.Last().RemoveStyleClass("ButtonSquare");
|
||||
ClaimButton.OnPressed += _ => OnClaimPatient?.Invoke();
|
||||
// End DeltaV - Medical Records
|
||||
}
|
||||
|
||||
// Shitmed Change Start
|
||||
public void SetActiveBodyPart(TargetBodyPart part, TextureButton button)
|
||||
{
|
||||
if (_target == null)
|
||||
return;
|
||||
|
||||
// Bit of the ole shitcode until we have Groins in the prototypes.
|
||||
OnBodyPartSelected?.Invoke(part == TargetBodyPart.Groin ? TargetBodyPart.Torso : part, _target.Value);
|
||||
}
|
||||
|
||||
public void ResetBodyPart()
|
||||
{
|
||||
if (_target == null)
|
||||
return;
|
||||
|
||||
OnBodyPartSelected?.Invoke(null, _target.Value);
|
||||
}
|
||||
|
||||
public void SetActiveButtons(bool isHumanoid)
|
||||
{
|
||||
foreach (var button in _bodyPartControls)
|
||||
button.Value.Visible = isHumanoid;
|
||||
}
|
||||
|
||||
// Not all of this function got messed with, but it was spread enough to warrant being covered entirely by a Shitmed Change
|
||||
public void Populate(HealthAnalyzerScannedUserMessage msg)
|
||||
{
|
||||
// Start-Shitmed
|
||||
_target = _entityManager.GetEntity(msg.TargetEntity);
|
||||
EntityUid? part = msg.Part != null ? _entityManager.GetEntity(msg.Part.Value) : null;
|
||||
var isPart = part != null;
|
||||
|
||||
if (_target == null
|
||||
|| !_entityManager.TryGetComponent<DamageableComponent>(isPart ? part : _target, out var damageable))
|
||||
{
|
||||
NoPatientDataText.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SetActiveButtons(_entityManager.HasComponent<TargetingComponent>(_target.Value));
|
||||
|
||||
ReturnButton.Visible = isPart;
|
||||
PartNameLabel.Visible = isPart;
|
||||
|
||||
if (part != null)
|
||||
PartNameLabel.Text = _entityManager.HasComponent<MetaDataComponent>(part)
|
||||
? Identity.Name(part.Value, _entityManager)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
NoPatientDataText.Visible = false;
|
||||
|
||||
// Scan Mode
|
||||
|
||||
ScanModeLabel.Text = msg.ScanMode.HasValue
|
||||
? msg.ScanMode.Value
|
||||
? Loc.GetString("health-analyzer-window-scan-mode-active")
|
||||
: Loc.GetString("health-analyzer-window-scan-mode-inactive")
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text");
|
||||
|
||||
ScanModeLabel.FontColorOverride = msg.ScanMode.HasValue && msg.ScanMode.Value ? Color.Green : Color.Red;
|
||||
|
||||
// Patient Information
|
||||
|
||||
SpriteView.SetEntity(SetupIcon(msg.Body) ?? _target.Value);
|
||||
SpriteView.Visible = msg.ScanMode.HasValue && msg.ScanMode.Value;
|
||||
PartView.Visible = SpriteView.Visible;
|
||||
NoDataTex.Visible = !SpriteView.Visible;
|
||||
|
||||
var name = new FormattedMessage();
|
||||
name.PushColor(Color.White);
|
||||
name.AddText(_entityManager.HasComponent<MetaDataComponent>(_target.Value)
|
||||
? Identity.Name(_target.Value, _entityManager)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text"));
|
||||
NameLabel.SetMessage(name);
|
||||
|
||||
SpeciesLabel.Text =
|
||||
_entityManager.TryGetComponent<HumanoidAppearanceComponent>(_target.Value,
|
||||
out var humanoidAppearanceComponent)
|
||||
? Loc.GetString(_prototypes.Index<SpeciesPrototype>(humanoidAppearanceComponent.Species).Name)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-species-text");
|
||||
|
||||
// Basic Diagnostic
|
||||
|
||||
TemperatureLabel.Text = !float.IsNaN(msg.Temperature)
|
||||
? $"{msg.Temperature - Atmospherics.T0C:F1} °C ({msg.Temperature:F1} K)"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
BloodLabel.Text = !float.IsNaN(msg.BloodLevel)
|
||||
? $"{msg.BloodLevel * 100:F1} %"
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-value-text");
|
||||
|
||||
StatusLabel.Text =
|
||||
_entityManager.TryGetComponent<MobStateComponent>(_target.Value, out var mobStateComponent)
|
||||
? GetStatus(mobStateComponent.CurrentState)
|
||||
: Loc.GetString("health-analyzer-window-entity-unknown-text");
|
||||
|
||||
// Total Damage
|
||||
|
||||
DamageLabel.Text = damageable.TotalDamage.ToString();
|
||||
|
||||
// Alerts
|
||||
|
||||
var unborgable = _unborgable.IsUnborgable(_target.Value); // DeltaV
|
||||
var redshirt = _redshirt.IsRedshirt(_target.Value) && mobStateComponent?.CurrentState == MobState.Dead; // DeltaV - Redshirt
|
||||
var showAlerts = msg.Unrevivable == true || msg.Bleeding == true || unborgable || redshirt; // DeltaV - Unborgable/Redshirt
|
||||
|
||||
AlertsDivider.Visible = showAlerts;
|
||||
AlertsContainer.Visible = showAlerts;
|
||||
|
||||
if (showAlerts)
|
||||
AlertsContainer.RemoveAllChildren();
|
||||
|
||||
if (msg.Unrevivable == true)
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-unrevivable-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (msg.Bleeding == true)
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-bleeding-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (unborgable) // DeltaV
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-unborgable-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
if (redshirt) // DeltaV
|
||||
AlertsContainer.AddChild(new RichTextLabel
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-entity-redshirt-text"),
|
||||
Margin = new Thickness(0, 4),
|
||||
MaxWidth = 300
|
||||
});
|
||||
|
||||
// Damage Groups
|
||||
|
||||
var damageSortedGroups =
|
||||
damageable.DamagePerGroup.OrderByDescending(damage => damage.Value)
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
|
||||
|
||||
DrawDiagnosticGroups(damageSortedGroups, damagePerType);
|
||||
|
||||
// Begin DeltaV - Medical Records
|
||||
if (msg.MedicalRecord is not {} records)
|
||||
{
|
||||
TriageControls.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_triageControls[records.Status].Pressed = true;
|
||||
// End DeltaV - Medical Records
|
||||
}
|
||||
// Shitmed Change End
|
||||
private static string GetStatus(MobState mobState)
|
||||
{
|
||||
return mobState switch
|
||||
{
|
||||
MobState.Alive => Loc.GetString("health-analyzer-window-entity-alive-text"),
|
||||
MobState.Critical => Loc.GetString("health-analyzer-window-entity-critical-text"),
|
||||
MobState.Dead => Loc.GetString("health-analyzer-window-entity-dead-text"),
|
||||
_ => Loc.GetString("health-analyzer-window-entity-unknown-text"),
|
||||
};
|
||||
}
|
||||
|
||||
private void DrawDiagnosticGroups(
|
||||
Dictionary<string, FixedPoint2> groups,
|
||||
IReadOnlyDictionary<string, FixedPoint2> damageDict)
|
||||
{
|
||||
GroupsContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var (damageGroupId, damageAmount) in groups)
|
||||
{
|
||||
if (damageAmount == 0)
|
||||
continue;
|
||||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
var groupContainer = new BoxContainer
|
||||
{
|
||||
Align = BoxContainer.AlignMode.Begin,
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
};
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId));
|
||||
|
||||
GroupsContainer.AddChild(groupContainer);
|
||||
|
||||
// Show the damage for each type in that group.
|
||||
var group = _prototypes.Index<DamageGroupPrototype>(damageGroupId);
|
||||
|
||||
foreach (var type in group.DamageTypes)
|
||||
{
|
||||
if (!damageDict.TryGetValue(type, out var typeAmount) || typeAmount <= 0)
|
||||
continue;
|
||||
|
||||
var damageString = Loc.GetString(
|
||||
"health-analyzer-window-damage-type-text",
|
||||
("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
|
||||
("amount", typeAmount)
|
||||
);
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · ")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Texture GetTexture(string texture)
|
||||
{
|
||||
var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
|
||||
var rsiSprite = new SpriteSpecifier.Rsi(rsiPath, texture);
|
||||
|
||||
var rsi = _cache.GetResource<RSIResource>(rsiSprite.RsiPath).RSI;
|
||||
if (!rsi.TryGetState(rsiSprite.RsiState, out var state))
|
||||
{
|
||||
rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown");
|
||||
}
|
||||
|
||||
return _spriteSystem.Frame0(rsiSprite);
|
||||
}
|
||||
|
||||
private static Label CreateDiagnosticItemLabel(string text)
|
||||
{
|
||||
return new Label
|
||||
{
|
||||
Text = text,
|
||||
};
|
||||
}
|
||||
|
||||
private BoxContainer CreateDiagnosticGroupTitle(string text, string id)
|
||||
{
|
||||
var rootContainer = new BoxContainer
|
||||
{
|
||||
Margin = new Thickness(0, 6, 0, 0),
|
||||
VerticalAlignment = VAlignment.Bottom,
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
};
|
||||
|
||||
rootContainer.AddChild(new TextureRect
|
||||
{
|
||||
SetSize = new Vector2(30, 30),
|
||||
Texture = GetTexture(id.ToLower())
|
||||
});
|
||||
|
||||
rootContainer.AddChild(CreateDiagnosticItemLabel(text));
|
||||
|
||||
return rootContainer;
|
||||
}
|
||||
|
||||
// Shitmed Change Start
|
||||
/// <summary>
|
||||
/// Sets up the Body Doll using Alert Entity to use in Health Analyzer.
|
||||
/// </summary>
|
||||
private EntityUid? SetupIcon(Dictionary<TargetBodyPart, TargetIntegrity>? body)
|
||||
{
|
||||
if (body is null)
|
||||
return null;
|
||||
|
||||
if (!_entityManager.Deleted(_spriteViewEntity))
|
||||
_entityManager.QueueDeleteEntity(_spriteViewEntity);
|
||||
|
||||
_spriteViewEntity = _entityManager.Spawn(_bodyView);
|
||||
|
||||
if (!_entityManager.TryGetComponent<SpriteComponent>(_spriteViewEntity, out var sprite))
|
||||
return null;
|
||||
|
||||
int layer = 0;
|
||||
foreach (var (bodyPart, integrity) in body)
|
||||
{
|
||||
// TODO: PartStatusUIController and make it use layers instead of TextureRects when EE refactors alerts.
|
||||
string enumName = Enum.GetName(typeof(TargetBodyPart), bodyPart) ?? "Unknown";
|
||||
int enumValue = (int) integrity;
|
||||
var rsi = new SpriteSpecifier.Rsi(new ResPath($"/Textures/_Shitmed/Interface/Targeting/Status/{enumName.ToLowerInvariant()}.rsi"), $"{enumName.ToLowerInvariant()}_{enumValue}");
|
||||
// Shitcode with love from Russia :)
|
||||
if (!_spriteSystem.TryGetLayer(_spriteViewEntity, layer, out _, false))
|
||||
_spriteSystem.AddTextureLayer(_spriteViewEntity, _spriteSystem.Frame0(rsi));
|
||||
else
|
||||
_spriteSystem.LayerSetTexture(_spriteViewEntity, layer, _spriteSystem.Frame0(rsi));
|
||||
_spriteSystem.LayerSetScale(_spriteViewEntity, layer, new Vector2(3f, 3f));
|
||||
layer++;
|
||||
}
|
||||
return _spriteViewEntity;
|
||||
}
|
||||
// Shitmed Change End
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,8 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
foreach (var (key, info) in component.CustomBaseLayers)
|
||||
{
|
||||
oldLayers.Remove(key);
|
||||
SetLayerData(entity, key, info.Id, sexMorph: false, color: info.Color);
|
||||
// Shitmed Change: For whatever reason these weren't actually ignoring the skin color as advertised.
|
||||
SetLayerData(entity, key, info.Id, sexMorph: false, color: info.Color, overrideSkin: true);
|
||||
}
|
||||
|
||||
// hide old layers
|
||||
@@ -105,7 +106,8 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
HumanoidVisualLayers key,
|
||||
string? protoId,
|
||||
bool sexMorph = false,
|
||||
Color? color = null)
|
||||
Color? color = null,
|
||||
bool overrideSkin = false) // Shitmed Change
|
||||
{
|
||||
var component = entity.Comp1;
|
||||
var sprite = entity.Comp2;
|
||||
@@ -126,7 +128,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
var proto = _prototypeManager.Index<HumanoidSpeciesSpriteLayer>(protoId);
|
||||
component.BaseLayers[key] = proto;
|
||||
|
||||
if (proto.MatchSkin)
|
||||
if (proto.MatchSkin && !overrideSkin) // Shitmed Change
|
||||
layer.Color = component.SkinColor.WithAlpha(proto.LayerAlpha);
|
||||
|
||||
if (proto.BaseSprite != null)
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class InfoSection : BoxContainer
|
||||
{
|
||||
TitleLabel.Text = title;
|
||||
if (markup)
|
||||
Content.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()), tagsAllowed: null);
|
||||
Content.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()));
|
||||
else
|
||||
Content.SetMessage(text);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Client.Info
|
||||
}
|
||||
public void SetInfoBlob(string markup)
|
||||
{
|
||||
_richTextLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(markup), tagsAllowed: null);
|
||||
_richTextLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(markup));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,6 @@ namespace Content.Client.Input
|
||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||
common.AddFunction(ContentKeyFunctions.ResetZoom);
|
||||
common.AddFunction(ContentKeyFunctions.InspectEntity);
|
||||
common.AddFunction(ContentKeyFunctions.InspectServerComponent);
|
||||
common.AddFunction(ContentKeyFunctions.InspectClientComponent);
|
||||
common.AddFunction(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
// Begin DeltaV Additions
|
||||
common.AddFunction(ContentKeyFunctions.OpenCHelp);
|
||||
@@ -98,6 +96,18 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.Arcade1);
|
||||
human.AddFunction(ContentKeyFunctions.Arcade2);
|
||||
human.AddFunction(ContentKeyFunctions.Arcade3);
|
||||
// Shitmed Change Start - TODO: Add hands, feet and groin targeting.
|
||||
human.AddFunction(ContentKeyFunctions.TargetHead);
|
||||
human.AddFunction(ContentKeyFunctions.TargetTorso);
|
||||
human.AddFunction(ContentKeyFunctions.TargetLeftArm);
|
||||
human.AddFunction(ContentKeyFunctions.TargetLeftHand);
|
||||
human.AddFunction(ContentKeyFunctions.TargetRightArm);
|
||||
human.AddFunction(ContentKeyFunctions.TargetRightHand);
|
||||
human.AddFunction(ContentKeyFunctions.TargetLeftLeg);
|
||||
human.AddFunction(ContentKeyFunctions.TargetLeftFoot);
|
||||
human.AddFunction(ContentKeyFunctions.TargetRightLeg);
|
||||
human.AddFunction(ContentKeyFunctions.TargetRightFoot);
|
||||
// Shitmed Change End
|
||||
|
||||
// actions should be common (for ghosts, mobs, etc)
|
||||
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared._Shitmed.Targeting.Events; // Shitmed
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Kitchen.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Kitchen.EntitySystems;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class ReagentGrinderSystem : SharedReagentGrinderSystem;
|
||||
@@ -20,10 +20,8 @@ namespace Content.Client.Launcher
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IClipboardManager _clipboard = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private LauncherConnectingGui? _control;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private Page _currentPage;
|
||||
private string? _connectFailReason;
|
||||
@@ -63,8 +61,6 @@ namespace Content.Client.Launcher
|
||||
{
|
||||
_control = new LauncherConnectingGui(this, _random, _prototypeManager, _cfg, _clipboard);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("launcher-ui");
|
||||
|
||||
_userInterfaceManager.StateRoot.AddChild(_control);
|
||||
|
||||
_clientNetManager.ConnectFailed += OnConnectFailed;
|
||||
@@ -119,12 +115,12 @@ namespace Content.Client.Launcher
|
||||
}
|
||||
else
|
||||
{
|
||||
_sawmill.Info($"Redial not possible, no Ss14Address");
|
||||
Logger.InfoS("launcher-ui", $"Redial not possible, no Ss14Address");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_sawmill.Error($"Redial exception: {ex}");
|
||||
Logger.ErrorS("launcher-ui", $"Redial exception: {ex}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Client._Impstation.ReadyManifest; // Impstation - Ready Manifest
|
||||
|
||||
namespace Content.Client.Lobby
|
||||
{
|
||||
@@ -32,7 +31,6 @@ namespace Content.Client.Lobby
|
||||
|
||||
private ClientGameTicker _gameTicker = default!;
|
||||
private ContentAudioSystem _contentAudioSystem = default!;
|
||||
private ReadyManifestSystem _readyManifest = default!; // Impstation - Ready Manifest
|
||||
|
||||
protected override Type? LinkedScreenType { get; } = typeof(LobbyGui);
|
||||
public LobbyGui? Lobby;
|
||||
@@ -50,7 +48,6 @@ namespace Content.Client.Lobby
|
||||
_gameTicker = _entityManager.System<ClientGameTicker>();
|
||||
_contentAudioSystem = _entityManager.System<ContentAudioSystem>();
|
||||
_contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo;
|
||||
_readyManifest = _entityManager.EntitySysManager.GetEntitySystem<ReadyManifestSystem>(); // Impstation - Ready Manifest
|
||||
|
||||
chatController.SetMainChat(true);
|
||||
|
||||
@@ -72,7 +69,6 @@ namespace Content.Client.Lobby
|
||||
Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed;
|
||||
Lobby.ReadyButton.OnPressed += OnReadyPressed;
|
||||
Lobby.ReadyButton.OnToggled += OnReadyToggled;
|
||||
Lobby.ManifestButton.OnPressed += OnManifestPressed; // Impstation - Ready Manifest
|
||||
|
||||
_gameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
||||
_gameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
||||
@@ -93,7 +89,6 @@ namespace Content.Client.Lobby
|
||||
Lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed;
|
||||
Lobby!.ReadyButton.OnPressed -= OnReadyPressed;
|
||||
Lobby!.ReadyButton.OnToggled -= OnReadyToggled;
|
||||
Lobby!.ManifestButton.OnPressed -= OnManifestPressed; // Impstation - Ready Manifest
|
||||
|
||||
Lobby = null;
|
||||
}
|
||||
@@ -125,12 +120,6 @@ namespace Content.Client.Lobby
|
||||
SetReady(args.Pressed);
|
||||
}
|
||||
|
||||
// Impstation - Ready Manifest
|
||||
private void OnManifestPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
_readyManifest.RequestReadyManifest();
|
||||
}
|
||||
|
||||
public override void FrameUpdate(FrameEventArgs e)
|
||||
{
|
||||
if (_gameTicker.IsGameStarted)
|
||||
@@ -193,7 +182,6 @@ namespace Content.Client.Lobby
|
||||
Lobby!.ReadyButton.ToggleMode = false;
|
||||
Lobby!.ReadyButton.Pressed = false;
|
||||
Lobby!.ObserveButton.Disabled = false;
|
||||
Lobby!.ManifestButton.Disabled = true; //imp
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -203,7 +191,6 @@ namespace Content.Client.Lobby
|
||||
Lobby!.ReadyButton.ToggleMode = true;
|
||||
Lobby!.ReadyButton.Disabled = false;
|
||||
Lobby!.ObserveButton.Disabled = true;
|
||||
Lobby!.ManifestButton.Disabled = false; // imp
|
||||
}
|
||||
|
||||
if (_gameTicker.ServerInfoBlob != null)
|
||||
|
||||
@@ -123,10 +123,10 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
_profileEditor.RefreshSpecies();
|
||||
}
|
||||
|
||||
// if (obj.WasModified<TraitPrototype>()) // DeltaV - Refreshed in TraitsTab
|
||||
// {
|
||||
// _profileEditor.RefreshTraits();
|
||||
// }
|
||||
if (obj.WasModified<TraitPrototype>())
|
||||
{
|
||||
_profileEditor.RefreshTraits();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Info.PlaytimeStats;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared._DV.Species; // DeltaV - Hidden species
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -91,10 +90,6 @@ namespace Content.Client.Lobby.UI
|
||||
|
||||
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
||||
{
|
||||
// DeltaV - Ignore hidden species.
|
||||
if (character is HumanoidCharacterProfile hcp && SpeciesHiderSystem.IsHidden(hcp.Species.Id))
|
||||
continue;
|
||||
// End DeltaV
|
||||
numberOfFullSlots++;
|
||||
var characterPickerButton = new CharacterPickerButton(_entManager,
|
||||
_protomanager,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
xmlns:humanoid="clr-namespace:Content.Client.Humanoid"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:ui="clr-namespace:Content.Client.Lobby.UI"
|
||||
xmlns:traits="clr-namespace:Content.Client._DV.Traits.UI"
|
||||
HorizontalExpand="True">
|
||||
<!-- Left side -->
|
||||
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
|
||||
@@ -134,15 +133,12 @@
|
||||
<BoxContainer Name="AntagList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<!-- DV - Traits -->
|
||||
<!-- <BoxContainer Orientation="Vertical" Margin="10"> -->
|
||||
<!-- ~1~ Traits @1@ -->
|
||||
<!-- <ScrollContainer VerticalExpand="True"> -->
|
||||
<!-- <BoxContainer Name="TraitsList" Orientation="Vertical" /> -->
|
||||
<!-- </ScrollContainer> -->
|
||||
<!-- </BoxContainer> -->
|
||||
<traits:TraitsTab Name="Traits"/>
|
||||
<!-- End DV -->
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<!-- Traits -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Name="TraitsList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="MarkingsTab" Orientation="Vertical" Margin="10">
|
||||
<!-- Markings -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
|
||||
@@ -9,7 +9,6 @@ using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Sprite;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared._DV.Species; // DeltaV - Species hider
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.GameTicking;
|
||||
@@ -40,7 +39,6 @@ using System.Globalization;
|
||||
using Content.Client._CD.Records.UI;
|
||||
using Content.Shared._CD.Records;
|
||||
// End CD - Character Records
|
||||
using Content.Shared._DV.Traits; // DV - Traits
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
@@ -182,8 +180,6 @@ namespace Content.Client.Lobby.UI
|
||||
Save?.Invoke();
|
||||
};
|
||||
|
||||
Traits.OnTraitsChanged += OnTraitsSelectionChanged; // DeltaV
|
||||
|
||||
#region Left
|
||||
|
||||
#region Name
|
||||
@@ -243,6 +239,7 @@ namespace Content.Client.Lobby.UI
|
||||
{
|
||||
SpeciesButton.SelectId(args.Id);
|
||||
SetSpecies(_species[args.Id].ID);
|
||||
RefreshTraits(); // DeltaV - Allows for hiding traits
|
||||
UpdateHairPickers();
|
||||
OnSkinColorOnValueChanged();
|
||||
};
|
||||
@@ -465,7 +462,7 @@ namespace Content.Client.Lobby.UI
|
||||
|
||||
TabContainer.SetTabTitle(2, Loc.GetString("humanoid-profile-editor-antags-tab"));
|
||||
|
||||
// RefreshTraits(); // DeltaV
|
||||
RefreshTraits();
|
||||
|
||||
#region Markings
|
||||
|
||||
@@ -518,58 +515,6 @@ namespace Content.Client.Lobby.UI
|
||||
IsDirty = false;
|
||||
}
|
||||
|
||||
// Begin DeltaV - Traits Integration
|
||||
/// <summary>
|
||||
/// Called when trait selection changes in the TraitsTab.
|
||||
/// Updates the profile with the new trait selection.
|
||||
/// </summary>
|
||||
private void OnTraitsSelectionChanged(HashSet<ProtoId<TraitPrototype>> traits)
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
// Remove all existing traits - iterate directly over readonly collection
|
||||
foreach (var existingTrait in Profile.TraitPreferences)
|
||||
{
|
||||
Profile = Profile.WithoutTraitPreference(existingTrait, _prototypeManager);
|
||||
}
|
||||
|
||||
// Add newly selected traits
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
Profile = Profile.WithTraitPreference(trait.Id, _prototypeManager);
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the traits tab with the current profile's selected traits.
|
||||
/// </summary>
|
||||
private void UpdateTraitsSelection()
|
||||
{
|
||||
if (Profile is null)
|
||||
{
|
||||
Traits.SetSelectedTraits(new HashSet<ProtoId<TraitPrototype>>());
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert profile's trait preferences (strings) to ProtoId<TraitPrototype>
|
||||
var selectedTraits = new HashSet<ProtoId<TraitPrototype>>(Profile.TraitPreferences.Count);
|
||||
foreach (var traitId in Profile.TraitPreferences)
|
||||
{
|
||||
// Validate that the trait still exists in prototypes
|
||||
if (_prototypeManager.HasIndex(traitId))
|
||||
{
|
||||
selectedTraits.Add(new ProtoId<TraitPrototype>(traitId));
|
||||
}
|
||||
}
|
||||
|
||||
Traits.SetSelectedTraits(selectedTraits);
|
||||
Traits.UpdateConditions(Profile);
|
||||
}
|
||||
// End DeltaV - Traits Integration
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the flavor text editor status.
|
||||
/// </summary>
|
||||
@@ -604,122 +549,122 @@ namespace Content.Client.Lobby.UI
|
||||
/// <summary>
|
||||
/// Refreshes traits selector
|
||||
/// </summary>
|
||||
// public void RefreshTraits()
|
||||
// {
|
||||
// TraitsList.RemoveAllChildren();
|
||||
//
|
||||
// var traits = _prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
|
||||
// TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
|
||||
//
|
||||
// if (traits.Count < 1)
|
||||
// {
|
||||
// TraitsList.AddChild(new Label
|
||||
// {
|
||||
// Text = Loc.GetString("humanoid-profile-editor-no-traits"),
|
||||
// FontColorOverride = Color.Gray,
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Setup model
|
||||
// Dictionary<string, List<string>> traitGroups = new();
|
||||
// List<string> defaultTraits = new();
|
||||
// traitGroups.Add(TraitCategoryPrototype.Default, defaultTraits);
|
||||
//
|
||||
// foreach (var trait in traits)
|
||||
// {
|
||||
// // Begin DeltaV Additions - Species trait exclusion
|
||||
// if (Profile?.Species is { } selectedSpecies && trait.ExcludedSpecies.Contains(selectedSpecies))
|
||||
// {
|
||||
// Profile = Profile?.WithoutTraitPreference(trait.ID, _prototypeManager);
|
||||
// continue;
|
||||
// }
|
||||
// // End DeltaV Additions
|
||||
//
|
||||
// if (trait.Category == null)
|
||||
// {
|
||||
// defaultTraits.Add(trait.ID);
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (!_prototypeManager.HasIndex(trait.Category))
|
||||
// continue;
|
||||
//
|
||||
// var group = traitGroups.GetOrNew(trait.Category);
|
||||
// group.Add(trait.ID);
|
||||
// }
|
||||
//
|
||||
// // Create UI view from model
|
||||
// foreach (var (categoryId, categoryTraits) in traitGroups)
|
||||
// {
|
||||
// TraitCategoryPrototype? category = null;
|
||||
//
|
||||
// if (categoryId != TraitCategoryPrototype.Default)
|
||||
// {
|
||||
// category = _prototypeManager.Index<TraitCategoryPrototype>(categoryId);
|
||||
// // Label
|
||||
// TraitsList.AddChild(new Label
|
||||
// {
|
||||
// Text = Loc.GetString(category.Name),
|
||||
// Margin = new Thickness(0, 10, 0, 0),
|
||||
// StyleClasses = { StyleClass.LabelHeading },
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// List<TraitPreferenceSelector?> selectors = new();
|
||||
// var selectionCount = 0;
|
||||
//
|
||||
// foreach (var traitProto in categoryTraits)
|
||||
// {
|
||||
// var trait = _prototypeManager.Index<TraitPrototype>(traitProto);
|
||||
// var selector = new TraitPreferenceSelector(trait);
|
||||
//
|
||||
// selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true;
|
||||
// if (selector.Preference)
|
||||
// selectionCount += trait.Cost;
|
||||
//
|
||||
// selector.PreferenceChanged += preference =>
|
||||
// {
|
||||
// if (preference)
|
||||
// {
|
||||
// Profile = Profile?.WithTraitPreference(trait.ID, _prototypeManager);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Profile = Profile?.WithoutTraitPreference(trait.ID, _prototypeManager);
|
||||
// }
|
||||
//
|
||||
// SetDirty();
|
||||
// RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
|
||||
// };
|
||||
// selectors.Add(selector);
|
||||
// }
|
||||
//
|
||||
// // Selection counter
|
||||
// if (category is { MaxTraitPoints: >= 0 })
|
||||
// {
|
||||
// TraitsList.AddChild(new Label
|
||||
// {
|
||||
// Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
|
||||
// FontColorOverride = Color.Gray
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// foreach (var selector in selectors)
|
||||
// {
|
||||
// if (selector == null)
|
||||
// continue;
|
||||
//
|
||||
// if (category is { MaxTraitPoints: >= 0 } &&
|
||||
// selector.Cost + selectionCount > category.MaxTraitPoints)
|
||||
// {
|
||||
// selector.Checkbox.Label.FontColorOverride = Color.Red;
|
||||
// }
|
||||
//
|
||||
// TraitsList.AddChild(selector);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public void RefreshTraits()
|
||||
{
|
||||
TraitsList.RemoveAllChildren();
|
||||
|
||||
var traits = _prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
|
||||
TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
|
||||
|
||||
if (traits.Count < 1)
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("humanoid-profile-editor-no-traits"),
|
||||
FontColorOverride = Color.Gray,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup model
|
||||
Dictionary<string, List<string>> traitGroups = new();
|
||||
List<string> defaultTraits = new();
|
||||
traitGroups.Add(TraitCategoryPrototype.Default, defaultTraits);
|
||||
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
// Begin DeltaV Additions - Species trait exclusion
|
||||
if (Profile?.Species is { } selectedSpecies && trait.ExcludedSpecies.Contains(selectedSpecies))
|
||||
{
|
||||
Profile = Profile?.WithoutTraitPreference(trait.ID, _prototypeManager);
|
||||
continue;
|
||||
}
|
||||
// End DeltaV Additions
|
||||
|
||||
if (trait.Category == null)
|
||||
{
|
||||
defaultTraits.Add(trait.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_prototypeManager.HasIndex(trait.Category))
|
||||
continue;
|
||||
|
||||
var group = traitGroups.GetOrNew(trait.Category);
|
||||
group.Add(trait.ID);
|
||||
}
|
||||
|
||||
// Create UI view from model
|
||||
foreach (var (categoryId, categoryTraits) in traitGroups)
|
||||
{
|
||||
TraitCategoryPrototype? category = null;
|
||||
|
||||
if (categoryId != TraitCategoryPrototype.Default)
|
||||
{
|
||||
category = _prototypeManager.Index<TraitCategoryPrototype>(categoryId);
|
||||
// Label
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString(category.Name),
|
||||
Margin = new Thickness(0, 10, 0, 0),
|
||||
StyleClasses = { StyleClass.LabelHeading },
|
||||
});
|
||||
}
|
||||
|
||||
List<TraitPreferenceSelector?> selectors = new();
|
||||
var selectionCount = 0;
|
||||
|
||||
foreach (var traitProto in categoryTraits)
|
||||
{
|
||||
var trait = _prototypeManager.Index<TraitPrototype>(traitProto);
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
|
||||
selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true;
|
||||
if (selector.Preference)
|
||||
selectionCount += trait.Cost;
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
if (preference)
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, _prototypeManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
Profile = Profile?.WithoutTraitPreference(trait.ID, _prototypeManager);
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
|
||||
};
|
||||
selectors.Add(selector);
|
||||
}
|
||||
|
||||
// Selection counter
|
||||
if (category is { MaxTraitPoints: >= 0 })
|
||||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
|
||||
FontColorOverride = Color.Gray
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var selector in selectors)
|
||||
{
|
||||
if (selector == null)
|
||||
continue;
|
||||
|
||||
if (category is { MaxTraitPoints: >= 0 } &&
|
||||
selector.Cost + selectionCount > category.MaxTraitPoints)
|
||||
{
|
||||
selector.Checkbox.Label.FontColorOverride = Color.Red;
|
||||
}
|
||||
|
||||
TraitsList.AddChild(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the species selector.
|
||||
@@ -729,7 +674,7 @@ namespace Content.Client.Lobby.UI
|
||||
SpeciesButton.Clear();
|
||||
_species.Clear();
|
||||
|
||||
_species.AddRange(_prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart && !SpeciesHiderSystem.IsHidden(o.ID))); // DeltaV - SpeciesHider
|
||||
_species.AddRange(_prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart));
|
||||
_species.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase));
|
||||
var speciesIds = _species.Select(o => o.ID).ToList();
|
||||
|
||||
@@ -900,13 +845,11 @@ namespace Content.Client.Lobby.UI
|
||||
_recordsTab.Update(profile);
|
||||
// End CD - Character Records
|
||||
|
||||
UpdateTraitsSelection(); // DeltaV - Traits
|
||||
|
||||
RefreshAntags();
|
||||
RefreshJobs();
|
||||
RefreshLoadouts();
|
||||
RefreshSpecies();
|
||||
// RefreshTraits(); // DeltaV
|
||||
RefreshTraits();
|
||||
RefreshFlavorText();
|
||||
ReloadPreview();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Prometheus;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user