Compare commits

...
Author SHA1 Message Date
pre-commit-ci[bot] 69b6463898 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-06-26 23:25:57 +00:00
Vanessa 2dc3f61cd8 type fix 2026-06-26 18:12:16 -05:00
Vanessa 164b9be1f9 Added CosCult state checking. 2026-06-26 18:11:52 -05:00
Vanessa eb95b25eaa End Rounds Better 2026-06-26 17:00:12 -05:00
Vanessa d3b13a6c83 I hate submodules. Why did it have to be submodules? (#6117)
Living on a prayer.
2026-06-25 15:07:33 -05:00
Delta-V bot 2ab0163598 Automatic changelog update 2026-06-25 20:04:07 +02:00
pathetic meowmeow 6cdeb3d9cf Suit sensors, but in space? Perish the thought. (#6054) 2026-06-25 12:59:49 -05:00
Delta-V bot d7543bd24a Automatic changelog update 2026-06-25 18:17:46 +02:00
f7f800c439 Tweaked Synthetic (#6090)
* actually not overhauled but tweaked i think

* forgot to stage this one

* make shocking 60% chance instead of 80%, and make shock dmg 5-20 random.

* update comment

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* okay this should work + remember to mention new changesa asdas asd

* I ALREADY STAGED ALL

* important summary comments to give more important info when someone's
too lazy to check the rule

* fix some stuff

* signs of shock

* Update Resources/Prototypes/_DV/Traits/meta.yml

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: wesker <53323309+weskified@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Traits/meta.yml

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: wesker <53323309+weskified@users.noreply.github.com>

* Update Resources/Locale/en-US/_DV/traits/traits.ftl

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: wesker <53323309+weskified@users.noreply.github.com>

* changessss

---------

Signed-off-by: wesker <53323309+weskified@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-25 11:17:26 -05:00
Delta-V bot b6ed84325f Automatic changelog update 2026-06-25 17:37:53 +02:00
ManoobandVanessa 61aee79dc2 Replace nachos recipes (#6099)
* make nachos from tacos and change how cheesy and chili variants get made

* Change WizDen Contentfreeze recipelimit

* Update Resources/Prototypes/_DV/Recipes/Cooking/deep_fryer_recipes.yml

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Manoob <manu.demoen@gmail.com>

* Moved nacho metamorph to DV folder

* newlline, keep file as it was :^)

---------

Signed-off-by: Manoob <manu.demoen@gmail.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-25 10:37:33 -05:00
Delta-V bot 55fe6026ea Automatic changelog update 2026-06-25 17:28:10 +02:00
pathetic meowmeowandVanessa 6dfa12a509 AI? Being able to send faxes? Perish the thought. (#6064)
* AI? Being able to send faxes? Perish the thought.

* Update Resources/Prototypes/Entities/Mobs/Player/silicon.yml

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

---------

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-25 15:27:49 +00:00
Delta-V bot 485fd14f05 Automatic changelog update 2026-06-25 16:07:32 +02:00
Theo 031b37ffcb Washing machines can hold huge items (#6086)
washer size huge
2026-06-25 09:07:12 -05:00
Delta-V bot 06365bdbf0 Automatic changelog update 2026-06-25 15:10:58 +02:00
Tmanzxd 7f18c5f346 Commented out Surgeon Job Requirement for CMO (#6112) 2026-06-25 13:10:38 +00:00
Delta-V bot d407652eb0 Automatic changelog update 2026-06-25 04:28:46 +02:00
Manoob 9108e2c9b5 Fix Low metabolism rate modifiers from truncating to zero (#6104) 2026-06-24 21:28:26 -05:00
VelcroboyandVelcroboy c7ef614f3d Edit towel spawner (#6106)
* Edit towel spawner

* numbers

---------

Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2026-06-24 21:26:51 -05:00
VelcroboyandVelcroboy fa735f7669 Fix Chapel Access (#6105)
Fix chapel access

Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2026-06-24 20:12:36 -05:00
Delta-V bot 6de6421636 Automatic changelog update 2026-06-25 00:03:18 +02:00
Vanessa 3cb24c8b68 Custom Client-Side Radio Colors (#6101) 2026-06-24 22:02:58 +00:00
Delta-V bot 9fad412075 Automatic changelog update 2026-06-24 15:15:21 +02:00
Vanessa fbc3104836 Fix Infinite Cloaking (#6085)
Fix infinite cloaking because I guess power draw doesn't count as using the battery charge.
2026-06-24 15:15:01 +02:00
Delta-V bot e231d9d67c Automatic changelog update 2026-06-24 15:08:40 +02:00
Stxcking 468d929c4b Tweak: AAC Subject Button Colors Return + Words Capitalized (#5567)
* Nuked StyleNano + Added colors to sheetlet

SubjectButtonColors table to define colors (same hex as the ones in StyleNano)
SubjectColorRules to loop rules for applying colors

* Capitalize every single phrase

CONSISTENCY RAHH
2026-06-24 15:08:20 +02:00
Delta-V bot 2f1aaff32c Automatic changelog update 2026-06-24 00:35:07 +02:00
Vanessa 4f49ccde68 Sleepers no longer have an annoucement (#6066)
* Sleepers no longer have an annoucement.

* NT sleepers too.
2026-06-23 18:34:47 -04:00
Vanessaandpre-commit-ci[bot] cb3b74e49e Station-Specific Department Cleanup (#6078)
* Removed some jobs from station specific jobs because they are on all stations.

* Department Prototype Cleanup

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Actually fix brigmedic on the crew monitor

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-23 18:34:44 -04:00
Vanessaandpre-commit-ci[bot] 7d15a39a77 Fixed emergency access locale string (#6044)
* Fixed emergency access locale string

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-23 18:23:22 -04:00
Delta-V bot 3ac816fee1 Automatic changelog update 2026-06-24 00:19:37 +02:00
Delta-V bot b909d9a2e5 Automatic changelog update 2026-06-23 17:27:27 +02:00
Theo 7547752159 Broken bottle slicing (#6092)
add comps
2026-06-23 10:27:07 -05:00
Manoob 9b7819288e Added a fix that resets the cook timers on fryer power changed (#6075) 2026-06-23 09:17:06 -05:00
Delta-V bot 424a4569d8 Automatic changelog update 2026-06-23 14:50:11 +02:00
wesker 12827c0e98 TraitSystem require fix (#6097)
how did i even forget this
2026-06-23 12:49:51 +00:00
Delta-V bot d1e61ba0a2 Automatic changelog update 2026-06-23 12:40:37 +02:00
652e52dbca Nuke/CosCult Global Music and SFX (#5918)
* Extended ServerGlobalSoundSystem and exposed its lies. Now its actually global.

* Fixed Epsilon event rule namespacing and formatting.

* Cosmic Cult Music Updates

* Few more CosCult changes.

* Nuke and Alert Changes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update Content.Server/_DV/CosmicCult/EntitySystems/CosmicChantrySystem.cs

Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

* Update Content.Server/_DV/Audio/ServerGlobalSoundSystem.cs

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

---------

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
2026-06-23 12:40:17 +02:00
Delta-V bot e7fe4865de Automatic changelog update 2026-06-22 20:06:07 +02:00
A111Vojta 84b600a39f Harmony lanyards (#6035)
* Lanyards Part1

* Lanyards Part 2

* Lanyards Part 3

* Fix

* Lanyards - Adding Comments

* replace plaintext with LocIds

* expanded the lanyard slot blacklist
2026-06-22 20:05:47 +02:00
Delta-V bot 31201c44ba Automatic changelog update 2026-06-22 18:48:51 +02:00
Theo 4f9f93eb87 Slugs structural increase TWO! (#6068)
* number up

* comment formatting
2026-06-22 11:48:31 -05:00
Delta-V bot f8a4d823b9 Automatic changelog update 2026-06-22 13:37:10 +02:00
Proxy also works :3 c61d2c6a58 guidebook shields (#6070) 2026-06-22 06:36:50 -05:00
Delta-V bot e25181e1bc Automatic changelog update 2026-06-22 13:11:55 +02:00
7c052442a8 Traits UI Modifications, Visuals, and a new Condition (#6082)
* traits ui things

* fluent

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update Resources/Locale/en-US/_DV/traits/traits-ui.ftl

Co-authored-by: Tobias Berger <toby@tobot.dev>
Signed-off-by: wesker <53323309+weskified@users.noreply.github.com>

* i hate toby

* newline

* pattern matching

* made simpler

* 2sleepy4myowngood

* kl;jasglkknf.,/.sa/.mmmmmmmmm

---------

Signed-off-by: wesker <53323309+weskified@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tobias Berger <toby@tobot.dev>
2026-06-22 11:11:35 +00:00
Delta-V bot 19b0de35dd Automatic changelog update 2026-06-22 10:46:57 +02:00
wesker 1c66c1fe2e Fix 8 0'Clock Ghosts to be Mono (#6084)
* fix jukebox to be mono

* ACTUALLY fix this, holy shit audacity
2026-06-22 10:46:37 +02:00
Delta-V bot ef46675a2f Automatic changelog update 2026-06-22 06:23:31 +02:00
Halo3moth c0b6929c9a lower pitbull vent spawn rate (#6081) 2026-06-22 04:23:12 +00:00
Vanessa c7aa50cf85 Update C# Contribution Examples and Guidelines (#6039)
Updated C# guidelines.
2026-06-21 15:59:47 +02:00
Delta-V bot c69fb99600 Automatic changelog update 2026-06-21 15:48:33 +02:00
VelcroboyandVelcroboy 3ca37d9c2b Map Edits: The Hive (#6018)
* Map Edits: The Hive

* review changes

---------

Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2026-06-21 15:48:12 +02:00
168 changed files with 12561 additions and 3712 deletions
+9
View File
@@ -12,12 +12,21 @@ jobs:
- name: Setup submodule
run: |
git submodule update --init --recursive
pushd RobustToolbox/Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- name: Update Engine Submodules
run: |
cd RobustToolbox/
git submodule update --init --recursive
pushd Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
+9 -1
View File
@@ -24,7 +24,11 @@ jobs:
- name: Setup Submodule
run: |
git submodule update --init --recursive
pushd RobustToolbox/Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
@@ -32,6 +36,10 @@ jobs:
run: |
cd RobustToolbox/
git submodule update --init --recursive
pushd Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
+8
View File
@@ -24,6 +24,10 @@ jobs:
- name: Setup Submodule
run: |
git submodule update --init --recursive
pushd RobustToolbox/Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
@@ -32,6 +36,10 @@ jobs:
run: |
cd RobustToolbox/
git submodule update --init --recursive
pushd Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
+7
View File
@@ -20,6 +20,7 @@ jobs:
- uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
@@ -30,6 +31,12 @@ jobs:
cd RobustToolbox
git fetch --depth=1
- name: Get Engine Tag
run: |
cd RobustToolbox/Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
- name: Install dependencies
run: dotnet restore
+8
View File
@@ -39,6 +39,10 @@ jobs:
- name: Setup Submodule
run: |
git submodule update --init --recursive
pushd RobustToolbox/Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
@@ -47,6 +51,10 @@ jobs:
run: |
cd RobustToolbox/
git submodule update --init --recursive
pushd Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
+8
View File
@@ -17,12 +17,20 @@ jobs:
- name: Setup submodule
run: |
git submodule update --init --recursive
pushd RobustToolbox/Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- name: Update Engine Submodules
run: |
cd RobustToolbox/
git submodule update --init --recursive
pushd Lidgren.Network/Lidgren.Network
git fetch
git checkout 726dd552a4b104fb2b701848705f1250a073f060
popd
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
+78 -18
View File
@@ -73,28 +73,88 @@ If you are adding a lot of C# code, then take advantage of partial classes. Put
Otherwise, **add comments on or around any changed lines.**
A comment on a new imported namespace:
### Single-Line Changes
Format should look like this.
```cs
using Content.Server.Psionics.Glimmer; // DeltaV
/* Importing Namespaces - Include optional comment if its not obvious what its being used for. */
using Content.Server._DV.Psionics.Glimmer; // DeltaV
using Content.Shared.Damage.Systems; // DeltaV - Addition of HandHeldArmor
/* Changing an upstream line - Same line as the change */
if (!TryComp<EyeComponent>(ent, out var eye) || _disabled) // DeltaV - check if disabled
/* Adding - Either same line or above the line. */
EnsureComp<PotentialPsionicComponent>(entity); // Deltav - Psionics
/* "Deleting" - Don't actually delete, just comment out and say why. This only applies to upstream code. */
// args.StatusIcons.Add(_prototype.Index(component.Icon)); // DeltaV - commented out. status icon now added above
```
A pair of comments enclosing a block of added code:
> * Its pretty obvious in the example above that importing `Content.Server._DV.Psionics.Glimmer` means we'll be interacting with glimmer so putting `// DeltaV - Add Glimmer` is needlessly redundant.
> * It's not as obvious what the `Content.Shared.Damage.Systems` namespace is used for, since its so broad, so adding a comment what feature is using it helps.
> * Actual code changes should almost always include the comment after ``// DeltaV`.
### Multi-Line Changes
Depending on how much you are editing, putting a comment on EACH line may be excessive, so if you have a larger block of code you are changing, denote it like so:
```cs
private EntityUid Slice(...)
// BEGIN DeltaV - Remove innate radio and radios from pockets
for (var i = 1; i <= 4; i++) // Arachnids have 4 pockets
{
...
_transform.SetLocalRotation(sliceUid, 0);
// DeltaV - start of deep frier stuff
var slicedEv = new FoodSlicedEvent(user, uid, sliceUid);
RaiseLocalEvent(uid, ref slicedEv);
// DeltaV - end of deep frier stuff
...
if (_inventory.TryGetSlotEntity(target, $"pocket{i}", out var headset) && HasComp<HeadsetComponent>(headset))
_inventory.TryUnequip(target, $"pocket{i}", true, true);
}
RemComp<ActiveRadioComponent>(target); // If the zombie has an innate radio, get rid of it.
// END DeltaV
```
> * Denoting these with a BEGIN and END clearly shows they are block of code without having to read the entire comment. This makes it easier to tell when you're dealing with single-line comments versus a block with merging in conflicts.
> * Case and order of the first two words is less of a concern. `// DeltaV Begin` or `// Begin DeltaV` will work fine too.
> * Try to make your blocks as small as possible, but use your discretion.
> * If you deleting multiple lines, use line comments (``//``) if its a few lines but if its a larger block (like commenting out an entire function), it is preferable to use block comments (`/* */`).
#### Soft Exceptions to the Multi-Line "Rules"
Some multi-line changes can use a single-line comment in certain scenarios. But if you are UNSURE, just use `// BEGIN DeltaV` and `// END DeltaV` comments like the previous section does and it'll be fine.
I'll give some examples.
```cs
/* This change comments out 3 lines but only needs a single line comment because commenting out the if statement implies that its logic will be commented out too. */
// if (obj.WasModified<TraitPrototype>()) // DeltaV - Refreshed in TraitsTab
// {
// _profileEditor.RefreshTraits();
// }
/* Same principle here. This adds two lines but the if statement implies the next line so commenting both lines isn't really needed. */
if (_flight.IsFlying(entity.Owner)) // DeltaV - Harpy Flight
return true;
```
### New Methods or Component Variables
Sometimes, you'll need to implement a whole new method or component variable and instead of wrapping it in `// BEGIN DeltaV` and `// END DeltaV`, you can just denote that it's a DeltaV function in the summary block before the function. This denotes the WHOLE function as a DeltaV addition.
```cs
/* New Method Example */
/// <summary>
/// DeltaV - Handle revealing ninja if cloaked when attacked by a hitscan attack.
/// </summary>
private void OnNinjaAttacked(Entity<SpaceNinjaComponent> ent, ref DamageChangedEvent args)
{
...
}
/* New Component Variable Example */
/// <summary>
/// DeltaV - If disabled the action will not disable when no charges remain. Use if you want to handle no charges differently.
/// </summary>
[DataField]
public bool DisableWhenEmpty = true;
```
In short:
* Use `// BEGIN DeltaV` and `// END Delta` to denote a *block* of changes.
* Keep blocks as small as possible.
* Use `// DeltaV` on or before the line if its not a block of changes.
* Use exceptions when they make sense.
### Changing Upstream Localization Fluent .ftl files
**Move all changed locale strings to a new DeltaV file** - use a `.ftl` file in the `_DV` folder. Comment out the old strings in the upstream file, and explain that they were moved.
@@ -104,10 +164,10 @@ Example:
Commented out old string in `Resources\Locale\en-US\xenoarchaeology\artifact-analyzer.ftl`
```
# DeltaV - moved to _DV file
#analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state ->
# [true] {$info}
# *[false] Unlock nodes to gain info
#}[/color][/font]
# analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state ->
# [true] {$info}
# *[false] Unlock nodes to gain info
# }[/color][/font]
```
The new version of the string in `Resources\Locale\en-US\_DV\xenoarchaeology\artifact-analyzer.ftl`
+10
View File
@@ -7,6 +7,7 @@ using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Radio.Components; // DeltaV - Client-side Radio Colors
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -158,6 +159,15 @@ namespace Content.Client.Examine
// since there's probably one open already if it's coming in from the server.
var entity = GetEntity(ev.EntityUid);
// BEGIN DeltaV - Client-Side Radio Colors
// This *seems* a bit hacky, but we basically don't want to overwrite what's been pushed on the client-side,
// because the server will send us the server-side colors (default). I've yet to find a scenario where the
// server and client don't match, so until then, this is probably fine, if a little hacky.
if (HasComp<EncryptionKeyHolderComponent>(entity) || HasComp<EncryptionKeyComponent>(entity))
return;
// END DeltaV
OpenTooltip(player.Value, entity, ev.CenterAtCursor, ev.OpenAtOldTooltip, ev.KnowTarget);
UpdateTooltipInfo(player.Value, entity, ev.Message, ev.Verbs, getVerbs: false);
}
@@ -1,13 +1,16 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:crewMonitoring="clr-namespace:Content.Client._DV.CrewMonitoring"
Title="{Loc crew-monitoring-ui-title}"
Resizable="False"
SetSize="1210 700"
MinSize="1210 700">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20"/>
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20" Visible="False" />
<crewMonitoring:CrewMonitoringShuttleControl Name="ShuttleMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20" Visible="False" />
<BoxContainer Orientation="Vertical" Margin="0 0 10 0">
<controls:StripeBack>
<PanelContainer>
@@ -5,6 +5,7 @@ using Content.Client.Pinpointer.UI;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Medical.SuitSensor;
using Content.Shared.Station; // DeltaV - crew monitoring radar
using Content.Shared.StatusIcon;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
@@ -27,6 +28,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly SharedTransformSystem _transformSystem;
private readonly SpriteSystem _spriteSystem;
private readonly SharedStationSystem _station; // DeltaV - crew monitoring radar
private NetEntity? _trackedEntity;
private bool _tryToScrollToListFocus;
@@ -39,8 +41,10 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
_transformSystem = _entManager.System<SharedTransformSystem>();
_spriteSystem = _entManager.System<SpriteSystem>();
_station = _entManager.System<SharedStationSystem>(); // DeltaV - crew monitoring radar
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
ShuttleMap.NavMap = NavMap; // DeltaV - crew monitoring radar
}
public void Set(string stationName, EntityUid? mapUid)
@@ -50,8 +54,10 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
if (_entManager.TryGetComponent<TransformComponent>(mapUid, out var xform))
NavMap.MapUid = xform.GridUid;
else
NavMap.Visible = false;
// Begin DeltaV - handle navmap visibility based on current position
// else
// NavMap.Visible = false;
// End DeltaV - handle navmap visibility based on current position
StationName.AddStyleClass("LabelBig");
StationName.Text = stationName;
@@ -62,6 +68,19 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
{
base.FrameUpdate(args);
// Begin DeltaV - handle navmap visibility based on current position
if (_station.GetOwningStation(ShuttleMap.Owner) is not null)
{
NavMap.Visible = true;
ShuttleMap.Visible = false;
}
else
{
NavMap.Visible = false;
ShuttleMap.Visible = true;
}
// End DeltaV - handle navmap visibility based on current position
if (_tryToScrollToListFocus)
TryToScrollToFocus();
}
@@ -69,6 +88,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
public void ShowSensors(List<SuitSensorStatus> sensors, EntityUid monitor, EntityCoordinates? monitorCoords)
{
ClearOutDatedData();
ShuttleMap.Owner = monitor; // DeltaV - crew monitoring radar
// No server label
if (sensors.Count == 0)
@@ -304,7 +324,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
jobContainer.AddChild(jobLabel);
// Add user coordinates to the navmap
if (coordinates != null && NavMap.Visible && _blipTexture != null)
if (coordinates != null && _blipTexture != null) // DeltaV - don't filter the navmap visibility
{
NavMap.TrackedEntities.TryAdd(sensor.SuitSensorUid,
new NavMapBlip
@@ -10,6 +10,7 @@
<tabs:AudioTab Name="AudioTab" />
<tabs:AccessibilityTab Name="AccessibilityTab" />
<tabs:AdminOptionsTab Name="AdminOptionsTab" />
<dtabs:RadioTab Name="RadioTab" /> <!-- DeltaV - Client-side Radio Options -->
<dtabs:DeltaTab Name="DeltaTab" /> <!-- DeltaV -->
</TabContainer>
</DefaultWindow>
@@ -21,7 +21,8 @@ namespace Content.Client.Options.UI
Tabs.SetTabTitle(3, Loc.GetString("ui-options-tab-audio"));
Tabs.SetTabTitle(4, Loc.GetString("ui-options-tab-accessibility"));
Tabs.SetTabTitle(5, Loc.GetString("ui-options-tab-admin"));
Tabs.SetTabTitle(6, Loc.GetString("ui-options-tab-deltav")); // DeltaV specific settings
Tabs.SetTabTitle(6, Loc.GetString("ui-options-tab-radio")); // DeltaV - Radio Channel Colors
Tabs.SetTabTitle(7, Loc.GetString("ui-options-tab-deltav")); // DeltaV specific settings
UpdateTabs();
}
@@ -36,6 +37,7 @@ namespace Content.Client.Options.UI
AccessibilityTab.Control.ReloadValues();
AudioTab.Control.ReloadValues();
AdminOptionsTab.Control.ReloadValues();
RadioTab.Control.ReloadValues(); // DeltaV - Radio Channel Colors
DeltaTab.Control.ReloadValues(); // DeltaV
}
}
@@ -56,8 +56,8 @@ public partial class NavMapControl : MapGridControl
// Constants
protected float UpdateTime = 1.0f;
protected float MaxSelectableDistance = 10f;
protected float MinDragDistance = 5f;
public float MaxSelectableDistance = 10f; // DeltaV - accessible to CrewMonitoringShuttleControl
public float MinDragDistance = 5f; // DeltaV - accessible to CrewMonitoringShuttleControl
protected static float MinDisplayedRange = 8f;
protected static float MaxDisplayedRange = 128f;
protected static float DefaultDisplayedRange = 48f;
@@ -193,6 +193,13 @@ public partial class NavMapControl : MapGridControl
_recenter.Disabled = false;
}
// Begin DeltaV - we need access to that from outside
public void TrackEntity(NetEntity? entity)
{
TrackedEntitySelectedAction?.Invoke(entity);
}
// End DeltaV - we need access to that from outside
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
{
base.KeyBindUp(args);
-111
View File
@@ -159,35 +159,6 @@ namespace Content.Client.Stylesheets
public static readonly Color ChatBackgroundColor = Color.FromHex("#25252ADD");
// DeltaV - AAC button variables
public static readonly string CommandButtonClass = "CommandButton";
public static readonly string EngineeringButtonClass = "EngineeringButton";
public static readonly string EpistemicsButtonClass = "EpistemicsButton";
public static readonly string JusticeButtonClass = "JusticeButton";
public static readonly string LogisticsButtonClass = "LogisticsButton";
public static readonly string MedicalButtonClass = "MedicalButton";
public static readonly string SecurityButtonClass = "SecurityButton";
public static readonly string ServiceButtonClass = "ServiceButton";
// DeltaV - AAC button colors
public static readonly Color CommandButtonColorDefault = Color.FromHex("#404A58");
public static readonly Color CommandColorHovered = Color.FromHex("#4F587B");
public static readonly Color EngineeringButtonColorDefault = Color.FromHex("#77684B");
public static readonly Color EngineeringColorHovered = Color.FromHex("#776D71");
public static readonly Color EpistemicsButtonColorDefault = Color.FromHex("#6F5973");
public static readonly Color EpistemicsColorHovered = Color.FromHex("#71638E");
public static readonly Color LogisticsButtonColorDefault = Color.FromHex("#61503A");
public static readonly Color LogisticsColorHovered = Color.FromHex("#675C64");
public static readonly Color JusticeButtonColorDefault = Color.FromHex("#4F3D4C");
public static readonly Color JusticeColorHovered = Color.FromHex("#5C4B5A");
public static readonly Color MedicalButtonColorDefault = Color.FromHex("#49687D");
public static readonly Color MedicalColorHovered = Color.FromHex("#556E95");
public static readonly Color SecurityButtonColorDefault = Color.FromHex("#724449");
public static readonly Color SecurityColorHovered = Color.FromHex("#745370");
public static readonly Color ServiceButtonColorDefault = Color.FromHex("#607952");
public static readonly Color ServiceColorHovered = Color.FromHex("#667A76");
// End DeltaV
public const string StyleClassCrewManifestGender = "CrewManifestGender"; //Delta-V - Manifest pronouns
// i'm not sure what the missing symbols were referencing, and this is getting obseleted anyway so:
@@ -1677,88 +1648,6 @@ namespace Content.Client.Stylesheets
BackgroundColor = FancyTreeSelectedRowColor,
}),
// DeltaV - AAC button styles
Element<ContainerButton>()
.Class(CommandButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, CommandButtonColorDefault),
Element<ContainerButton>()
.Class(CommandButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, CommandColorHovered),
Element<ContainerButton>()
.Class(EngineeringButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, EngineeringButtonColorDefault),
Element<ContainerButton>()
.Class(EngineeringButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, EngineeringColorHovered),
Element<ContainerButton>()
.Class(EpistemicsButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, EpistemicsButtonColorDefault),
Element<ContainerButton>()
.Class(EpistemicsButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, EpistemicsColorHovered),
Element<ContainerButton>()
.Class(LogisticsButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, LogisticsButtonColorDefault),
Element<ContainerButton>()
.Class(LogisticsButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, LogisticsColorHovered),
Element<ContainerButton>()
.Class(MedicalButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, MedicalButtonColorDefault),
Element<ContainerButton>()
.Class(MedicalButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, MedicalColorHovered),
Element<ContainerButton>()
.Class(SecurityButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, SecurityButtonColorDefault),
Element<ContainerButton>()
.Class(SecurityButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, SecurityColorHovered),
Element<ContainerButton>()
.Class(ServiceButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, ServiceButtonColorDefault),
Element<ContainerButton>()
.Class(ServiceButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, ServiceColorHovered),
Element<ContainerButton>()
.Class(JusticeButtonClass)
.Pseudo(ContainerButton.StylePseudoClassNormal)
.Prop(Control.StylePropertyModulateSelf, JusticeButtonColorDefault),
Element<ContainerButton>()
.Class(JusticeButtonClass)
.Pseudo(ContainerButton.StylePseudoClassHover)
.Prop(Control.StylePropertyModulateSelf, JusticeColorHovered),
// End DeltaV
// Silicon law edit ui
Element<Label>().Class(SiliconLawContainer.StyleClassSiliconLawPositionLabel)
.Prop(Label.StylePropertyFontColor, NanoGold),
@@ -1,6 +1,7 @@
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Text.RegularExpressions; // DeltaV - Client-Side Radio Channel Colors
using Content.Client.Administration.Managers;
using Content.Client.Chat;
using Content.Client.Chat.Managers;
@@ -839,8 +840,38 @@ public sealed partial class ChatUIController : UIController
}
}
/// <summary>
/// DeltaV - Regex expression to match on the chat message text in order to replace it with another color.
/// </summary>
private static readonly Regex RadioChatColorRegex = new(@"color=#?([a-fA-F0-9]{8})", RegexOptions.Compiled);
/// <summary>
/// DeltaV - Replaces the channel color hex code in a message with the client-side hex color
/// overridden by <see cref="Content.Client._DV.Radio.RadioChannelColorSystem"/> .
/// </summary>
/// <param name="message">The message to find and replace the color hex code in.</param>
/// <param name="channelProtoId">The proto ID of the channel. Basically the replacement color.</param>
/// <returns>The message with the color hex code replaced, if it exists.</returns>
private string ReplaceRadioChannelColorFromServer(string message, string channelProtoId)
{
if (_prototypeManager.Resolve<RadioChannelPrototype>(channelProtoId, out var channel))
{
return RadioChatColorRegex.Replace(message, $"color={channel.Color.ToHex()}");
}
return message;
}
public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true)
{
// BEGIN DeltaV
// Needs to happen before ANY other color or pattern matching so that we don't screw up the other colors in the message.
if (msg.Channel == ChatChannel.Radio && msg.RadioChannelProtoId is { } channel)
{
msg.WrappedMessage = ReplaceRadioChannelColorFromServer(msg.WrappedMessage, channel);
}
// END DeltaV
// color the name unless it's something like "the old man"
if ((msg.Channel == ChatChannel.Local || msg.Channel == ChatChannel.Whisper) && _chatNameColorsEnabled)
{
@@ -12,6 +12,19 @@ namespace Content.Client._DV.AACTablet.UI;
[CommonSheetlet]
public sealed class AACSheetlet<T> : Sheetlet<T> where T : PalettedStylesheet, IButtonConfig
{
private static readonly (string StyleClass, Color Normal, Color Hover)[] SubjectButtonColors =
[
// Department colors
("CommandButton", Color.FromHex("#404A58"), Color.FromHex("#4F587B")),
("EngineeringButton", Color.FromHex("#77684B"), Color.FromHex("#776D71")),
("EpistemicsButton", Color.FromHex("#6F5973"), Color.FromHex("#71638E")),
("JusticeButton", Color.FromHex("#4F3D4C"), Color.FromHex("#5C4B5A")),
("LogisticsButton", Color.FromHex("#61503A"), Color.FromHex("#675C64")),
("MedicalButton", Color.FromHex("#49687D"), Color.FromHex("#556E95")),
("SecurityButton", Color.FromHex("#724449"), Color.FromHex("#745370")),
("ServiceButton", Color.FromHex("#607952"), Color.FromHex("#667A76")),
];
public override StyleRule[] GetRules(T sheet, object config)
{
// Create style boxes
@@ -81,6 +94,21 @@ public sealed class AACSheetlet<T> : Sheetlet<T> where T : PalettedStylesheet, I
.Prop(Control.StylePropertyModulateSelf, Color.DarkRed),
]);
SubjectColorRules(rules);
return rules.ToArray();
}
// AAC - Makes sure colors are applied to subject buttons
private static void SubjectColorRules(List<StyleRule> rules)
{
foreach (var (styleClass, normal, hover) in SubjectButtonColors)
{
rules.AddRange([
E<ContainerButton>().Class(ContainerButton.StyleClassButton).Class(styleClass)
.PseudoNormal().Prop(Control.StylePropertyModulateSelf, normal),
E<ContainerButton>().Class(ContainerButton.StyleClassButton).Class(styleClass)
.PseudoHovered().Prop(Control.StylePropertyModulateSelf, hover),
]);
}
}
}
@@ -218,7 +218,7 @@ public sealed partial class AACWindow : FancyWindow
MaxSize = new Vector2(ButtonWidth, ButtonWidth),
ClipText = false,
HorizontalExpand = true,
StyleClasses = { styleClass }
StyleClasses = { ContainerButton.StyleClassButton, styleClass }
};
var buttonLabel = new RichTextLabel
@@ -0,0 +1,180 @@
using System.Numerics;
using Content.Client.Pinpointer.UI;
using Content.Client.Shuttles.UI;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
namespace Content.Client._DV.CrewMonitoring;
public sealed class CrewMonitoringShuttleControl : BaseShuttleControl
{
[Dependency] private readonly IMapManager _mapManager = default!;
private readonly SharedShuttleSystem _shuttles;
private readonly SharedTransformSystem _transform;
public EntityUid? Owner;
public NavMapControl? NavMap;
private List<Entity<MapGridComponent>> _grids = new();
public CrewMonitoringShuttleControl() : base(64f, 256f, 256f)
{
_shuttles = EntManager.System<SharedShuttleSystem>();
_transform = EntManager.System<SharedTransformSystem>();
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
DrawBacking(handle);
DrawCircles(handle);
if (Owner is not { } owner || NavMap is not { } navMap)
return;
var xformQuery = EntManager.GetEntityQuery<TransformComponent>();
var fixturesQuery = EntManager.GetEntityQuery<FixturesComponent>();
var bodyQuery = EntManager.GetEntityQuery<PhysicsComponent>();
var iffQuery = EntManager.GetEntityQuery<IFFComponent>();
if (!xformQuery.TryGetComponent(owner, out var xform)
|| xform.MapID == MapId.Nullspace)
{
return;
}
var coordinates = new EntityCoordinates(owner, Vector2.Zero);
var mapPos = _transform.ToMapCoordinates(coordinates);
var ourEntRot = Angle.Zero;
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
Matrix3x2.Invert(ourEntMatrix, out var worldToShuttle);
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
var worldToView = worldToShuttle * shuttleToView;
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), ourEntRot, mapPos.Position);
var viewAABB = viewBounds.CalcBoundingBox();
_grids.Clear();
_mapManager.FindGridsIntersecting(xform.MapID, new Box2(mapPos.Position - MaxRadarRangeVector, mapPos.Position + MaxRadarRangeVector), ref _grids, approx: true, includeMap: false);
foreach (var grid in _grids)
{
if (!fixturesQuery.HasComponent(grid))
continue;
var body = bodyQuery.GetComponent(grid);
iffQuery.TryComp(grid, out var iff);
if (!_shuttles.CanDraw(grid, body, iff))
continue;
var curGridToWorld = _transform.GetWorldMatrix(grid);
var curGridToView = curGridToWorld * worldToShuttle * shuttleToView;
var gridAABB = curGridToWorld.TransformBox(grid.Comp.LocalAABB);
if (!gridAABB.Intersects(viewAABB))
continue;
var labelColor = _shuttles.GetIFFColor(grid, self: false, iff);
DrawGrid(handle, curGridToView, grid, labelColor);
}
var curTime = Timing.RealTime;
var blinkFrequency = 1f / 1f;
var lit = curTime.TotalSeconds % blinkFrequency > blinkFrequency / 2f;
var box = SizeBox;
foreach (var (coord, value) in navMap.TrackedCoordinates)
{
if (!lit || !value.Visible)
continue;
var blipPos = _transform.ToMapCoordinates(coord);
if (mapPos.MapId == MapId.Nullspace)
continue;
handle.DrawCircle(Vector2.Clamp(Vector2.Transform(blipPos.Position, worldToView), box.TopLeft, box.BottomRight), float.Sqrt(MinimapScale) * 2f, value.Color);
}
foreach (var blip in navMap.TrackedEntities.Values)
{
if (blip.Blinks && !lit)
continue;
var blipPos = _transform.ToMapCoordinates(blip.Coordinates);
if (mapPos.MapId == MapId.Nullspace)
continue;
handle.DrawCircle(Vector2.Clamp(Vector2.Transform(blipPos.Position, worldToView), box.TopLeft, box.BottomRight), float.Sqrt(MinimapScale) * 2f, blip.Color);
}
}
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
{
base.KeyBindUp(args);
if (Owner is not { } owner || NavMap is null)
return;
if (!EntManager.TryGetComponent<TransformComponent>(owner, out var xform) || xform.MapID == MapId.Nullspace)
{
return;
}
var ourEntRot = Angle.Zero;
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
Matrix3x2.Invert(ourEntMatrix, out var worldToShuttle);
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
var worldToView = worldToShuttle * shuttleToView;
Matrix3x2.Invert(worldToView, out var viewToWorld);
if (args.Function == EngineKeyFunctions.UIClick)
{
if (NavMap.TrackedEntities.Count == 0)
return;
if ((StartDragPosition - args.PointerLocation.Position).Length() > NavMap.MinDragDistance)
return;
var localPosition = args.PointerLocation.Position - GlobalPixelPosition;
var worldPosition = Vector2.Transform(localPosition, viewToWorld);
var closestEntity = NetEntity.Invalid;
var closestDistance = float.PositiveInfinity;
foreach (var (currentEntity, blip) in NavMap.TrackedEntities)
{
if (!blip.Selectable)
continue;
var currentDistance = (_transform.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length();
if (closestDistance < currentDistance || currentDistance * MinimapScale > NavMap.MaxSelectableDistance)
continue;
closestEntity = currentEntity;
closestDistance = currentDistance;
}
if (closestDistance > NavMap.MaxSelectableDistance || !closestEntity.IsValid())
return;
NavMap.TrackEntity(closestEntity);
}
else if (args.Function == EngineKeyFunctions.UIRightClick)
{
NavMap.TrackEntity(null);
}
}
}
@@ -0,0 +1,87 @@
<tabs:RadioTab xmlns="https://spacestation14.io"
xmlns:tabs="clr-namespace:Content.Client._DV.Options.UI.Tabs"
xmlns:ui="clr-namespace:Content.Client.Options.UI">
<BoxContainer Orientation="Vertical">
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Orientation="Vertical" Margin="8">
<!-- Common -->
<Label Text="{Loc 'ui-options-radio-header-common'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioCommonColorSlider"
Example="{Loc 'ui-options-radio-example-common'}"/>
<Button Name="ResetCommonColorButton" Text="{Loc 'ui-options-radio-reset-common'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Command -->
<Label Text="{Loc 'ui-options-radio-header-command'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioCommandColorSlider"
Example="{Loc 'ui-options-radio-example-command'}"/>
<Button Name="ResetCommandColorButton" Text="{Loc 'ui-options-radio-reset-command'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Security -->
<Label Text="{Loc 'ui-options-radio-header-security'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioSecurityColorSlider"
Example="{Loc 'ui-options-radio-example-security'}"/>
<Button Name="ResetSecurityColorButton" Text="{Loc 'ui-options-radio-reset-security'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Medical -->
<Label Text="{Loc 'ui-options-radio-header-medical'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioMedicalColorSlider"
Example="{Loc 'ui-options-radio-example-medical'}"/>
<Button Name="ResetMedicalColorButton" Text="{Loc 'ui-options-radio-reset-medical'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Engineering -->
<Label Text="{Loc 'ui-options-radio-header-engineering'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioEngineeringColorSlider"
Example="{Loc 'ui-options-radio-example-engineering'}"/>
<Button Name="ResetEngineeringColorButton" Text="{Loc 'ui-options-radio-reset-engineering'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Science -->
<Label Text="{Loc 'ui-options-radio-header-science'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioScienceColorSlider"
Example="{Loc 'ui-options-radio-example-science'}"/>
<Button Name="ResetScienceColorButton" Text="{Loc 'ui-options-radio-reset-science'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Cargo -->
<Label Text="{Loc 'ui-options-radio-header-cargo'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioCargoColorSlider"
Example="{Loc 'ui-options-radio-example-cargo'}"/>
<Button Name="ResetCargoColorButton" Text="{Loc 'ui-options-radio-reset-cargo'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Service -->
<Label Text="{Loc 'ui-options-radio-header-service'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioServiceColorSlider"
Example="{Loc 'ui-options-radio-example-service'}"/>
<Button Name="ResetServiceColorButton" Text="{Loc 'ui-options-radio-reset-service'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- Binary -->
<Label Text="{Loc 'ui-options-radio-header-binary'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioBinaryColorSlider"
Example="{Loc 'ui-options-radio-example-binary'}"/>
<Button Name="ResetBinaryColorButton" Text="{Loc 'ui-options-radio-reset-binary'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- DeltaV - Justice -->
<Label Text="{Loc 'ui-options-radio-header-justice'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioJusticeColorSlider"
Example="{Loc 'ui-options-radio-example-justice'}"/>
<Button Name="ResetJusticeColorButton" Text="{Loc 'ui-options-radio-reset-justice'}" MaxWidth="300" Margin="0 4 0 0" />
<!-- DeltaV - Prison -->
<Label Text="{Loc 'ui-options-radio-header-prison'}"
StyleClasses="LabelKeyText"/>
<ui:OptionColorSlider Name="RadioPrisonColorSlider"
Example="{Loc 'ui-options-radio-example-prison'}"/>
<Button Name="ResetPrisonColorButton" Text="{Loc 'ui-options-radio-reset-prison'}" MaxWidth="300" Margin="0 4 0 0" />
</BoxContainer>
</ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" />
</BoxContainer>
</tabs:RadioTab>
@@ -0,0 +1,55 @@
using Content.Client.Options.UI;
using Content.Shared._DV.CCVars;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Client._DV.Options.UI.Tabs;
[GenerateTypedNameReferences]
public sealed partial class RadioTab : Control
{
[Dependency] private readonly IConfigurationManager _config = default!; // DeltaV
public RadioTab()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
// Having so many color sliders on one page is a lot. We should only really try
// to have common/departmental channels here, or it just becomes WAAAAY too much.
Control.AddOptionColorSlider(DCCVars.RadioColorCommon, RadioCommonColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorCommand, RadioCommandColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorSecurity, RadioSecurityColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorMedical, RadioMedicalColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorEngineering, RadioEngineeringColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorScience, RadioScienceColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorCargo, RadioCargoColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorService, RadioServiceColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorBinary, RadioBinaryColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorJustice, RadioJusticeColorSlider);
Control.AddOptionColorSlider(DCCVars.RadioColorPrison, RadioPrisonColorSlider);
ResetCommonColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorCommon);
ResetCommandColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorCommand);
ResetSecurityColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorSecurity);
ResetMedicalColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorMedical);
ResetEngineeringColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorEngineering);
ResetScienceColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorScience);
ResetCargoColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorCargo);
ResetServiceColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorService);
ResetBinaryColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorBinary);
ResetJusticeColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorJustice);
ResetPrisonColorButton.OnPressed += _ => OnResetColorButton(DCCVars.RadioColorPrison);
Control.Initialize();
}
private void OnResetColorButton(CVarDef<string> cvar)
{
_config.SetCVar(cvar, cvar.DefaultValue);
Control.ReloadValues();
}
}
@@ -0,0 +1,49 @@
using Content.Shared._DV.CCVars;
using Content.Shared.Radio;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Client._DV.Radio;
public sealed partial class RadioChannelColorSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
// Feels like there should be a better way to do this but I'd rather not add a client-side cvar ID or reference
// to a prototype in Content.Shared, since I don't want people thinking they can ever use it on the server-side.
private static readonly ProtoId<RadioChannelPrototype> CommonChannelProto = "Common";
private static readonly ProtoId<RadioChannelPrototype> CommandChannelProto = "Command";
private static readonly ProtoId<RadioChannelPrototype> SecurityChannelProto = "Security";
private static readonly ProtoId<RadioChannelPrototype> MedicalChannelProto = "Medical";
private static readonly ProtoId<RadioChannelPrototype> EngineeringChannelProto = "Engineering";
private static readonly ProtoId<RadioChannelPrototype> ScienceChannelProto = "Science";
private static readonly ProtoId<RadioChannelPrototype> CargoChannelProto = "Supply";
private static readonly ProtoId<RadioChannelPrototype> ServiceChannelProto = "Service";
private static readonly ProtoId<RadioChannelPrototype> BinaryChannelProto = "Binary";
private static readonly ProtoId<RadioChannelPrototype> JusticeChannelProto = "Justice";
private static readonly ProtoId<RadioChannelPrototype> PrisonChannelProto = "Prison";
public override void Initialize()
{
base.Initialize();
_config.OnValueChanged(DCCVars.RadioColorCommon, (value) => { SetChannelColor(CommonChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorCommand, (value) => { SetChannelColor(CommandChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorSecurity, (value) => { SetChannelColor(SecurityChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorMedical, (value) => { SetChannelColor(MedicalChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorEngineering, (value) => { SetChannelColor(EngineeringChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorScience, (value) => { SetChannelColor(ScienceChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorCargo, (value) => { SetChannelColor(CargoChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorService, (value) => { SetChannelColor(ServiceChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorBinary, (value) => { SetChannelColor(BinaryChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorJustice, (value) => { SetChannelColor(JusticeChannelProto, value); }, true);
_config.OnValueChanged(DCCVars.RadioColorPrison, (value) => { SetChannelColor(PrisonChannelProto, value); }, true);
}
private void SetChannelColor(ProtoId<RadioChannelPrototype> channel, string newColor)
{
if (Color.TryParse(newColor, out var color))
_proto.Index(channel).Color = color;
}
}
@@ -0,0 +1,17 @@
using Content.Shared.Labels.Components;
namespace Content.Client._DV.Silicons;
public static class NameHelpers
{
extension(IEntityManager self)
{
public string PaperName(EntityUid paper)
{
if (self.TryGetComponent<LabelComponent>(paper, out var label) && label.CurrentLabel is { } labelText)
return labelText;
return self.GetComponent<MetaDataComponent>(paper).EntityName;
}
}
}
@@ -0,0 +1,144 @@
using System.IO;
using Content.Shared._DV.Silicons;
using Content.Shared.Fax;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Robust.Shared.Containers;
namespace Content.Client._DV.Silicons;
[UsedImplicitly]
public sealed class StationAiFaxBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[Dependency] private readonly IFileDialogManager _fileDialogManager = default!;
private StationAiFaxWindow? _window;
private List<StationAiFaxPageWindow> _windows = new();
private bool _dialogIsOpen;
protected override void Open()
{
base.Open();
_window = this.CreateWindow<StationAiFaxWindow>();
_window.FileButtonPressed += OnFileButtonPressed;
_window.BlankButtonPressed += OnBlankButtonPressed;
_window.OpenSendWindowPressed += OnOpenSendWindowPressed;
_window.ExaminePressed += OnExaminePressed;
_window.ShredPressed += OnShredPressed;
_window.DuplicatePressed += OnDuplicatePressed;
UpdateFaxes(EntMan.System<SharedContainerSystem>().EnsureContainer<Container>(Owner, StationAiFaxComponent.Container, out _));
}
private async void OnFileButtonPressed()
{
if (_dialogIsOpen)
return;
_dialogIsOpen = true;
var filters = new FileDialogFilters(new FileDialogFilters.Group("txt"));
await using var file = await _fileDialogManager.OpenFile(filters, FileAccess.Read);
_dialogIsOpen = false;
if (_window == null || _window.Disposed || file == null)
{
return;
}
using var reader = new StreamReader(file);
var firstLine = await reader.ReadLineAsync();
string? label = null;
var content = await reader.ReadToEndAsync();
if (firstLine is { })
{
if (firstLine.StartsWith('#'))
{
label = firstLine[1..].Trim();
}
else
{
content = firstLine + "\n" + content;
}
}
SendMessage(new FaxFileMessage(
label?[..Math.Min(label.Length, FaxFileMessageValidation.MaxLabelSize)],
content[..Math.Min(content.Length, FaxFileMessageValidation.MaxContentSize)],
false));
}
private void OnBlankButtonPressed()
{
SendMessage(new FaxFileMessage(null, string.Empty, false));
}
private void OnOpenSendWindowPressed(EntityUid entity)
{
var window = new StationAiFaxPageWindow(entity, EntMan);
_windows.Add(window);
window.RefreshButtonPressed += OnRefreshButtonPressed;
window.PeerSelected += OnPeerSelected;
window.SendButtonPressed += OnSendDocument;
if (UiSystem.TryGetUiState<FaxUiState>(Owner, UiKey, out var state))
{
window.UpdateState(state);
}
window.OpenCentered();
}
private void OnSendDocument(EntityUid entity)
{
SendMessage(new StationAiFaxSendMessage(EntMan.GetNetEntity(entity)));
}
private void OnExaminePressed(EntityUid entity)
{
SendPredictedMessage(new StationAiFaxExamineMessage(EntMan.GetNetEntity(entity)));
}
private void OnShredPressed(EntityUid entity)
{
SendPredictedMessage(new StationAiFaxShredMessage(EntMan.GetNetEntity(entity)));
}
private void OnDuplicatePressed(EntityUid entity)
{
SendPredictedMessage(new StationAiFaxDuplicateMessage(EntMan.GetNetEntity(entity)));
}
private void OnRefreshButtonPressed()
{
SendMessage(new FaxRefreshMessage());
}
private void OnPeerSelected(string address)
{
SendMessage(new FaxDestinationMessage(address));
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not FaxUiState cast)
return;
_window?.UpdateState(cast);
foreach (var window in _windows)
{
window.UpdateState(cast);
}
}
public void UpdateFaxes(Container container)
{
_window?.UpdateFaxes(container);
}
}
@@ -0,0 +1,14 @@
<PanelContainer xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client._DV.UserInterfaces"
xmlns:xe="clr-namespace:Content.Client.UserInterface.XamlExtensions"
StyleClasses="PanelLight"
HorizontalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8">
<Label Name="PaperName" HorizontalExpand="True" />
<ui:IconButton Name="Shred" StyleClasses="OpenRight" Icon="{xe:Tex '/Textures/Interface/shredder.svg.192dpi.png'}" />
<ui:IconButton Name="Duplicate" StyleClasses="OpenBoth" Icon="{xe:Tex '/Textures/Interface/files.svg.192dpi.png'}" />
<ui:IconButton Name="Examine" StyleClasses="OpenBoth" Icon="{xe:Tex '/Textures/Interface/eye.svg.192dpi.png'}" />
<ui:IconButton Name="Send" StyleClasses="OpenLeft" Icon="{xe:Tex '/Textures/Interface/send.svg.192dpi.png'}" />
</BoxContainer>
</PanelContainer>
@@ -0,0 +1,27 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._DV.Silicons;
[GenerateTypedNameReferences]
public sealed partial class StationAiFaxItem : PanelContainer
{
public string? Text { get => PaperName.Text; set => PaperName.Text = value; }
public bool CanSend { get => !Duplicate.Disabled; set => Duplicate.Disabled = !value; }
public event Action? ShredPressed;
public event Action? DuplicatePressed;
public event Action? ExaminePressed;
public event Action? SendPressed;
public StationAiFaxItem()
{
RobustXamlLoader.Load(this);
Shred.OnPressed += _ => ShredPressed?.Invoke();
Duplicate.OnPressed += _ => DuplicatePressed?.Invoke();
Examine.OnPressed += _ => ExaminePressed?.Invoke();
Send.OnPressed += _ => SendPressed?.Invoke();
}
}
@@ -0,0 +1,64 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'fax-machine-ui-page-window'}"
MinSize="380 220">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<PanelContainer StyleClasses="PanelDark" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Orientation="Vertical" Margin="8">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'fax-machine-ui-sending'}"
VerticalAlignment="Bottom"
HorizontalExpand="True" />
<Label Name="SendingLabel"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Align="Right"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'fax-machine-ui-from'}"
VerticalAlignment="Bottom"
HorizontalExpand="True" />
<Label Name="FromLabel"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Align="Right"/>
</BoxContainer>
<BoxContainer
Orientation="Horizontal">
<Label Text="{Loc 'fax-machine-ui-to'}"
VerticalAlignment="Bottom"
HorizontalExpand="True"/>
<BoxContainer Orientation="Horizontal">
<OptionButton Name="PeerSelector"
StyleClasses="OpenRight" />
<Button StyleClasses="OpenLeft"
Name="RefreshButton"
ToolTip="{Loc 'fax-machine-ui-refresh-button'}">
<TextureButton
MouseFilter="Ignore"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Scale="0.9 0.9"
StyleClasses="RefreshButton" />
</Button>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</PanelContainer>
<BoxContainer Orientation="Horizontal" SeparationOverride="8" Margin="8">
<Button Name="SendButton"
Text="{Loc 'fax-machine-ui-send-button'}"
HorizontalExpand="True"
Disabled="True" />
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>
@@ -0,0 +1,70 @@
using System.Linq;
using Content.Client.UserInterface.Controls;
using Content.Shared.Fax;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._DV.Silicons;
[GenerateTypedNameReferences]
public sealed partial class StationAiFaxPageWindow : FancyWindow
{
public event Action? RefreshButtonPressed;
public event Action<string>? PeerSelected;
public event Action<EntityUid>? SendButtonPressed;
public StationAiFaxPageWindow(EntityUid uid, IEntityManager entityManager)
{
RobustXamlLoader.Load(this);
SendingLabel.Text = entityManager.PaperName(uid);
RefreshButton.OnPressed += _ => RefreshButtonPressed?.Invoke();
SendButton.OnPressed += _ => SendButtonPressed?.Invoke(uid);
PeerSelector.OnItemSelected += args =>
PeerSelected?.Invoke((string) args.Button.GetItemMetadata(args.Id)!);
}
public void UpdateState(FaxUiState state)
{
SendButton.Disabled = !state.CanSendCooldown;
FromLabel.Text = state.DeviceName;
if (state.AvailablePeers.Count == 0)
{
PeerSelector.AddItem(Loc.GetString("fax-machine-ui-no-peers"));
PeerSelector.Disabled = true;
}
if (PeerSelector.Disabled && state.AvailablePeers.Count != 0)
{
PeerSelector.Clear();
PeerSelector.Disabled = false;
}
// always must be selected destination
if (string.IsNullOrEmpty(state.DestinationAddress) && state.AvailablePeers.Count != 0)
{
PeerSelected?.Invoke(state.AvailablePeers.First().Key);
return;
}
if (state.AvailablePeers.Count != 0)
{
PeerSelector.Clear();
foreach (var (address, name) in state.AvailablePeers)
{
var id = AddPeerSelect(name, address);
if (address == state.DestinationAddress)
PeerSelector.Select(id);
}
}
}
private int AddPeerSelect(string name, string address)
{
PeerSelector.AddItem(name);
PeerSelector.SetItemMetadata(PeerSelector.ItemCount - 1, address);
return PeerSelector.ItemCount - 1;
}
}
@@ -0,0 +1,37 @@
using Content.Shared._DV.Silicons;
using Content.Shared.Fax;
using Robust.Client.GameObjects;
using Robust.Shared.Containers;
namespace Content.Client._DV.Silicons;
public sealed class StationAiFaxSystem : SharedStationAiFaxSystem
{
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StationAiFaxComponent, EntRemovedFromContainerMessage>(OnRemoved);
}
protected override void OnInserted(Entity<StationAiFaxComponent> ent, ref EntInsertedIntoContainerMessage args)
{
base.OnInserted(ent, ref args);
if (!_userInterface.TryGetOpenUi<StationAiFaxBoundUserInterface>(ent.Owner, FaxUiKey.Key, out var bui))
return;
bui.UpdateFaxes(_container.EnsureContainer<Container>(ent, StationAiFaxComponent.Container, out _));
}
private void OnRemoved(Entity<StationAiFaxComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (!_userInterface.TryGetOpenUi<StationAiFaxBoundUserInterface>(ent.Owner, FaxUiKey.Key, out var bui))
return;
bui.UpdateFaxes(_container.EnsureContainer<Container>(ent, StationAiFaxComponent.Container, out _));
}
}
@@ -0,0 +1,29 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'fax-machine-ui-window'}"
MinSize="500 600">
<BoxContainer Orientation="Vertical" Name="NoFaxes" HorizontalAlignment="Center" VerticalAlignment="Center" SeparationOverride="8">
<Label StyleClasses="PlaceholderText" Text="{Loc 'fax-machine-ui-no-faxes'}" Align="Center" />
<BoxContainer Orientation="Vertical" HorizontalAlignment="Center">
<Button Name="NoFaxesBlank" Text="{Loc 'fax-machine-ui-add-blank-file'}" HorizontalExpand="True" StyleClasses="OpenLeft" />
<Button Name="NoFaxesUpload" Text="{Loc 'fax-machine-ui-upload-file'}" HorizontalExpand="True" StyleClasses="OpenRight" />
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Vertical" Name="YesFaxes" HorizontalExpand="True" VerticalExpand="True">
<PanelContainer StyleClasses="PanelDark" VerticalExpand="True" HorizontalExpand="True">
<ScrollContainer VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Name="FaxList" Margin="8" VerticalAlignment="Top" HorizontalExpand="True" SeparationOverride="8" Orientation="Vertical" />
</ScrollContainer>
</PanelContainer>
<BoxContainer Orientation="Horizontal" SeparationOverride="8" Margin="8">
<Button Name="YesFaxesBlank" Text="{Loc 'fax-machine-ui-add-blank-file'}" HorizontalAlignment="Center" />
<Button Name="YesFaxesUpload" Text="{Loc 'fax-machine-ui-upload-file'}" HorizontalAlignment="Center" />
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>
@@ -0,0 +1,92 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Fax;
using Content.Shared.Labels.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._DV.Silicons;
[GenerateTypedNameReferences]
public sealed partial class StationAiFaxWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entityManager = default!;
public event Action? BlankButtonPressed;
public event Action? FileButtonPressed;
public event Action<EntityUid>? OpenSendWindowPressed;
public event Action<EntityUid>? ExaminePressed;
public event Action<EntityUid>? ShredPressed;
public event Action<EntityUid>? DuplicatePressed;
public bool CanSend
{
get;
set
{
if (field == value)
return;
field = value;
foreach (var item in FaxList.Children)
{
var castItem = (StationAiFaxItem)item;
castItem.CanSend = value;
}
NoFaxesBlank.Disabled = !value;
NoFaxesUpload.Disabled = !value;
YesFaxesBlank.Disabled = !value;
YesFaxesUpload.Disabled = !value;
}
}
public StationAiFaxWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
NoFaxesBlank.OnPressed += _ => BlankButtonPressed?.Invoke();
NoFaxesUpload.OnPressed += _ => FileButtonPressed?.Invoke();
YesFaxesBlank.OnPressed += _ => BlankButtonPressed?.Invoke();
YesFaxesUpload.OnPressed += _ => FileButtonPressed?.Invoke();
}
public void UpdateFaxes(Robust.Shared.Containers.Container container)
{
FaxList.RemoveAllChildren();
foreach (var item in container.ContainedEntities)
{
var page = new StationAiFaxItem
{
Text = _entityManager.TryGetComponent<LabelComponent>(item, out var label) &&
label.CurrentLabel is { } labelText
? labelText
: _entityManager.GetComponent<MetaDataComponent>(item).EntityName,
CanSend = CanSend,
};
FaxList.AddChild(page);
page.ExaminePressed += () => ExaminePressed?.Invoke(item);
page.ShredPressed += () => ShredPressed?.Invoke(item);
page.DuplicatePressed += () => DuplicatePressed?.Invoke(item);
page.SendPressed += () => OpenSendWindowPressed?.Invoke(item);
}
if (container.ContainedEntities.Count == 0)
{
YesFaxes.Visible = false;
NoFaxes.Visible = true;
}
else
{
YesFaxes.Visible = true;
NoFaxes.Visible = false;
}
}
public void UpdateState(FaxUiState state)
{
CanSend = state.CanSendCooldown;
}
}
@@ -108,6 +108,70 @@ public sealed partial class TraitCategory : BoxContainer
{
CategoryPointsLabel.Visible = false;
}
// Lock unselected entries whenever the category cap is reached so the
// player gets a clear visual cue instead of silently failing to equip.
UpdateCategoryFullState();
}
/// <summary>
/// Propagates category-cap lock state to every entry.
/// <para>
/// Selected entries are never locked - they are the traits filling the cap
/// and must stay interactive so the player can deselect them.
/// </para>
/// <para>
/// Unselected entries are locked when:
/// <list type="bullet">
/// <item><see cref="TraitCategoryPrototype.MaxTraits"/> is defined and already reached, OR</item>
/// <item><see cref="TraitCategoryPrototype.MaxPoints"/> is defined and this specific
/// trait's cost would push spending over the limit.</item>
/// </list>
/// </para>
/// </summary>
private void UpdateCategoryFullState()
{
var traitCapReached = _category.MaxTraits.HasValue && SelectedCount >= _category.MaxTraits.Value;
foreach (var (_, entry) in _traitEntries)
{
if (entry.IsSelected)
{
// Selected traits are never locked by category state.
entry.SetLockedByCategory(false);
continue;
}
var locked = traitCapReached;
// Even when the trait count cap hasn't been hit, lock this specific entry
// if adding its cost would exceed the category's point budget.
if (!locked && _category.MaxPoints.HasValue)
locked = PointsSpent + entry.TraitCost > _category.MaxPoints.Value;
entry.SetLockedByCategory(locked);
}
}
/// <summary>
/// Called by <see cref="TraitsTab"/> whenever the player's global point budget changes.
/// Locks any unselected entry whose cost the player can no longer afford.
/// <para>
/// Free (0-cost) and negative-cost traits are never locked - they cost nothing or
/// actually give points back. Selected traits are also never locked here; they
/// were affordable when picked and must stay interactive to be deselected.
/// </para>
/// </summary>
public void UpdateGlobalPointsLock(int remainingPoints)
{
foreach (var (_, entry) in _traitEntries)
{
// A selected trait is always unlocked here - same reasoning as category cap:
// it was paid for at selection time and must stay interactive.
// Traits that cost nothing or give points back are never locked by budget.
var shouldLock = !entry.IsSelected && entry.TraitCost > remainingPoints;
entry.SetLockedByPoints(shouldLock);
}
}
public void SetTraitSelected(ProtoId<TraitPrototype> traitId, bool selected)
@@ -171,11 +235,11 @@ public sealed partial class TraitCategory : BoxContainer
/// Updates condition states for all trait entries based on current job/species.
/// Traits that don't meet conditions are disabled but still visible.
/// </summary>
public void UpdateConditions(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
public void UpdateConditions(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences, IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
{
foreach (var (_, entry) in _traitEntries)
{
entry.UpdateConditionsMet(jobId, speciesId, antagPreferences);
entry.UpdateConditionsMet(jobId, speciesId, antagPreferences, selectedTraits);
}
// Update stats since some traits may have been deselected
+160 -39
View File
@@ -27,6 +27,9 @@ public sealed partial class TraitEntry : PanelContainer
private bool _isUpdating;
private readonly List<string> _failedConditionTooltips = new();
private bool _isLockedByCategory;
private bool _isLockedByPoints;
public TraitEntry(TraitPrototype trait)
{
RobustXamlLoader.Load(this);
@@ -59,9 +62,17 @@ public sealed partial class TraitEntry : PanelContainer
foreach (var condition in _trait.Conditions)
{
var tooltip = condition.GetTooltip(_prototype, _loc);
if (!string.IsNullOrEmpty(tooltip))
tooltips.Add(tooltip);
if (condition is TraitDependencyCondition depCond)
{
// Use GetTooltips() to get individual lines without duplication
tooltips.AddRange(depCond.GetTooltips(_prototype, _loc));
}
else
{
var tooltip = condition.GetTooltip(_prototype, _loc);
if (!string.IsNullOrEmpty(tooltip))
tooltips.Add(tooltip);
}
}
if (tooltips.Count > 0)
@@ -91,39 +102,92 @@ public sealed partial class TraitEntry : PanelContainer
/// <summary>
/// Updates whether conditions are met based on current job/species.
/// </summary>
public void UpdateConditionsMet(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
public void UpdateConditionsMet(
ProtoId<JobPrototype>? jobId,
ProtoId<SpeciesPrototype>? speciesId,
IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences,
IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
{
_failedConditionTooltips.Clear();
MeetsConditions = true;
foreach (var condition in _trait.Conditions)
{
var result = condition switch
{
IsSpeciesCondition speciesCond => CheckSpeciesCondition(speciesCond, speciesId),
HasJobCondition jobCond => CheckJobCondition(jobCond, jobId),
InDepartmentCondition deptCond => CheckDepartmentCondition(deptCond, jobId),
HasCompCondition compCond => !compCond.Invert, // can't check in lobby but screws with the inversion logic
IsAntagEligibleCondition antagEligibleCond => CheckAntagEligibleCondition(antagEligibleCond, antagPreferences),
AnyOfCondition anyOfCond => CheckAnyOfCondition(anyOfCond, jobId, speciesId, antagPreferences),
_ => true,
};
bool result;
// Apply inversion
result ^= condition.Invert;
if (condition is TraitDependencyCondition depCond)
{
result = CheckDependencyCondition(depCond, selectedTraits);
// CheckDependencyCondition adds its own tooltips directly
}
else
{
result = condition switch
{
IsSpeciesCondition speciesCond => CheckSpeciesCondition(speciesCond, speciesId),
HasJobCondition jobCond => CheckJobCondition(jobCond, jobId),
InDepartmentCondition deptCond => CheckDepartmentCondition(deptCond, jobId),
HasCompCondition compCond => !compCond.Invert, // can't check in lobby
IsAntagEligibleCondition antagEligibleCond => CheckAntagEligibleCondition(antagEligibleCond, antagPreferences),
AnyOfCondition anyOfCond => CheckAnyOfCondition(anyOfCond, jobId, speciesId, antagPreferences, selectedTraits),
_ => true,
};
// Apply inversion for non-dependency conditions
result ^= condition.Invert;
if (!result)
{
var tooltip = condition.GetTooltip(_prototype, _loc);
if (!string.IsNullOrEmpty(tooltip))
_failedConditionTooltips.Add(tooltip);
}
}
if (!result)
{
MeetsConditions = false;
var tooltip = condition.GetTooltip(_prototype, _loc);
if (!string.IsNullOrEmpty(tooltip))
_failedConditionTooltips.Add(tooltip);
}
}
UpdateDisabledState();
}
/// <summary>
/// Checks a TraitDependencyCondition and adds failure tooltips directly.
/// Returns true if the condition passes.
/// </summary>
private bool CheckDependencyCondition(TraitDependencyCondition condition, IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
{
var passed = true;
foreach (var conflict in condition.Conflicts)
{
if (selectedTraits == null || !selectedTraits.Contains(conflict))
continue;
if (_prototype.TryIndex(conflict, out var conflictProto))
{
_failedConditionTooltips.Add(_loc.GetString("trait-condition-trait-conflict",
("trait", _loc.GetString(conflictProto.Name))));
}
passed = false;
}
foreach (var required in condition.Requires)
{
if (selectedTraits != null && selectedTraits.Contains(required))
continue;
if (_prototype.TryIndex(required, out var requiredProto))
{
_failedConditionTooltips.Add(_loc.GetString("trait-condition-trait-required",
("trait", _loc.GetString(requiredProto.Name))));
}
passed = false;
}
return passed;
}
private bool CheckSpeciesCondition(IsSpeciesCondition condition, ProtoId<SpeciesPrototype>? speciesId)
{
if (!_prototype.TryIndex(speciesId, out var species))
@@ -159,7 +223,12 @@ public sealed partial class TraitEntry : PanelContainer
return antagPreferences.Contains(condition.Antag);
}
private bool CheckAnyOfCondition(AnyOfCondition condition, ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
private bool CheckAnyOfCondition(
AnyOfCondition condition,
ProtoId<JobPrototype>? jobId,
ProtoId<SpeciesPrototype>? speciesId,
IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences,
IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
{
if (condition.Conditions.Count == 0)
return false;
@@ -167,19 +236,27 @@ public sealed partial class TraitEntry : PanelContainer
// Return true if ANY child condition evaluates to true
foreach (var childCondition in condition.Conditions)
{
var result = childCondition switch
{
IsSpeciesCondition speciesCond => CheckSpeciesCondition(speciesCond, speciesId),
HasJobCondition jobCond => CheckJobCondition(jobCond, jobId),
InDepartmentCondition deptCond => CheckDepartmentCondition(deptCond, jobId),
HasCompCondition compCond => !compCond.Invert, // can't check in lobby
AnyOfCondition nestedAnyOf => CheckAnyOfCondition(nestedAnyOf, jobId, speciesId, antagPreferences), // Recursive!
IsAntagEligibleCondition antagEligibleCond => CheckAntagEligibleCondition(antagEligibleCond, antagPreferences),
_ => true,
};
bool result;
// Apply child's inversion
result ^= childCondition.Invert;
if (childCondition is TraitDependencyCondition depCond)
{
result = CheckDependencyCondition(depCond, selectedTraits);
}
else
{
result = childCondition switch
{
IsSpeciesCondition speciesCond => CheckSpeciesCondition(speciesCond, speciesId),
HasJobCondition jobCond => CheckJobCondition(jobCond, jobId),
InDepartmentCondition deptCond => CheckDepartmentCondition(deptCond, jobId),
HasCompCondition compCond => !compCond.Invert, // can't check in lobby
IsAntagEligibleCondition antagEligibleCond => CheckAntagEligibleCondition(antagEligibleCond, antagPreferences),
AnyOfCondition nestedAnyOf => CheckAnyOfCondition(nestedAnyOf, jobId, speciesId, antagPreferences, selectedTraits),
_ => true,
};
result ^= childCondition.Invert;
}
// If any child passes, the AnyOf passes
if (result)
@@ -190,16 +267,50 @@ public sealed partial class TraitEntry : PanelContainer
return false;
}
/// <summary>
/// Called by <see cref="TraitCategory"/> whenever the category's trait or
/// points-cap state changes. Locking only applies to <em>unselected</em>
/// entries - the selected traits are the ones filling the cap and must
/// remain interactive so the player can deselect them.
/// </summary>
public void SetLockedByCategory(bool locked)
{
if (_isLockedByCategory == locked)
return;
_isLockedByCategory = locked;
UpdateDisabledState();
}
/// <summary>
/// Called by <see cref="TraitCategory"/> whenever the player's global point budget
/// changes. Locks unselected entries whose cost the player can no longer afford.
/// Free (0-cost) and negative-cost traits are never locked by this.
/// </summary>
public void SetLockedByPoints(bool locked)
{
if (_isLockedByPoints == locked)
return;
_isLockedByPoints = locked;
UpdateDisabledState();
}
private void UpdateDisabledState()
{
if (!MeetsConditions)
var isSelected = TraitCheckbox.Pressed;
var conditionLocked = !MeetsConditions;
var categoryLocked = _isLockedByCategory && !isSelected;
var pointsLocked = _isLockedByPoints && !isSelected;
var isDisabled = conditionLocked || categoryLocked || pointsLocked;
if (isDisabled)
{
// Hide checkbox, show lock icon
TraitCheckbox.Visible = false;
LockIcon.Visible = true;
// Deselect if conditions no longer met
if (TraitCheckbox.Pressed)
if (isSelected && conditionLocked)
{
_isUpdating = true;
TraitCheckbox.Pressed = false;
@@ -211,14 +322,24 @@ public sealed partial class TraitEntry : PanelContainer
// Add disabled styling
AddStyleClass("TraitsEntryDisabled");
// Update tooltip to show failed conditions
if (_failedConditionTooltips.Count > 0)
// Tooltip priority for conditions: condition failures > category full > insufficient points.
if (conditionLocked && _failedConditionTooltips.Count > 0)
{
var tooltipText = Loc.GetString("trait-conditions-not-met-tooltip",
("requirements", string.Join("\n", _failedConditionTooltips)));
TooltipSupplier = _ => CreateMarkupTooltip(tooltipText);
}
else if (categoryLocked)
{
var tooltipText = Loc.GetString("trait-category-full-tooltip");
TooltipSupplier = _ => CreateMarkupTooltip(tooltipText);
}
else if (pointsLocked)
{
var tooltipText = Loc.GetString("trait-insufficient-points-tooltip");
TooltipSupplier = _ => CreateMarkupTooltip(tooltipText);
}
}
else
{
+18 -8
View File
@@ -154,14 +154,18 @@ public sealed partial class TraitsTab : BoxContainer
}
}
// Check conflicts
foreach (var conflict in trait.Conflicts)
// Check conflicts via TraitDependencyCondition
var depCondition = trait.Conditions.OfType<Content.Shared._DV.Traits.Conditions.TraitDependencyCondition>().FirstOrDefault();
if (depCondition != null)
{
if (!_selectedTraits.Contains(conflict))
continue;
foreach (var conflict in depCondition.Conflicts)
{
if (!_selectedTraits.Contains(conflict))
continue;
RevertTraitToggle(traitId);
return;
RevertTraitToggle(traitId);
return;
}
}
_selectedTraits.Add(traitId);
@@ -176,6 +180,7 @@ public sealed partial class TraitsTab : BoxContainer
}
UpdateGlobalStats();
UpdateAllConditions();
UpdateCategoryStats(trait.Category);
OnTraitsChanged?.Invoke(_selectedTraits);
}
@@ -211,7 +216,7 @@ public sealed partial class TraitsTab : BoxContainer
// If parent width is 0 (not laid out yet), defer until layout happens
if (parentWidth > 0)
{
GlobalPointsBar.SetWidth = (int)((parentWidth - 2 ) * percentage);
GlobalPointsBar.SetWidth = (int)((parentWidth - 2) * percentage);
_awaitingLayoutUpdate = false;
}
else if (!_awaitingLayoutUpdate)
@@ -235,6 +240,11 @@ public sealed partial class TraitsTab : BoxContainer
> 0f => "TraitsProgressBarLow",
_ => "TraitsProgressBarEmpty"
});
foreach (var (_, categoryUi) in _categoryUis)
{
categoryUi.UpdateGlobalPointsLock(remainingPoints);
}
}
private void OnProgressBarParentResized()
@@ -281,7 +291,7 @@ public sealed partial class TraitsTab : BoxContainer
foreach (var (_, categoryUi) in _categoryUis)
{
// If some fork wants to use the top selected job as well, just add that to the UpdateConditions method in the editor
categoryUi.UpdateConditions(null, _profile?.Species, _profile?.AntagPreferences);
categoryUi.UpdateConditions(null, _profile?.Species, _profile?.AntagPreferences, _selectedTraits);
}
RecalculateStats();
@@ -0,0 +1,7 @@
<Button xmlns="https://spacestation14.io">
<TextureRect Name="TextureRect"
TextureScale="0.5 0.5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="KeepCentered"/>
</Button>
@@ -0,0 +1,17 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._DV.UserInterfaces;
[GenerateTypedNameReferences]
public sealed partial class IconButton : Button
{
public Texture? Icon { get => TextureRect.Texture; set => TextureRect.Texture = value; }
public IconButton()
{
RobustXamlLoader.Load(this);
}
}
@@ -24,7 +24,7 @@ public sealed class WizdenContentFreeze
var protoMan = server.ProtoMan;
var recipesCount = protoMan.Count<FoodRecipePrototype>();
var recipesLimit = 269; // DeltaV - was 218
var recipesLimit = 266; // DeltaV - was 218
if (recipesCount > recipesLimit)
{
@@ -6,6 +6,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Player; // DeltaV
namespace Content.Server.AlertLevel;
@@ -191,7 +192,8 @@ public sealed class AlertLevelSystem : EntitySystem
{
if (detail.Sound != null)
{
var filter = _stationSystem.GetInOwningStation(station);
//var filter = _stationSystem.GetInOwningStation(station); // DeltaV - Global Annoucements
var filter = Filter.Empty().AddAllPlayers(); // DeltaV - Global Annoucements
_audio.PlayGlobal(detail.Sound, filter, true, detail.Sound.Params);
}
else
@@ -202,7 +204,7 @@ public sealed class AlertLevelSystem : EntitySystem
if (announce)
{
_chatSystem.DispatchStationAnnouncement(station, announcementFull, playDefaultSound: playDefault,
_chatSystem.DispatchGlobalAnnouncement(announcementFull, playSound: playDefault, // DeltaV - Global Annoucements
colorOverride: detail.Color, sender: stationName);
}
@@ -7,7 +7,7 @@ using Robust.Shared.Player;
namespace Content.Server.Audio;
public sealed class ServerGlobalSoundSystem : SharedGlobalSoundSystem
public sealed partial class ServerGlobalSoundSystem : SharedGlobalSoundSystem // DeltaV - Made Partial
{
[Dependency] private readonly IConsoleHost _conHost = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
@@ -184,6 +184,11 @@ public sealed class MetabolizerSystem : SharedMetabolizerSystem
var rate = entry.MetabolismRate * group.MetabolismRateModifier;
// BEGIN DeltaV - fix FixedPoint2 truncation flooring low rateModifiers to zero
if (rate == FixedPoint2.Zero && entry.MetabolismRate > FixedPoint2.Zero && group.MetabolismRateModifier > 0f)
rate = FixedPoint2.Epsilon;
// END DeltaV
// Remove $rate, as long as there's enough reagent there to actually remove that much
mostToRemove = FixedPoint2.Clamp(rate, 0, quantity);
+13 -8
View File
@@ -5,6 +5,7 @@ using Content.Server.DeviceNetwork.Systems;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Tools;
using Content.Shared._DV.Fax; // DeltaV - fax
using Content.Shared._DV.Pager; // DeltaV - pagers
using Content.Shared.Administration.Logs;
using Content.Shared.Containers.ItemSlots;
@@ -90,10 +91,10 @@ public sealed class FaxSystem : EntitySystem
{
base.Update(frameTime);
var query = EntityQueryEnumerator<FaxMachineComponent, ApcPowerReceiverComponent>();
while (query.MoveNext(out var uid, out var fax, out var receiver))
var query = EntityQueryEnumerator<FaxMachineComponent>();
while (query.MoveNext(out var uid, out var fax)) // DeltaV - faxes aren't necessarily powered
{
if (!receiver.Powered)
if (TryComp<ApcPowerReceiverComponent>(uid, out var receiver) && !receiver.Powered) // DeltaV - faxes aren't necessarily powered
continue;
ProcessPrintingAnimation(uid, frameTime, fax);
@@ -392,7 +393,7 @@ public sealed class FaxSystem : EntitySystem
var canCopy = isPaperInserted &&
component.SendTimeoutRemaining <= 0 &&
component.InsertingTimeRemaining <= 0;
var state = new FaxUiState(component.FaxName, component.KnownFaxes, canSend, canCopy, isPaperInserted, component.DestinationFaxAddress);
var state = new FaxUiState(component.FaxName, component.KnownFaxes, canSend, canCopy, isPaperInserted, component.DestinationFaxAddress, component.SendTimeoutRemaining <= 0); // DeltaV - AI fax
_userInterface.SetUiState(uid, FaxUiKey.Key, state);
}
@@ -461,7 +462,7 @@ public sealed class FaxSystem : EntitySystem
/// Copies the paper in the fax. A timeout is set after copying,
/// which is shared by the send button.
/// </summary>
public void Copy(EntityUid uid, FaxMachineComponent? component, FaxCopyMessage args)
public void Copy(EntityUid uid, FaxMachineComponent? component, BoundUserInterfaceMessage args, EntityUid? item = null) // DeltaV - station AI fax
{
if (!Resolve(uid, ref component))
return;
@@ -469,7 +470,7 @@ public sealed class FaxSystem : EntitySystem
if (component.SendTimeoutRemaining > 0)
return;
var sendEntity = component.PaperSlot.Item;
var sendEntity = item ?? component.PaperSlot.Item; // DeltaV - station AI fax
if (sendEntity == null)
return;
@@ -511,7 +512,7 @@ public sealed class FaxSystem : EntitySystem
/// Sends message to addressee if paper is set and a known fax is selected
/// A timeout is set after sending, which is shared by the copy button.
/// </summary>
public void Send(EntityUid uid, FaxMachineComponent? component, FaxSendMessage args)
public void Send(EntityUid uid, FaxMachineComponent? component, BoundUserInterfaceMessage args, EntityUid? item = null) // DeltaV - station AI fax
{
if (!Resolve(uid, ref component))
return;
@@ -519,7 +520,7 @@ public sealed class FaxSystem : EntitySystem
if (component.SendTimeoutRemaining > 0)
return;
var sendEntity = component.PaperSlot.Item;
var sendEntity = item ?? component.PaperSlot.Item; // DeltaV - station AI fax
if (sendEntity == null)
return;
@@ -635,6 +636,10 @@ public sealed class FaxSystem : EntitySystem
}
_adminLogger.Add(LogType.Action, LogImpact.Low, $"\"{component.FaxName}\" {ToPrettyString(uid):tool} printed {ToPrettyString(printed):subject}: {printout.Content}");
// Begin DeltaV - notify on fax printing
var evt = new FaxPrintedEvent(printed);
RaiseLocalEvent(uid, ref evt);
// End DeltaV - notify on fax printing
}
private void NotifyAdmins(string faxName)
@@ -1,3 +1,4 @@
using Content.Server.RoundEnd; // DeltaV
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
@@ -25,4 +26,29 @@ public sealed partial class ZombieRuleComponent : Component
/// </summary>
[DataField]
public float ZombieShuttleCallPercentage = 0.7f;
/// <summary>
/// DeltaV - The behavior of the round if all zombies are defeated.
/// </summary>
public RoundEndBehavior ZombieRoundEndBehavior = RoundEndBehavior.ShuttleCall;
/// <summary>
/// DeltaV - The amount of time before the evac shuttle will arrive if the ZombieRoundEndBehavior is set to ShuttleCall.
/// </summary>
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(5);
/// <summary>
/// DeltaV - The announcement sender when calling the shuttle.
/// </summary>
public LocId RoundEndTextSender = "comms-console-announcement-title-centcom";
/// <summary>
/// DeltaV - The announcement text when calling a shuttle.
/// </summary>
public LocId RoundEndTextShuttleCall = "zombie-no-more-threat-announcement-shuttle-call";
/// <summary>
/// DeltaV - The announcement text when a shuttle is already called.
/// </summary>
public LocId RoundEndTextAnnouncement = "zombie-no-more-threat-announcement";
}
@@ -30,6 +30,7 @@ using System.Linq;
using Content.Shared.Station.Components;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
using Content.Shared.Cuffs.Components; // DeltaV
namespace Content.Server.GameTicking.Rules;
@@ -500,12 +501,23 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
// Check if there are nuke operatives still alive on the same map as the shuttle,
// or on the same map as the station.
// If there are, the round can continue.
var operatives = EntityQuery<NukeOperativeComponent, MobStateComponent, TransformComponent>(true);
// BEGIN DeltaV - End Rounds Better
// We need to use a EntityQueryEnumerator instead of a EntityQuery, because we need the uid to check for cuffs
var operatives = new List<Entity<NukeOperativeComponent, MobStateComponent, TransformComponent>>();
var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent, TransformComponent>();
while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState, out var transform))
{
operatives.Add((uid, nukeOp, mobState, transform));
}
var operativesAlive = operatives
.Where(op =>
op.Item3.MapID == shuttleMapId
|| op.Item3.MapID == targetStationMap)
.Any(op => op.Item2.CurrentState == MobState.Alive && op.Item1.Running);
op.Comp3.MapID == shuttleMapId
|| op.Comp3.MapID == targetStationMap)
.Any(op => op.Comp2.CurrentState == MobState.Alive && op.Comp1.Running &&
!TryComp<CuffableComponent>(op, out _)); // in the case the crew keeps one alive
// END DeltaV
if (operativesAlive)
return; // There are living operatives than can access the shuttle, or are still on the station's map.
@@ -3,7 +3,7 @@ using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Popups;
using Content.Server.Roles;
using Content.Server.RoundEnd;
using Content.Server.RoundEnd; // DeltaV
using Content.Server.Station.Systems;
using Content.Server.Zombies;
using Content.Shared.GameTicking.Components;
@@ -114,11 +114,31 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
/// </summary>
private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent)
{
// BEGIN DeltaV - End Rounds Better
// Zombies have failed, time to go home
if (zombieRuleComponent.ZombieRoundEndBehavior == RoundEndBehavior.Nothing)
return;
// END DeltaV
var healthy = GetHealthyHumans();
if (healthy.Count == 1) // Only one human left. spooky
_popup.PopupEntity(Loc.GetString("zombie-alone"), healthy[0], healthy[0]);
if (GetInfectedFraction(false) > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested())
// BEGIN DeltaV - End Rounds Better
var infectedPercent = GetInfectedFraction(false);
if (Math.Round(infectedPercent, 0) == 0) // All zombies defeated
{
_roundEnd.DoRoundEndBehavior(zombieRuleComponent.ZombieRoundEndBehavior,
zombieRuleComponent.EvacShuttleTime,
zombieRuleComponent.RoundEndTextSender,
zombieRuleComponent.RoundEndTextShuttleCall,
zombieRuleComponent.RoundEndTextAnnouncement);
zombieRuleComponent.ZombieRoundEndBehavior = RoundEndBehavior.Nothing; // stop this check in the future
}
// END DeltaV
if (infectedPercent > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested()) // DeltaV - Move GetInfectedFraction to var
{
foreach (var station in _station.GetStations())
{
+9 -9
View File
@@ -327,15 +327,15 @@ public sealed class NukeSystem : EntitySystem
// should play
if (nuke.RemainingTime <= _nukeSongLength + nuke.AlertSoundTime + NukeSongBuffer && !nuke.PlayedNukeSong && !ResolvedSoundSpecifier.IsNullOrEmpty(_selectedNukeSong))
{
_sound.DispatchStationEventMusic(uid, _selectedNukeSong, StationEventMusicType.Nuke);
_sound.DispatchGlobalEventMusic(_selectedNukeSong, StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
nuke.PlayedNukeSong = true;
}
// play alert sound if time is running out
if (nuke.RemainingTime <= nuke.AlertSoundTime && !nuke.PlayedAlertSound)
{
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(nuke.AlertSound), new AudioParams{Volume = -5f});
_sound.StopStationEventMusic(uid, StationEventMusicType.Nuke);
_sound.PlayGlobal(_audio.ResolveSound(nuke.AlertSound), new AudioParams { Volume = -5f }); // DeltaV - Global Nuke SFX
_sound.StopGlobalEventMusic(StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
nuke.PlayedAlertSound = true;
UpdateAppearance(uid, nuke);
}
@@ -504,9 +504,9 @@ public sealed class NukeSystem : EntitySystem
("time", (int) component.RemainingTime),
("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((uid, nukeXform)))));
var sender = Loc.GetString("nuke-component-announcement-sender");
_chatSystem.DispatchStationAnnouncement(stationUid ?? uid, announcement, sender, false, null, Color.Red);
_chatSystem.DispatchGlobalAnnouncement(announcement, sender, false, null, Color.Red); // DeltaV - Global Nuke Annoucement
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(component.ArmSound));
_sound.PlayGlobal(_audio.ResolveSound(component.ArmSound)); // DeltaV - Global Nuke Music
_nukeSongLength = (float) _audio.GetAudioLength(_selectedNukeSong).TotalSeconds;
// turn on the spinny light
@@ -544,11 +544,11 @@ public sealed class NukeSystem : EntitySystem
// warn a crew
var announcement = Loc.GetString("nuke-component-announcement-unarmed");
var sender = Loc.GetString("nuke-component-announcement-sender");
_chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false);
_chatSystem.DispatchGlobalAnnouncement(announcement, sender, false); // DeltaV - Global Nuke Announcement
component.PlayedNukeSong = false;
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(component.DisarmSound));
_sound.StopStationEventMusic(uid, StationEventMusicType.Nuke);
_sound.PlayGlobal(_audio.ResolveSound(component.DisarmSound)); // DeltaV - Global Nuke SFX
_sound.StopGlobalEventMusic(StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
// reset nuke remaining time to either itself or the minimum time, whichever is higher
component.RemainingTime = Math.Max(component.RemainingTime, component.MinimumTime);
@@ -610,7 +610,7 @@ public sealed class NukeSystem : EntitySystem
OwningStation = transform.GridUid,
});
_sound.StopStationEventMusic(uid, StationEventMusicType.Nuke);
_sound.StopGlobalEventMusic(StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
Del(uid);
}
@@ -157,7 +157,8 @@ public sealed class RadioSystem : EntitySystem
message,
wrappedMessage,
NetEntity.Invalid,
null);
null,
radioChannelProto: channel.ID); // DeltaV - Add RadioChannel for committing sins
var chatMsg = new MsgChatMessage { Message = chat };
var ev = new RadioReceiveEvent(message, messageSource, channel, radioSource, chatMsg);
+1 -1
View File
@@ -351,7 +351,7 @@ namespace Content.Server.RoundEnd
else
{
RequestRoundEnd(time, null, false, textCall,
Loc.GetString(sender));
Loc.GetString(sender), cantRecall: true);
}
break;
}
@@ -6,4 +6,41 @@ namespace Content.Server.StationEvents.Components;
[RegisterComponent]
public sealed partial class IonStormRuleComponent : Component
{
// DV start - Synthetic tweaks
/// <summary>
/// The chance that a synth gets electrocuted by the ion storm.
/// </summary>
[DataField]
public float SynthElectrocutionChance = 0.6f;
/// <summary>
/// The minimum delay before a synth affected by the ion storm gets electrocuted. (in seconds)
/// </summary>
[DataField]
public int SynthElectrocutionDelayMin = 3;
/// <summary>
/// The maximum delay before a synth affected by the ion storm gets electrocuted. (in seconds)
/// </summary>
[DataField]
public int SynthElectrocutionDelayMax = 10;
/// <summary>
/// The minimum damage dealt by the synth electrocution. (shock damage)
/// </summary>
[DataField]
public int SynthElectrocutionDamageMin = 5;
/// <summary>
/// The maximum damage dealt by the synth electrocution. (shock damage)
/// </summary>8
[DataField]
public int SynthElectrocutionDamageMax = 10;
/// <summary>
/// How long the synth is stunned for by the electrocution. (in seconds)
/// </summary>
[DataField]
public int SynthElectrocutionStunDuration = 3;
// DV end - synthetic tweaks
}
@@ -6,9 +6,12 @@ using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Station.Components;
// CD - start synth trait
using Content.Server.Chat.Managers;
using Content.Server.Electrocution; // DV - added for synth trait
using Content.Shared.Chat;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Content.Shared._ES.Sparks; // Delta-V change - added for synth trait (where they get affected by ion storms)
using Timer = Robust.Shared.Timing.Timer; // DV - used for synth trait delayed damage
// CD - end synth trait
namespace Content.Server.StationEvents.Events;
@@ -17,6 +20,8 @@ public sealed class IonStormRule : StationEventSystem<IonStormRuleComponent>
{
[Dependency] private readonly IonStormSystem _ionStorm = default!;
[Dependency] private readonly IChatManager _chatManager = default!; // CD - Used for synth trait
[Dependency] private readonly ElectrocutionSystem _electrocution = default!; // DV - used for synth trait
[Dependency] private readonly ESSparksSystem _esSparks = default!; // DV - used for synth trait
protected override void Started(EntityUid uid, IonStormRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
@@ -26,18 +31,32 @@ public sealed class IonStormRule : StationEventSystem<IonStormRuleComponent>
return;
// CD - Go through everyone with the SynthComponent and inform them a storm is happening.
// DV - buuuuuut only if IonStormAffected is present so that not all synthetics are affected by this
// but they are still notified about incoming ion storms regardless.
var synthQuery = EntityQueryEnumerator<SynthComponent>();
while (synthQuery.MoveNext(out var ent, out var synthComp))
{
if (RobustRandom.Prob(synthComp.AlertChance))
continue;
if (!TryComp<ActorComponent>(ent, out var actor))
continue;
var msg = Loc.GetString("station-event-ion-storm-synth");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Yellow);
if (RobustRandom.Prob(synthComp.AlertChance))
{
var msg = Loc.GetString("station-event-ion-storm-synth");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Yellow);
}
// DV start - super duper fucked up thing for synths
if (synthComp.IonStormAffected && RobustRandom.Prob(comp.SynthElectrocutionChance))
{
var delay = RobustRandom.Next(TimeSpan.FromSeconds(comp.SynthElectrocutionDelayMin), TimeSpan.FromSeconds(comp.SynthElectrocutionDelayMax)); // they'll never know when it'll hit them
Timer.Spawn(delay, () =>
{
_electrocution.TryDoElectrocution(ent, null, RobustRandom.Next(comp.SynthElectrocutionDamageMin, comp.SynthElectrocutionDamageMax), TimeSpan.FromSeconds(comp.SynthElectrocutionStunDuration), true, ignoreInsulation: true);
_esSparks.DoSparks(ent);
});
}
// DV end
}
// CD - End of synth trait
+10 -1
View File
@@ -7,7 +7,16 @@ namespace Content.Server._CD.Traits;
public sealed partial class SynthComponent : Component
{
/// <summary>
/// The chance that the synth is alerted of an ion storm
/// Whether this synthetic is affected by ion storms (electrocution/sparks and
/// alerts when one occurs). False by default. set true by the
/// "Susceptible to Ion Storms" meta trait.
/// </summary>
[DataField]
public bool IonStormAffected;
/// <summary>
/// The chance that the synth is alerted of an ion storm. Only relevant if
/// <see cref="IonStormAffected"/> is true.
/// </summary>
[DataField]
public float AlertChance = 0.3f;
+24 -1
View File
@@ -1,8 +1,11 @@
using Content.Server.Body.Systems;
using Content.Server.Database;
using Content.Shared.Body.Components;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Chat.TypingIndicator;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Speech.Components;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
namespace Content.Server._CD.Traits;
@@ -12,9 +15,13 @@ public sealed class SynthSystem : EntitySystem
// Begin DeltaV - make strings static readonly
private static readonly ProtoId<TypingIndicatorPrototype> RobotTypingIndicator = "robot";
private static readonly ProtoId<ReagentPrototype> SynthBloodReagent = "SynthBlood";
private static readonly ProtoId<TagPrototype> SyntheticEmotesTag = "SyntheticEmotes";
private static readonly ProtoId<EmoteSoundsPrototype> SyntheticEmoteSounds = "SyntheticEmoteSounds";
// End DeltaV
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public override void Initialize()
{
@@ -38,5 +45,21 @@ public sealed class SynthSystem : EntitySystem
_bloodstream.ChangeBloodReagents((uid, bloodstream), new([new(SynthBloodReagent, bloodstream.BloodReferenceSolution.Volume)]));
}
// End DeltaV
// Begin DeltaV - Add SyntheticEmotes tag and merge synthetic emote sounds into species sounds
_tag.AddTag(uid, SyntheticEmotesTag);
if (TryComp<VocalComponent>(uid, out var vocal)
&& _proto.TryIndex(SyntheticEmoteSounds, out var synthSounds)
&& vocal.EmoteSounds is { } currentSoundsId
&& _proto.TryIndex(currentSoundsId, out var currentSounds))
{
foreach (var (emoteId, sound) in synthSounds.Sounds)
{
currentSounds.Sounds.TryAdd(emoteId, sound);
}
Dirty(uid, vocal);
}
// End DeltaV
}
}
}
@@ -0,0 +1,73 @@
using Content.Shared.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Map;
namespace Content.Server.Audio;
/// <summary>
/// Extends upstream's Content.Server/Audio/ServerGlobalSoundSystem.cs.
/// </summary>
public sealed partial class ServerGlobalSoundSystem
{
/// <summary>
/// DeltaV - Plays a sound globally for all players, no matter where they are.
/// </summary>
/// <param name="specifier"></param>
/// <param name="audioParams"></param>
public void PlayGlobal(ResolvedSoundSpecifier specifier, AudioParams? audioParams = null)
{
var msg = new GameGlobalSoundEvent(specifier, audioParams);
RaiseNetworkEvent(msg);
}
public void StopGlobalEventMusic(StationEventMusicType type)
{
var msg = new StopStationEventMusic(type);
RaiseNetworkEvent(msg);
}
public void DispatchGlobalEventMusic(SoundSpecifier sound, StationEventMusicType type)
{
DispatchGlobalEventMusic(_audio.ResolveSound(sound), type);
}
public void DispatchGlobalEventMusic(ResolvedSoundSpecifier specifier, StationEventMusicType type)
{
var audio = AudioParams.Default.WithVolume(-8);
var msg = new StationEventMusicEvent(specifier, type, audio);
RaiseNetworkEvent(msg);
}
/// <summary>
/// DeltaV - Plays a sound globally for all players on a specified Map.
/// </summary>
/// <param name="specifier"></param>
/// <param name="audioParams"></param>
public void PlayGlobalOnMap(MapId map, ResolvedSoundSpecifier specifier, AudioParams? audioParams = null)
{
var msg = new GameGlobalSoundEvent(specifier, audioParams);
var filter = Filter.Empty().AddInMap(map);
RaiseNetworkEvent(msg, filter);
}
public void StopMapEventMusic(MapId map, StationEventMusicType type)
{
var msg = new StopStationEventMusic(type);
var filter = Filter.Empty().AddInMap(map);
RaiseNetworkEvent(msg, filter);
}
public void DispatchMapEventMusic(MapId map, SoundSpecifier sound, StationEventMusicType type)
{
DispatchMapEventMusic(map, _audio.ResolveSound(sound), type);
}
public void DispatchMapEventMusic(MapId map, ResolvedSoundSpecifier specifier, StationEventMusicType type)
{
var audio = AudioParams.Default.WithVolume(-8);
var msg = new StationEventMusicEvent(specifier, type, audio);
var filter = Filter.Empty().AddInMap(map);
RaiseNetworkEvent(msg, filter);
}
}
@@ -61,6 +61,39 @@ public sealed partial class CosmicCultRuleComponent : Component
[DataField]
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(5);
#region Progress Checking
/// <summary>
/// The next time the monument should check the progress of the cult.
/// </summary>
public TimeSpan NextProgressCheck = TimeSpan.Zero;
/// <summary>
/// The amount of time between progress checks.
/// </summary>
public TimeSpan TimeBetweenProgressChecks = TimeSpan.FromMinutes(15);
/// <summary>
/// The progress value of the monument last time it was checked.
/// </summary>
[DataField]
public int LastProgress = 0;
/// <summary>
/// The number of times the the progress check as failed. Resets after some progress.
/// </summary>
public int ConsecutiveProgressFails = 0;
/// <summary>
/// The number of times the progress check has to fail for the round to start to end.
/// </summary>
public int ProgressFailureTolerance = 3;
public LocId ProgressFailTextShuttleCall = "cosmiccult-progress-fail-shuttle-call";
public LocId ProgressFailTextAnnouncement = "cosmiccult-progress-fail-announcement";
#endregion
[DataField]
public HashSet<EntityUid> Cultists = [];
@@ -62,13 +62,15 @@ using Robust.Shared.Random;
using Robust.Shared.Timing;
using System.Collections.Immutable;
using System.Linq;
using Content.Shared.StatusIcon;
using Content.Shared.SSDIndicator;
namespace Content.Server._DV.CosmicCult;
/// <summary>
/// Where all the main stuff for Cosmic Cultists happens.
/// </summary>
public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponent>
public sealed partial class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponent>
{
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
@@ -172,6 +174,28 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
component.StewardVoteTimer = null;
StewardVote();
}
// Progress check. Is the cult trying to progress at all?
if (component.MonumentPlaced && _timing.CurTime > component.NextProgressCheck)
{
// We're going to assume one monument.
var monument = EntityQuery<MonumentComponent>().First();
if (component.LastProgress == monument.CurrentProgress)
component.ConsecutiveProgressFails++;
else
component.ConsecutiveProgressFails = 0;
// They haven't progressed enough in X checks, end the round.
if (component.ConsecutiveProgressFails >= component.ProgressFailureTolerance) {
SetWinType((uid, component), WinType.CrewMinor);
_roundEnd.DoRoundEndBehavior(component.RoundEndBehavior, component.EvacShuttleTime, component.RoundEndTextSender, component.ProgressFailTextShuttleCall, component.ProgressFailTextAnnouncement);
component.RoundEndBehavior = RoundEndBehavior.Nothing;
}
component.LastProgress = monument.CurrentProgress;
component.NextProgressCheck = _timing.CurTime + component.TimeBetweenProgressChecks;
}
if (component.ExtraRiftTimer is { } riftTimer && _timing.CurTime >= riftTimer && !component.RiftStop)
{
component.ExtraRiftTimer = _timing.CurTime + _rand.Next(TimeSpan.FromSeconds(230), TimeSpan.FromSeconds(360)); //3min50 to 6min between new rifts. Seconds instead of minutes for granularity.
@@ -442,17 +466,19 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
}
}
private bool CultistsAlive()
private int GetCultistsAlive()
{
var query = EntityQueryEnumerator<CosmicCultComponent, MobStateComponent>();
int cultistsAlive = 0;
while (query.MoveNext(out var ent, out _, out var mobComp))
{
if (TryComp<CuffableComponent>(ent, out var cuffed) && cuffed.CuffedHandCount > 0) continue;
if (TryComp<SSDIndicatorComponent>(ent, out var ssd) && ssd.IsSSD) continue; // TODO: Maybe check how long.
if (mobComp.Running && mobComp.CurrentState != MobState.Dead)
return true;
cultistsAlive++;
}
return false;
return cultistsAlive;
}
private void OnMobStateChanged(Entity<CosmicCultComponent> ent, ref MobStateChangedEvent args)
@@ -467,14 +493,19 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
private void CheckForActiveCultists()
{
if (CultistsAlive())
// A lone cultist can't do anything, so check for more than one.
if (GetCultistsAlive() > 1)
return;
var query = QueryActiveRules();
List<Entity<CosmicCultRuleComponent>> cosCultRules = new();
// ConfirmWinState needs to be outside the query, because it might add an active rule
while (query.MoveNext(out var ruleUid, out _, out var ruleComp, out _))
{
ConfirmWinState((ruleUid, ruleComp));
cosCultRules.Add((ruleUid, ruleComp));
}
cosCultRules.ForEach(ConfirmWinState);
}
private void ConfirmWinState(Entity<CosmicCultRuleComponent> ent)
@@ -504,8 +535,6 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
else
SetWinType(ent, WinType.Neutral); //The monument wasn't completed, no cultists escaped to midpoint. Some cultists still remain on the station, though.
if (CultistsAlive()) return; //If there are no cultists alive, ignore all previous checks, crew alreay won.
if (tier <= 1) //Prevent the cult getting cooked by accident before anyone even knows there's a cult.
{
SetWinType(ent, WinType.CrewMinor); //If we somehow get here at the end of the round, we'll still count this as a crew minor.
@@ -521,7 +550,7 @@ public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponen
{
finComp.CurrentState = FinaleState.Unavailable;
_popup.PopupCoordinates(Loc.GetString("cosmiccult-monument-powerdown"), Transform(gameruleMonument).Coordinates, PopupType.Large);
_sound.StopStationEventMusic(gameruleMonument, StationEventMusicType.CosmicCult);
_sound.StopGlobalEventMusic(StationEventMusicType.CosmicCult);
_monument.UpdateMonumentAppearance(gameruleMonument, false);
}
@@ -82,11 +82,9 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
_corrupting.SetCorruptionTime((uid, corruptingComp), TimeSpan.FromSeconds(1));
_appearance.SetData(uid, MonumentVisuals.FinaleReached, 2);
_sound.DispatchStationEventMusic(uid, comp.SelectedSong, StationEventMusicType.CosmicCult);
_chatSystem.DispatchStationAnnouncement(uid,
Loc.GetString("cosmiccult-finale-location", ("location", indicatedLocation)),
null, false, null,
Color.FromHex("#cae8e8"));
_sound.DispatchGlobalEventMusic(comp.SelectedSong, StationEventMusicType.CosmicCult);
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cosmiccult-finale-location", ("location", indicatedLocation)),
null, false, null, Color.FromHex("#cae8e8"));
var stationUid = _station.GetStationInMap(Transform(uid).MapID);
if (stationUid != null)
@@ -128,8 +126,8 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
if (stationUid != null)
_alert.SetLevel(stationUid.Value, "green", true, true, true);
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(comp.CancelEventSound));
_sound.StopStationEventMusic(uid, StationEventMusicType.CosmicCult);
_sound.PlayGlobal(_audio.ResolveSound(comp.CancelEventSound));
_sound.StopGlobalEventMusic(StationEventMusicType.CosmicCult);
uid.Comp.CurrentState = FinaleState.ReadyFinale;
if (TryComp<CosmicCorruptingComponent>(uid, out var corruptingComp))
@@ -8,6 +8,7 @@ using Content.Shared.Mind;
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -25,6 +26,7 @@ public sealed class CosmicChantrySystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _role = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly NavMapSystem _navMap = default!;
/// <summary>
@@ -79,11 +81,11 @@ public sealed class CosmicChantrySystem : EntitySystem
comp.SpawnTimer = _timing.CurTime + comp.SpawningTime;
comp.CountdownTimer = _timing.CurTime + comp.EventTime;
_sound.PlayGlobalOnStation(ent, _audio.ResolveSound(comp.ChantryAlarm));
_chatSystem.DispatchStationAnnouncement(ent,
Loc.GetString("cosmiccult-chantry-location", ("location", indicatedLocation)),
null, false, null,
Color.FromHex("#cae8e8"));
var targetMap = _transform.GetMapId(ent.Owner);
_sound.PlayGlobalOnMap(targetMap, _audio.ResolveSound(comp.ChantryAlarm));
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cosmiccult-chantry-location",
("location", indicatedLocation)), null, false, null, Color.FromHex("#cae8e8"));
if (_mind.TryGetMind(comp.InternalVictim, out _, out var mind))
mind.PreventGhosting = true;
@@ -79,13 +79,13 @@ public sealed class MonumentSystem : SharedMonumentSystem
{
comp.SongTimer = null;
if (comp.SelectedSong is { } song)
_sound.DispatchStationEventMusic(uid, song, StationEventMusicType.CosmicCult);
_sound.DispatchGlobalEventMusic(song, StationEventMusicType.CosmicCult);
}
if (comp.CurrentState == FinaleState.ActiveFinale && comp.FinaleAnnounceCheck && comp.FinaleTimer - _timing.CurTime < comp.VisualsThreshold)
{
_appearance.SetData(uid, MonumentVisuals.FinaleReached, 3);
_chatSystem.DispatchStationAnnouncement(uid, Loc.GetString("cosmiccult-announce-finale-warning"), null, false, null, Color.FromHex("#cae8e8"));
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cosmiccult-announce-finale-warning"), null, false, null, Color.FromHex("#cae8e8"));
comp.FinaleAnnounceCheck = false;
}
@@ -90,6 +90,7 @@ public sealed class DeepFryerSystem : SharedDeepFryerSystem
private void OnPowerChanged(Entity<DeepFryerComponent> ent, ref PowerChangedEvent args)
{
UpdateAppearance(ent);
ResetCookingItemsStartTime(ent);
}
private void OnThrowHitBy(Entity<DeepFryerComponent> ent, ref ThrowHitByEvent args)
@@ -674,4 +675,26 @@ public sealed class DeepFryerSystem : SharedDeepFryerSystem
// Add the split solution to the food
Solution.AddSolution(foodSolutionEnt.Value, transferredSolution);
}
/// <summary>
/// Resets the starttime for cooking items (on power change)
/// </summary>
private void ResetCookingItemsStartTime(Entity<DeepFryerComponent> ent)
{
if (_power.IsPowered(ent.Owner) && !ent.Comp.WasPreviouslyPowered)
{
// Power just came back on - reset all cooking timers
foreach (var itemUid in ent.Comp.CookingItems.Keys.ToList())
{
var cookingItem = ent.Comp.CookingItems[itemUid];
cookingItem.TimeStarted = _timing.CurTime;
ent.Comp.CookingItems[itemUid] = cookingItem;
}
ent.Comp.WasPreviouslyPowered = true;
}
else if (!_power.IsPowered(ent.Owner) && ent.Comp.WasPreviouslyPowered)
{
ent.Comp.WasPreviouslyPowered = false;
}
}
}
@@ -0,0 +1,39 @@
using Content.Server.Fax;
using Content.Shared._DV.Silicons;
using Content.Shared.Fax;
using Content.Shared.Fax.Components;
namespace Content.Server._DV.Silicons;
public sealed class StationAiFaxSystem : SharedStationAiFaxSystem
{
[Dependency] private readonly FaxSystem _fax = default!;
public override void Initialize()
{
base.Initialize();
Subs.BuiEvents<StationAiFaxComponent>(FaxUiKey.Key,
subs =>
{
subs.Event<StationAiFaxSendMessage>(OnSend);
subs.Event<StationAiFaxDuplicateMessage>(OnDuplicate);
});
}
private void OnSend(Entity<StationAiFaxComponent> ent, ref StationAiFaxSendMessage args)
{
if (GetDocument(ent, args.Document) is not { } document)
return;
_fax.Send(ent, Comp<FaxMachineComponent>(ent), args, document);
}
private void OnDuplicate(Entity<StationAiFaxComponent> ent, ref StationAiFaxDuplicateMessage args)
{
if (GetDocument(ent, args.Document) is not { } document)
return;
_fax.Copy(ent, Comp<FaxMachineComponent>(ent), args, document);
}
}
@@ -1,9 +1,9 @@
using Content.Server.Power.Components;
using Content.Server.StationEvents.Events;
using Content.Server._DV.StationEvents.Events;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
namespace Content.Server.StationEvents.Components;
namespace Content.Server._DV.StationEvents.Components;
/// <summary>
/// When fired, turns off power on the station for a few seconds, playing <see cref="EpsilonEventRuleComponent.PowerOffSound"/>
@@ -1,14 +1,15 @@
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.StationEvents.Components;
using Content.Server.StationEvents.Events;
using Content.Shared.GameTicking.Components;
using Content.Shared.Station.Components;
using Content.Server.AlertLevel;
using Content.Server.Audio;
using Content.Server._DV.StationEvents.Components;
using Robust.Shared.Audio.Systems;
namespace Content.Server.StationEvents.Events
{
namespace Content.Server._DV.StationEvents.Events;
public sealed class EpsilonEventRule : StationEventSystem<EpsilonEventRuleComponent>
{
[Dependency] private readonly ApcSystem _apcSystem = default!;
@@ -25,7 +26,7 @@ public sealed class EpsilonEventRule : StationEventSystem<EpsilonEventRuleCompon
return;
component.AffectedStation = chosenStation.Value;
// Plays the power off sound to the station.
// Plays the power off sound for those who are on the station.
_sound.PlayGlobalOnStation(component.AffectedStation, _audio.ResolveSound(component.PowerOffSound));
var query = AllEntityQuery<ApcComponent, TransformComponent>();
@@ -70,4 +71,3 @@ public sealed class EpsilonEventRule : StationEventSystem<EpsilonEventRuleCompon
_alertLevelSystem.SetLevel(component.AffectedStation, "epsilon", true, true, true);
}
}
}
+2 -1
View File
@@ -109,7 +109,8 @@ public sealed class TraitSystem : EntitySystem
JobId = jobId,
SpeciesId = speciesId,
Profile = profile,
StatusEffects = _statusEffects
StatusEffects = _statusEffects,
SelectedTraits = selectedTraits
};
foreach (var traitId in selectedTraits)
+9 -1
View File
@@ -41,7 +41,13 @@ namespace Content.Shared.Chat
[NonSerialized]
public bool Read;
public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, int? senderKey, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
/// <summary>
/// DeltaV - If not null, contains the radio channel proto ID that the message was sent on.
/// Used to replace the channel color client-side.
/// </summary>
public string? RadioChannelProtoId = null;
public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, int? senderKey, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, string? radioChannelProto = null) // DeltaV - added radioChannelProto
{
Channel = channel;
Message = message;
@@ -52,6 +58,7 @@ namespace Content.Shared.Chat
MessageColorOverride = colorOverride;
AudioPath = audioPath;
AudioVolume = audioVolume;
RadioChannelProtoId = radioChannelProto; // DeltaV
}
public ChatMessage(ChatMessage copyFrom)
@@ -66,6 +73,7 @@ namespace Content.Shared.Chat
AudioPath = copyFrom.AudioPath;
AudioVolume = copyFrom.AudioVolume;
Read = copyFrom.Read;
RadioChannelProtoId = copyFrom.RadioChannelProtoId; // DeltaV
}
}
@@ -2,6 +2,7 @@ using System.Linq;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Ghost;
using Content.Shared.Interaction;
using Content.Shared.Inventory; // Harmony - for lanyards
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using JetBrains.Annotations;
@@ -293,7 +294,7 @@ namespace Content.Shared.Examine
/// If you're pushing multiple messages that should be grouped together (or ordered in some way),
/// call <see cref="PushGroup"/> before pushing and <see cref="PopGroup"/> when finished.
/// </summary>
public sealed class ExaminedEvent : EntityEventArgs
public sealed class ExaminedEvent : EntityEventArgs, IInventoryRelayEvent //Harmony InventoryRelayEvent for Lanyards
{
/// <summary>
/// The message that will be displayed as the examine text.
@@ -529,6 +530,7 @@ namespace Content.Shared.Examine
}
private record ExamineMessagePart(FormattedMessage Message, int Priority, bool DoNewLine, string? Group);
public SlotFlags TargetSlots { get; } = SlotFlags.NECK; // Harmony - For targeting lanyards.
}
@@ -63,7 +63,7 @@ public sealed partial class FaxMachineComponent : Component
/// Sound to play when fax printing new message
/// </summary>
[DataField]
public SoundSpecifier PrintSound = new SoundPathSpecifier("/Audio/Machines/printer.ogg");
public SoundSpecifier? PrintSound = new SoundPathSpecifier("/Audio/Machines/printer.ogg"); // DeltaV - nullable
/// <summary>
/// Sound to play when fax successfully send message
+4 -1
View File
@@ -17,13 +17,15 @@ public sealed class FaxUiState : BoundUserInterfaceState
public bool IsPaperInserted { get; }
public bool CanSend { get; }
public bool CanCopy { get; }
public bool CanSendCooldown; // DeltaV - AI fax
public FaxUiState(string deviceName,
Dictionary<string, string> peers,
bool canSend,
bool canCopy,
bool isPaperInserted,
string? destAddress)
string? destAddress,
bool canSendCooldown) // DeltaV - AI fax
{
DeviceName = deviceName;
AvailablePeers = peers;
@@ -31,6 +33,7 @@ public sealed class FaxUiState : BoundUserInterfaceState
CanSend = canSend;
CanCopy = canCopy;
DestinationAddress = destAddress;
CanSendCooldown = canSendCooldown; // DeltaV - AI fax
}
}
@@ -33,6 +33,7 @@ using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Wieldable;
using Content.Shared.Zombies;
using Content.Shared.Examine; //Harmony
namespace Content.Shared.Inventory;
@@ -64,6 +65,7 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, BeforeEmoteEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, StoodEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, DownedEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, ExaminedEvent>(RelayInventoryEvent); // Harmony - added for lanyards.
// by-ref events
SubscribeLocalEvent<InventoryComponent, RefreshFrictionModifiersEvent>(RefRelayInventoryEvent);
@@ -1,6 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement; //Harmony Lanyards
using Content.Shared.Inventory; //Harmony Lanyards
using Content.Shared.Labels.Components;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Paper;
@@ -8,6 +10,7 @@ using Content.Shared.Tag; // DeltaV
using Robust.Shared.Containers;
using Robust.Shared.Prototypes; // DeltaV
using Robust.Shared.Utility;
using System.Linq; //Harmony Lanyards
namespace Content.Shared.Labels.EntitySystems;
@@ -34,6 +37,11 @@ public sealed partial class LabelSystem : EntitySystem
SubscribeLocalEvent<PaperLabelComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
SubscribeLocalEvent<PaperLabelComponent, EntRemovedFromContainerMessage>(OnContainerModified);
SubscribeLocalEvent<PaperLabelComponent, ExaminedEvent>(OnExamined);
// Harmony - OnExamined can now be inventory relayed from the neck slot for lanyard implementation,
// getting the entity's pronouns and changing the label inspection text to reflect it being from a lanyard.
// This would cause any other neck slot item with the label component to be described as a lanyard on inspection,
// but currently no others exist.
SubscribeLocalEvent<PaperLabelComponent, InventoryRelayedEvent<ExaminedEvent>>((e, c, ev) => OnExaminedInInventory(e, c, ev.Args));
}
private void OnLabelCompMapInit(Entity<LabelComponent> ent, ref MapInitEvent args)
@@ -123,9 +131,65 @@ public sealed partial class LabelSystem : EntitySystem
args.PushMarkup(Loc.GetString("comp-paper-label-has-label"));
var text = paper.Content;
args.PushMarkup(text.TrimEnd());
// Harmony addition begins - shows which stamps have been applied to a label when inspected. Copied from PaperSystem.
if (paper.StampedBy.Count > 0)
{
var commaSeparated =
string.Join(", ", paper.StampedBy.Select(s => Loc.GetString(s.StampedName)));
args.PushMarkup(
Loc.GetString(
"comp-label-examine-detail-stamped-by",
("stamps", commaSeparated))
);
}
// Harmony addition ends
}
}
// Harmony addition begins - version of OnExamined for when the event is inventory relayed. Used when reading from a worn lanyard.
private void OnExaminedInInventory(EntityUid uid, PaperLabelComponent comp, ExaminedEvent args)
{
if (comp.LabelSlot.Item is not { Valid: true } item)
return;
using (args.PushGroup(nameof(PaperLabelComponent)))
{
// UID's parent is saved to be used for localisation grammar when the label is from a lanyard, since the text is changed.
var user = Comp<TransformComponent>(uid).ParentUid;
if (!args.IsInDetailsRange)
{
args.PushMarkup(Loc.GetString("comp-lanyard-has-lanyard-cant-read", ("user", Identity.Entity(user, EntityManager))));
return;
}
if (!TryComp(item, out PaperComponent? paper))
// Assuming yaml has the correct entity whitelist, this should not happen.
return;
if (string.IsNullOrWhiteSpace(paper.Content))
{
args.PushMarkup(Loc.GetString("comp-lanyard-has-lanyard-blank", ("user", Identity.Entity(user, EntityManager))));
return;
}
args.PushMarkup(Loc.GetString("comp-lanyard-has-lanyard", ("user", Identity.Entity(user, EntityManager))));
var text = paper.Content;
args.PushMarkup(text.TrimEnd());
// Harmony - shows which stamps have been applied to a lanyard's label when inspected. Copied from PaperSystem.
if (paper.StampedBy.Count > 0)
{
var commaSeparated =
string.Join(", ", paper.StampedBy.Select(s => Loc.GetString(s.StampedName)));
args.PushMarkup(
Loc.GetString(
"comp-lanyard-examine-detail-stamped-by",
("stamps", commaSeparated))
);
}
}
}
// Harmony Addition Ends
// Not ref-sub due to being used for multiple subscriptions.
private void OnContainerModified(EntityUid uid, PaperLabelComponent label, ContainerModifiedMessage args)
{
@@ -68,6 +68,11 @@ public abstract class SharedSuitSensorSystem : EntitySystem
/// <returns>True if the sensor is assigned to a station or assigning it was successful. False otherwise.</returns>
public bool CheckSensorAssignedStation(Entity<SuitSensorComponent> sensor)
{
// Begin DeltaV - fallback
if (sensor.Comp.StationId.HasValue && _stationSystem.GetOwningStation(sensor.Owner) is null)
return true;
// End DeltaV - fallback
if (!sensor.Comp.StationId.HasValue && Transform(sensor.Owner).GridUid == null)
return false;
@@ -348,7 +353,7 @@ public abstract class SharedSuitSensorSystem : EntitySystem
var transform = ent.Comp2;
// check if sensor is enabled and worn by user
if (sensor.Mode == SuitSensorMode.SensorOff || sensor.User == null || !HasComp<MobStateComponent>(sensor.User) || transform.GridUid == null)
if (sensor.Mode == SuitSensorMode.SensorOff || sensor.User == null || !HasComp<MobStateComponent>(sensor.User)) // DeltaV - don't block on grids here
return null;
// try to get mobs id from ID slot
+1 -1
View File
@@ -276,7 +276,7 @@ public sealed class PaperSystem : EntitySystem
_appearance.SetData(entity, PaperVisuals.Status, status, appearance);
}
private void UpdateUserInterface(Entity<PaperComponent> entity)
public void UpdateUserInterface(Entity<PaperComponent> entity) // DeltaV - station AI
{
_uiSystem.SetUiState(entity.Owner, PaperUiKey.Key, new PaperBoundUserInterfaceState(entity.Comp.Content, entity.Comp.StampedBy, entity.Comp.Mode));
}
@@ -120,10 +120,23 @@ public abstract partial class SharedBatterySystem : EntitySystem
var rechargerQuery = EntityQueryEnumerator<BatterySelfRechargerComponent, BatteryComponent>();
while (rechargerQuery.MoveNext(out var uid, out var recharger, out var battery))
{
// BEGIN DeltaV - Battery Drain
// Currently, NextAutoRecharge is only set when there's an explicit power draw from
// the battery, and it doesn't count a negative charge rate as battery charge being used
// so, this is to patch that in the case that the drain exceeds the recharge.
if (recharger.AutoRechargePauseTime > TimeSpan.Zero && battery.ChargeRate < 0)
{
TrySetChargeCooldown((uid, recharger));
continue;
}
// END DeltaV
if (recharger.NextAutoRecharge == null || curTime < recharger.NextAutoRecharge)
continue;
recharger.NextAutoRecharge = null; // Don't refresh every tick.
Dirty(uid, recharger);
RefreshChargeRate((uid, battery)); // Cooldown is over, apply the new recharge rate.
}
@@ -32,4 +32,10 @@ public sealed partial class PowerCellDrawComponent : Component
/// </summary>
[DataField, AutoNetworkedField]
public float UseCharge;
/// <summary>
/// DeltaV - If the battery's recharge should be paused while power draw is active.
/// </summary>
[DataField, AutoNetworkedField]
public bool PauseRecharging;
}
@@ -17,8 +17,14 @@ public sealed partial class PowerCellSystem
ent.Comp.Enabled = enabled;
Dirty(ent, ent.Comp);
if (TryGetBatteryFromSlot(ent.Owner, out var battery))
_battery.RefreshChargeRate(battery.Value.AsNullable());
// BEGIN DeltaV - Allow PowerDraw to prevent recharge
if (TryGetBatteryFromSlot(ent.Owner, out var battery) && battery is { } existingBattery)
{
_battery.RefreshChargeRate(existingBattery.AsNullable());
if (ent.Comp.PauseRecharging) _battery.TrySetChargeCooldown(existingBattery.Owner);
}
// END DeltaV
}
@@ -24,7 +24,7 @@ public sealed partial class RadioChannelPrototype : IPrototype
public int Frequency { get; private set; } = 0;
[DataField("color")]
public Color Color { get; private set; } = Color.Lime;
public Color Color { get; set; } = Color.Lime; // DeltaV - Client-side radio channel colors, no longer private set
[IdDataField, ViewVariables]
public string ID { get; private set; } = default!;
@@ -0,0 +1,73 @@
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
namespace Content.Shared._DV.CCVars;
public sealed partial class DCCVars
{
public static readonly CVarDef<string> RadioColorCommon =
CVarDef.Create("deltav.options.radio.common_color",
"#2cdb2c",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Common radio channel.");
public static readonly CVarDef<string> RadioColorCommand =
CVarDef.Create("deltav.options.radio.command_color",
"#fcdf03",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Command radio channel.");
public static readonly CVarDef<string> RadioColorSecurity =
CVarDef.Create("deltav.options.radio.security_color",
"#8c93f5",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Security radio channel.");
public static readonly CVarDef<string> RadioColorMedical =
CVarDef.Create("deltav.options.radio.medical_color",
"#57b8f0",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Medical radio channel.");
public static readonly CVarDef<string> RadioColorEngineering =
CVarDef.Create("deltav.options.radio.engineering_color",
"#ff733c",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Engineering radio channel.");
public static readonly CVarDef<string> RadioColorScience =
CVarDef.Create("deltav.options.radio.science_color",
"#cd7ccd",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Science radio channel.");
public static readonly CVarDef<string> RadioColorCargo =
CVarDef.Create("deltav.options.radio.cargo_color",
"#b48b57",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Cargo radio channel.");
public static readonly CVarDef<string> RadioColorService =
CVarDef.Create("deltav.options.radio.service_color",
"#539c00",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Service radio channel.");
public static readonly CVarDef<string> RadioColorBinary =
CVarDef.Create("deltav.options.radio.binary_color",
"#5ed7aa",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Binary radio channel.");
public static readonly CVarDef<string> RadioColorJustice =
CVarDef.Create("deltav.options.radio.justice_color",
"#c70667",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Justice radio channel.");
public static readonly CVarDef<string> RadioColorPrison =
CVarDef.Create("deltav.options.radio.prison_color",
"#FFA500",
CVar.CLIENTONLY | CVar.ARCHIVE,
"The color of the Prison radio channel.");
}
@@ -0,0 +1,4 @@
namespace Content.Shared._DV.Fax;
[ByRefEvent]
public readonly record struct FaxPrintedEvent(EntityUid Fax);
@@ -137,6 +137,12 @@ public sealed partial class DeepFryerComponent : Component
/// <seealso cref="ProfessionalChefComponent"/>
[DataField]
public float MissChance = 0.25f;
/// <summary>
/// Check for whether the cooking timer needs to be reset after powering down
/// </summary>
[DataField]
public bool WasPreviouslyPowered = true;
}
/// <summary>
@@ -1,3 +1,4 @@
using Content.Shared.CartridgeLoader;
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Events;
using Content.Shared.DeviceNetwork.Systems;
@@ -54,5 +55,11 @@ public sealed class PageSenderSystem : EntitySystem
{ PagerConstants.DataBody, body },
};
_deviceNetwork.QueuePacket(ent, null, payload);
if (HasComp<PagerComponent>(ent))
{
var evt = new CartridgeLoaderNotificationSentEvent(Loc.GetString("pager-self-notification"), body);
RaiseLocalEvent(ent, ref evt);
}
}
}
@@ -0,0 +1,82 @@
using Content.Shared._DV.Fax;
using Content.Shared.Fax;
using Content.Shared.Paper;
using Content.Shared.Silicons.StationAi;
using Robust.Shared.Containers;
namespace Content.Shared._DV.Silicons;
public abstract class SharedStationAiFaxSystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
[Dependency] private readonly PaperSystem _paper = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StationAiFaxComponent, EntInsertedIntoContainerMessage>(OnInserted);
SubscribeLocalEvent<StationAiFaxComponent, FaxPrintedEvent>(OnFaxPrinted);
Subs.BuiEvents<StationAiFaxComponent>(FaxUiKey.Key,
subs =>
{
subs.Event<StationAiFaxExamineMessage>(OnExamine);
subs.Event<StationAiFaxShredMessage>(OnShred);
});
}
protected virtual void OnInserted(Entity<StationAiFaxComponent> ent, ref EntInsertedIntoContainerMessage args)
{
if (HasComp<PaperComponent>(args.Entity))
EnsureComp<StationAiWhitelistComponent>(args.Entity);
}
private void OnExamine(Entity<StationAiFaxComponent> ent, ref StationAiFaxExamineMessage args)
{
if (GetDocument(ent, args.Document) is not { } document)
return;
if (TryComp<PaperComponent>(document, out var paper))
{
paper.Mode = paper.EditingDisabled || paper.StampedBy.Count != 0
? PaperComponent.PaperAction.Read
: PaperComponent.PaperAction.Write;
_paper.UpdateUserInterface((document, paper));
}
_userInterface.OpenUi(document, PaperComponent.PaperUiKey.Key, args.Actor);
}
private void OnShred(Entity<StationAiFaxComponent> ent, ref StationAiFaxShredMessage args)
{
if (GetDocument(ent, args.Document) is { } document)
{
PredictedQueueDel(document);
}
}
protected EntityUid? GetDocument(Entity<StationAiFaxComponent> ent, NetEntity netEntity)
{
if (GetEntity(netEntity) is not { Valid: true } entity)
return null;
if (!_container.TryGetContainingContainer(entity, out var container))
return null;
if (container.Owner != ent.Owner)
return null;
return entity;
}
private void OnFaxPrinted(Entity<StationAiFaxComponent> ent, ref FaxPrintedEvent args)
{
var container = _container.EnsureContainer<Container>(ent, StationAiFaxComponent.Container);
if (!_container.Insert(args.Fax, container, force: true))
{
PredictedQueueDel(args.Fax);
}
}
}
@@ -0,0 +1,34 @@
using Content.Shared._DV.Fax;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared._DV.Silicons;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedStationAiFaxSystem))]
public sealed partial class StationAiFaxComponent : Component
{
public const string Container = "PaperLibrary";
}
[Serializable, NetSerializable]
public abstract partial class StationAiFaxFileMessage : BoundUserInterfaceMessage
{
public readonly NetEntity Document;
public StationAiFaxFileMessage(NetEntity document)
{
Document = document;
}
}
[Serializable, NetSerializable]
public sealed partial class StationAiFaxSendMessage(NetEntity document) : StationAiFaxFileMessage(document);
[Serializable, NetSerializable]
public sealed partial class StationAiFaxExamineMessage(NetEntity document) : StationAiFaxFileMessage(document);
[Serializable, NetSerializable]
public sealed partial class StationAiFaxDuplicateMessage(NetEntity document) : StationAiFaxFileMessage(document);
[Serializable, NetSerializable]
public sealed partial class StationAiFaxShredMessage(NetEntity document) : StationAiFaxFileMessage(document);
@@ -65,4 +65,10 @@ public sealed class TraitConditionContext
/// The <see cref="HumanoidCharacterProfile"/> of the player, if available.
/// </summary>
public HumanoidCharacterProfile? Profile { get; init; }
/// <summary>
/// The set of trait IDs currently selected by the player, if available.
/// Used by <see cref="TraitDependencyCondition"/> to check conflicts and requirements.
/// </summary>
public IReadOnlySet<ProtoId<TraitPrototype>>? SelectedTraits { get; init; }
}
@@ -0,0 +1,80 @@
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Traits.Conditions;
/// <summary>
/// Condition that checks trait conflicts and requirements.
/// - Conflicts: if any listed trait is selected, this condition fails.
/// - Requires: all listed traits must be selected for this condition to pass.
/// Tooltips are generated per-entry by the UI rather than via GetTooltip,
/// so GetTooltip returns empty to avoid duplication.
/// </summary>
public sealed partial class TraitDependencyCondition : BaseTraitCondition
{
/// <summary>
/// Traits that are mutually exclusive with this one.
/// If any of these are selected, this condition fails.
/// </summary>
[DataField]
public List<ProtoId<TraitPrototype>> Conflicts = new();
/// <summary>
/// Traits that must already be selected for this condition to pass.
/// All listed traits must be present.
/// </summary>
[DataField]
public List<ProtoId<TraitPrototype>> Requires = new();
protected override bool EvaluateImplementation(TraitConditionContext ctx)
{
if (ctx.SelectedTraits is { } selectedTraits)
{
foreach (var conflict in Conflicts)
{
if (selectedTraits.Contains(conflict))
return false;
}
}
if (Requires.Count > 0)
{
if (ctx.SelectedTraits is not { } selected)
return false;
foreach (var required in Requires)
{
if (!selected.Contains(required))
return false;
}
}
return true;
}
/// <summary>
/// Returns empty. the UI generates conflict/require tooltips directly via
/// <see cref="GetTooltips"/> to avoid duplicating lines across the two tooltip paths.
/// </summary>
public override string GetTooltip(IPrototypeManager proto, ILocalizationManager loc) => string.Empty;
/// <summary>
/// Returns individual tooltip lines for each conflict and requirement.
/// Used by the UI to build both the requirements preview and the failed-conditions tooltip.
/// </summary>
public IEnumerable<string> GetTooltips(IPrototypeManager proto, ILocalizationManager loc)
{
foreach (var conflict in Conflicts)
{
if (!proto.TryIndex(conflict, out var traitProto))
continue;
yield return loc.GetString("trait-condition-trait-conflict", ("trait", loc.GetString(traitProto.Name)));
}
foreach (var required in Requires)
{
if (!proto.TryIndex(required, out var traitProto))
continue;
yield return loc.GetString("trait-condition-trait-required", ("trait", loc.GetString(traitProto.Name)));
}
}
}
+200 -177
View File
@@ -1,181 +1,4 @@
Entries:
- author: snowywinters
changes:
- message: Station AI can now use traffic comms. Your secrets are no longer safe.
type: Add
- message: Station AI can now see silicon health, just like diagnostic HUDs.
type: Add
id: 1941
time: '2025-12-09T17:56:16.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4905
- author: Stxcking
changes:
- message: Syndicat ghost role description doesn't mention microbomb implant.
type: Fix
id: 1942
time: '2025-12-09T18:31:07.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4889
- author: Stxcking
changes:
- message: Added more festive headwear, an elf costume, and holiday red dress to
the AutoDrobe!
type: Add
id: 1943
time: '2025-12-10T08:05:33.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4909
- author: ShepardToTheStars
changes:
- message: Entropic Degen should no longer last forever.
type: Fix
id: 1944
time: '2025-12-10T08:12:42.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4931
- author: SirWarock
changes:
- message: Fixed Rollerbeds getting annihilated by mouse traps!
type: Fix
id: 1945
time: '2025-12-10T14:57:38.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4933
- author: verybigman311
changes:
- message: Mowed Astro-Snow can now be crafted and utilized
type: Add
id: 1946
time: '2025-12-10T14:59:27.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4921
- author: pootslap
changes:
- message: New caw sounds for harpies!
type: Add
- message: Harpy emotes are now gender specific.
type: Tweak
id: 1947
time: '2025-12-10T15:25:50.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4915
- author: verybigman311
changes:
- message: Saws are now a little more lethal
type: Tweak
id: 1948
time: '2025-12-10T21:59:39.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4886
- author: Eternally-Confused
changes:
- message: The Crime Assist PDA app has been updated to match Space Law.
type: Fix
id: 1949
time: '2025-12-11T14:58:57.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4937
- author: SirWarock
changes:
- message: Truncheons can now be selected instead of a firearm in Security Loadouts!
type: Tweak
id: 1950
time: '2025-12-11T22:05:22.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4838
- author: AeraAuling
changes:
- message: Adds purifying salt to allow for healing of metaphysical damage
type: Add
id: 1951
time: '2025-12-11T22:39:23.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4682
- author: Halo3moth
changes:
- message: Lawyers now spawn with a forensic scanner.
type: Add
- message: Minor detective goodies added to the lawdrobe.
type: Add
id: 1952
time: '2025-12-12T10:54:37.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4879
- author: IRSMsoso
changes:
- message: Harpy flight no longer causes silent errors
type: Fix
id: 1953
time: '2025-12-12T11:05:00.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4952
- author: EmberAstra
changes:
- message: forensic pads used to scan gloves now also show the fiber's ID.
type: Tweak
id: 1954
time: '2025-12-12T19:21:56.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4932
- author: TheShuEd, Spielern
changes:
- message: The Christmas Anomaly is back in rotation!
type: Add
id: 1955
time: '2025-12-12T22:23:47.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4957
- author: AirlineFood001
changes:
- message: Adjusted the armor values on the Advanced Plate Carrier to make it all
around stronger.
type: Tweak
id: 1956
time: '2025-12-12T22:40:34.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4942
- author: Velcroboy
changes:
- message: Reconfigurated the TEG tentacles
type: Tweak
id: 1957
time: '2025-12-13T19:18:53.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4965
- author: kotobdev
changes:
- message: The Redshirt trait has been added, which causes you to immediately die
instead of going crit.
type: Add
id: 1958
time: '2025-12-16T01:34:03.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4974
- author: Stxcking
changes:
- message: Courier, Boxer, Gladiator, and Prisoner have the option to choose glasses
in their loadout.
type: Tweak
- message: Surgeons get medical eyewear options in loadout.
type: Tweak
id: 1959
time: '2025-12-16T05:04:42.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4977
- author: Proxysseia
changes:
- message: replaced vestigial entity mentions with employee mentions. You can no
longer harass vent spiders.
type: Tweak
id: 1960
time: '2025-12-16T13:07:37.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4947
- author: MantasDab360
changes:
- message: Revenant's crayon is now infinite once again.
type: Fix
id: 1961
time: '2025-12-16T13:15:10.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4987
- author: Stxcking
changes:
- message: Corpsman's inventory on spawn has been cleaned up. Most medical gear
has been moved to the corpsman. locker.
type: Tweak
- message: Corpsman uniforms are now available in the SecDrobe.
type: Tweak
id: 1962
time: '2025-12-16T13:24:57.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4979
- author: marigauge
changes:
- message: IPCs now leave behind steel fibers when touching stuff.
type: Tweak
id: 1963
time: '2025-12-16T13:41:33.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4968
- author: marigauge
changes:
- message: Cybernetic eyes are no longer edible.
@@ -4394,4 +4217,204 @@
id: 2441
time: '2026-06-20T17:14:12.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6049
- author: Velcroboy
changes:
- message: 'The Hive: Epistemics now has a public thoroughfare through the center
where a new chapel and robotics lab are located. Also security is blue.'
type: Tweak
id: 2442
time: '2026-06-21T13:48:13.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6018
- author: Halo3moth
changes:
- message: Pitbulls now spawn from vents less often
type: Tweak
id: 2443
time: '2026-06-22T04:23:12.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6081
- author: wesk
changes:
- message: Fixed **8 O'Clock Ghosts** from being stereo, instead of mono. This will
no longer make the music play "loud" especially when the jukebox is behind a
wall.
type: Fix
id: 2444
time: '2026-06-22T08:46:38.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6084
- author: wesk
changes:
- message: The Traits UI now shows a visual indicator when you have selected the
max amount of traits in a category.
type: Tweak
- message: The Traits UI now indicates if you do not have enough points for a trait.
type: Tweak
- message: The Traits UI now indicates if a trait is conflicting with another trait,
or is requiring another trait in order to be used/equipped by your character.
type: Tweak
id: 2445
time: '2026-06-22T11:11:35.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6082
- author: Proxysseia
changes:
- message: Shields are now not allowed to be carried on code Green (again)
type: Tweak
id: 2446
time: '2026-06-22T11:36:51.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6070
- author: rebe83
changes:
- message: Slugs are made out of lead and do 100 structural.
type: Tweak
id: 2447
time: '2026-06-22T16:48:31.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6068
- author: A111Vojta
changes:
- message: Ported lanyards from Harmony
type: Add
id: 2448
time: '2026-06-22T18:05:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6035
- author: ShepardToTheStars
changes:
- message: Nuke and Cosmic Cult finale music and SFX will always play, even if you're
jetpacking around the station.
type: Tweak
- message: If a cultist converts a borg off-station, it will properly announce that
it is happening.
type: Fix
id: 2449
time: '2026-06-23T10:40:17.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5918
- author: wesk
changes:
- message: Fixed required traits from not being detected by the Trait System, causing
a false positive of traits not being met, even if you have them equipped already.
type: Fix
id: 2450
time: '2026-06-23T12:49:52.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6097
- author: rebe83
changes:
- message: Broken glass bottles can cut and slice things. Yay?
type: Tweak
id: 2451
time: '2026-06-23T15:27:08.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6092
- author: RoboBozu, SalDragonsNOA
changes:
- message: NanoTrasen corporate has decided to celebrate the increased productivity
of the Delta Sector with ice cream! Recipes for various frozen treats have been
added to Bartenders' handbooks to facilitate the celebration!
type: Add
id: 2452
time: '2026-06-20T15:02:14.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5943
- author: ShepardToTheStars
changes:
- message: Sleeper and NT sleeper agents no longer raise the alert level to blue
and no longer display a 'communications intercepted' message.
type: Tweak
id: 2453
time: '2026-06-23T22:34:48.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6066
- author: ShepardToTheStars
changes:
- message: Station-specific jobs in the job preferences can now be found under their
proper categories.
type: Tweak
- message: The station-specific jobs category will no longer appear on the crew
monitor.
type: Tweak
- message: The corpsman job will now properly appear as a member of security on
the crew monitor.
type: Fix
id: 2454
time: '2026-06-23T22:34:44.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6078
- author: Stxcking
changes:
- message: AAC tablet buttons have colors again!
type: Tweak
id: 2455
time: '2026-06-24T13:08:20.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5567
- author: ShepardToTheStars
changes:
- message: Stealth borg modules are now working as intended and no longer provide
infinite duration cloaking.
type: Fix
id: 2456
time: '2026-06-24T13:15:02.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6085
- author: ShepardToTheStars
changes:
- message: You can now change common radio channel colors to the colors of your
choice in the Radio tab in the Options menu!
type: Add
id: 2457
time: '2026-06-24T22:02:59.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6101
- author: ManuDemoen
changes:
- message: Fixed certain entities from not metabolizing alcohol
type: Fix
id: 2458
time: '2026-06-25T02:28:27.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6104
- author: Tmanzxd
changes:
- message: Removed the Surgeon time requirement for Chief Medical Officer, since
surgery is once again a week away.
type: Remove
id: 2459
time: '2026-06-25T13:10:38.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6112
- author: rebe83
changes:
- message: Washing machines now can fit larger items inside, such as felinids, rodentia,
or even a china lake. (China Lake not included)
type: Tweak
id: 2460
time: '2026-06-25T14:07:13.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6086
- author: sowelipililimute
changes:
- message: The station AI can now send and receive faxes.
type: Add
id: 2461
time: '2026-06-25T15:27:50.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6064
- author: ManuDemoen
changes:
- message: The nachos recipe has changed! Cut them from taco shells and then add
chili or cheese for variants.
type: Tweak
id: 2462
time: '2026-06-25T15:37:34.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6099
- author: wesk
changes:
- message: Synthetics can now use borg emotes!
type: Add
- message: Synthetics now has an option of being susceptible to Ion Storms.
type: Add
- message: Synthetics can now be purely mechanical! (cosmetical function only; you
spark when damaged.)
type: Add
id: 2463
time: '2026-06-25T16:17:27.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6090
- author: sowelipililimute
changes:
- message: Suit sensors now report their location, even while in space
type: Add
- message: Crew monitoring computers now have a radar UI when you're off-station
type: Add
- message: Due to suit sensors now working in space, salvage no longer needs deathrattles
for recovery, hence, they have been removed.
type: Remove
id: 2464
time: '2026-06-25T17:59:50.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6054
Order: 1
@@ -62,9 +62,11 @@ cosmiccult-summary-crewminor = The Monument did not reach full power.
cosmiccult-summary-crewmajor = All cosmic cultists were eliminated.
cosmiccult-summary-crewcomplete = All cosmic cultists were deconverted!
cosmiccult-elimination-shuttle-call = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle has been automatically called to the station for decontamination and debriefing procedures. ETA: {$time} {$units}. Please note, if the psychological impact of the anomaly is negligible, you may recall the shuttle to extend the shift.
cosmiccult-elimination-announcement = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle is already inbound. Return to CentComm safely for decontamination and debriefing procedures.
cosmiccult-elimination-shuttle-call = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle has been automatically called to the station for decontamination and debriefing procedures. ETA: {$time} {$units}.
cosmiccult-elimination-announcement = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle is already inbound. Return to Midpoint safely for decontamination and debriefing procedures.
cosmiccult-progress-fail-shuttle-call = Due to sustained Λ-CDM activity on you station, an emergency shuttle has been automatically called so that the station may be decontaminated as soon as possible. We thank you for your cooperation.
cosmiccult-progress-fail-announcement = Due to sustained Λ-CDM activity on you station, an emergency shuttle is already inbound. Please ensure as much of the station is ready and accessible for the decontamination team. We thank you for your cooperation.
## BRIEFINGS
@@ -0,0 +1,43 @@
ui-options-tab-radio = Radio
ui-options-tab-radio-description = Customize common in-game radio channel colors.
# Upstream Departments
ui-options-radio-header-binary = Binary
ui-options-radio-header-cargo = Logistics
ui-options-radio-header-command = Command
ui-options-radio-header-common = Common
ui-options-radio-header-engineering = Engineering
ui-options-radio-header-medical = Medical
ui-options-radio-header-science = Epistemics
ui-options-radio-header-security = Security
ui-options-radio-header-service = Service
ui-options-radio-example-binary = [Binary] Freefall (AI-752) states, "Someone is breaking into my core."
ui-options-radio-example-cargo = [Logistics] Seren says, "Salvage, are you alive?"
ui-options-radio-example-command = [Command] Alexis asks, "Uh... Where is the nuke disk?"
ui-options-radio-example-common = [Common] Maya says, "I love slacking off."
ui-options-radio-example-engineering = [Engineering] Abigail asks, "Has anyone checked on the Tesla lately?"
ui-options-radio-example-medical = [Medical] Aurora asks, "Did anyone set up cryo?"
ui-options-radio-example-science = [Epistemics] Isaiah says, "That's the next thing we'll research."
ui-options-radio-example-security = [Security] Simon says, "Littering? That's ten minutes in the brig."
ui-options-radio-example-service = [Service] Preston says, "I gave medical almost 2000u of omnizine."
ui-options-radio-reset-binary = Reset Binary channel color
ui-options-radio-reset-cargo = Reset Cargo channel color
ui-options-radio-reset-command = Reset Command channel color
ui-options-radio-reset-common = Reset Common channel color
ui-options-radio-reset-engineering = Reset Engineering channel color
ui-options-radio-reset-medical = Reset Medical channel color
ui-options-radio-reset-science = Reset Science channel color
ui-options-radio-reset-security = Reset Security channel color
ui-options-radio-reset-service = Reset Service channel color
# DeltaV-Specific Departments
ui-options-radio-header-justice = Justice
ui-options-radio-header-prison = Prison
ui-options-radio-example-justice = [Justice] Mila exclaims, "My client did no such thing!"
ui-options-radio-example-prison = [Prison] Pepper asks, "Is anyone getting that scarf for me?"
ui-options-radio-reset-justice = Reset Justice channel color
ui-options-radio-reset-prison = Reset Prison channel color
+8
View File
@@ -0,0 +1,8 @@
fax-machine-ui-no-faxes =
You have no documents.
Add some, and they'll show up here.
fax-machine-ui-add-blank-file = Add blank document
fax-machine-ui-upload-file = Upload document…
fax-machine-ui-page-window = Send Document
fax-machine-ui-sending = Sending:
@@ -1,3 +1,4 @@
department-Epistemics-description = Research artifacts and anomalies to invent new equipment for the station, while trying to keep glimmer tame.
department-Logistics-description = Complete bounties, earn Spesos and deliver useful supplies to the crew.
department-Justice-description = Uphold justice on the station.
department-Incarcerated-description = Serving your sentence for past mistakes.
@@ -1,3 +1,4 @@
department-Epistemics = Epistemics
department-Logistics = Logistics
department-Justice = Justice
department-Incarcerated = Incarcerated
@@ -2,3 +2,4 @@ pager-error-different-networks = { THE($sender) } is on a different network from
pager-connection-added = { THE($receiver) } will now receive pages from { THE($sender) }.
pager-connection-removed = { THE($receiver) } will no longer receive pages from { THE($sender) }.
pager-notification = Page from { $sender }
pager-self-notification = Page received
+10 -10
View File
@@ -81,22 +81,22 @@ phrase-number7 = 7
phrase-number8 = 8
phrase-number9 = 9
phrase-number0 = 0
phrase-ten = ten
phrase-hundred = hundred
phrase-thousand = thousand
phrase-million = million
phrase-billion = billion
phrase-stack = stack
phrase-hour = hour
phrase-minute = minute
phrase-second = second
phrase-ten = Ten
phrase-hundred = Hundred
phrase-thousand = Thousand
phrase-million = Million
phrase-billion = Billion
phrase-stack = Stack
phrase-hour = Hour
phrase-minute = Minute
phrase-second = Second
phrase-i-am = I am
phrase-you-are = You are
phrase-mine = Mine
phrase-yours = Yours
phrase-on = On
phrase-off = Off
phrase-day = day
phrase-day = Day
phrase-which = Which
phrase-not = Not
phrase-call = Call
@@ -7,17 +7,17 @@ phrase-location-shuttle = Shuttle
phrase-location-maintenance = Maintenance
# relative
phrase-location-near = near
phrase-location-far = far
phrase-location-inside = inside
phrase-location-outside = outside
phrase-location-near = Near
phrase-location-far = Far
phrase-location-inside = Inside
phrase-location-outside = Outside
## compass
phrase-location-north = north
phrase-location-east = east
phrase-location-south = south
phrase-location-west = west
phrase-location-northeast = northeast
phrase-location-southeast = southeast
phrase-location-southwest = southwest
phrase-location-northwest = northwest
phrase-location-north = North
phrase-location-east = East
phrase-location-south = South
phrase-location-west = West
phrase-location-northeast = Northeast
phrase-location-southeast = Southeast
phrase-location-southwest = Southwest
phrase-location-northwest = Northwest
+43 -43
View File
@@ -1,43 +1,43 @@
phrase-species-dog = dog
phrase-species-cat = cat
phrase-species-opossum = opossum
phrase-species-lizard = lizard
phrase-species-bat = sloth
phrase-species-slime = slime
phrase-species-mouse = mouse
phrase-species-mothroach = mothroach
phrase-species-cockroach = cockroach
phrase-species-spider = spider
phrase-species-bird = bird
phrase-species-parrot = parrot
phrase-species-snake = snake
phrase-species-pig = pig
phrase-species-monkey = monkey
phrase-species-kobold = kobold
phrase-species-ferret = ferret
phrase-species-carp = carp
phrase-species-xeno = xeno
phrase-species-hamster = hamster
phrase-species-ifrit = ifrit
phrase-species-raccoon = raccoon
phrase-species-rouny = rouny
phrase-species-fox = fox
phrase-species-crab = crab
phrase-species-kangaroo = kangaroo
phrase-species-cow = cow
phrase-species-duck = duck
phrase-species-bear = bear
phrase-species-penguin = penguin
phrase-species-goat = goat
phrase-species-person = person
phrase-species-animal = animal
phrase-species-corpse = corpse
phrase-species-robot = robot
phrase-species-alien = alien
phrase-species-monster = monster
phrase-species-ghost = ghost
phrase-species-pest = pest
phrase-species-insect = insect
phrase-species-pet = pet
phrase-species-chicken = chicken
phrase-species-fish = fish
phrase-species-dog = Dog
phrase-species-cat = Cat
phrase-species-opossum = Opossum
phrase-species-lizard = Lizard
phrase-species-bat = Sloth
phrase-species-slime = Slime
phrase-species-mouse = Mouse
phrase-species-mothroach = Mothroach
phrase-species-cockroach = Cockroach
phrase-species-spider = Spider
phrase-species-bird = Bird
phrase-species-parrot = Parrot
phrase-species-snake = Snake
phrase-species-pig = Pig
phrase-species-monkey = Monkey
phrase-species-kobold = Kobold
phrase-species-ferret = Ferret
phrase-species-carp = Carp
phrase-species-xeno = Xeno
phrase-species-hamster = Hamster
phrase-species-ifrit = Ifrit
phrase-species-raccoon = Raccoon
phrase-species-rouny = Rouny
phrase-species-fox = Fox
phrase-species-crab = Crab
phrase-species-kangaroo = Kangaroo
phrase-species-cow = Cow
phrase-species-duck = Duck
phrase-species-bear = Bear
phrase-species-penguin = Penguin
phrase-species-goat = Goat
phrase-species-person = Person
phrase-species-animal = Animal
phrase-species-corpse = Corpse
phrase-species-robot = Robot
phrase-species-alien = Alien
phrase-species-monster = Monster
phrase-species-ghost = Ghost
phrase-species-pest = Pest
phrase-species-insect = Insect
phrase-species-pet = Pet
phrase-species-chicken = Chicken
phrase-species-fish = Fish
+225 -225
View File
@@ -1,249 +1,249 @@
phrase-pen = pen
phrase-paper = paper
phrase-wrench = wrench
phrase-crowbar = crowbar
phrase-screwdriver = screwdriver
phrase-wirecutter = wirecutter
phrase-welding-tool = welding tool
phrase-welding-mask = welding mask
phrase-toolbox = toolbox
phrase-beaker = beaker
phrase-radio = radio
phrase-uniform = uniform
phrase-suit-sensors = suit sensors
phrase-internals = internals
phrase-gas-tank = gas tank
phrase-multitool = multitool
phrase-lathe = lathe
phrase-computer = computer
phrase-door = door
phrase-firelock = firelock
phrase-space = space
phrase-tool = tool
phrase-weapon = weapon
phrase-armor = armor
phrase-brain = brain
phrase-drink = drink
phrase-food = food
phrase-water = water
phrase-signature = signature
phrase-belt = belt
phrase-hardsuit = hardsuit
phrase-light = light
phrase-bag = bag
phrase-steel = steel
phrase-plastic = plastic
phrase-plasteel = plasteel
phrase-plasma = plasma
phrase-uranium = uranium
phrase-bananium = bananium
phrase-wood = wood
phrase-cloth = cloth
phrase-cardboard = cardboard
phrase-gold = gold
phrase-glass = glass
phrase-silver = silver
phrase-bluespace = bluespace
phrase-access = access
phrase-encryption-key = encryption key
phrase-pen = Pen
phrase-paper = Paper
phrase-wrench = Wrench
phrase-crowbar = Crowbar
phrase-screwdriver = Screwdriver
phrase-wirecutter = Wirecutter
phrase-welding-tool = Welding tool
phrase-welding-mask = Welding mask
phrase-toolbox = Toolbox
phrase-beaker = Beaker
phrase-radio = Radio
phrase-uniform = Uniform
phrase-suit-sensors = Suit sensors
phrase-internals = Internals
phrase-gas-tank = Gas tank
phrase-multitool = Multitool
phrase-lathe = Lathe
phrase-computer = Computer
phrase-door = Door
phrase-firelock = Firelock
phrase-space = Space
phrase-tool = Tool
phrase-weapon = Weapon
phrase-armor = Armor
phrase-brain = Brain
phrase-drink = Drink
phrase-food = Food
phrase-water = Water
phrase-signature = Signature
phrase-belt = Belt
phrase-hardsuit = Hardsuit
phrase-light = Light
phrase-bag = Bag
phrase-steel = Steel
phrase-plastic = Plastic
phrase-plasteel = Plasteel
phrase-plasma = Plasma
phrase-uranium = Uranium
phrase-bananium = Bananium
phrase-wood = Wood
phrase-cloth = Cloth
phrase-cardboard = Cardboard
phrase-gold = Gold
phrase-glass = Glass
phrase-silver = Silver
phrase-bluespace = Bluespace
phrase-access = Access
phrase-encryption-key = Encryption key
phrase-id-card = ID card
phrase-pda = pda
phrase-stamp = stamp
phrase-implant = implant
phrase-pda = Pda
phrase-stamp = Stamp
phrase-implant = Implant
phrase-ame = AME
phrase-apc = APC
phrase-canister = canister
phrase-compressed-matter = compressed matter
phrase-frezon = frezon
phrase-gravity = gravity
phrase-canister = Canister
phrase-compressed-matter = Compressed matter
phrase-frezon = Frezon
phrase-gravity = Gravity
phrase-hvwire = HV cable
phrase-lvwire = LV cable
phrase-mvwire = MV cable
phrase-network-configurator = network configurator
phrase-nitrogen = nitrogen
phrase-oxygen = oxygen
phrase-network-configurator = Network configurator
phrase-nitrogen = Nitrogen
phrase-oxygen = Oxygen
phrase-rcd = RCD
phrase-rtg = RTG
phrase-scrubber = scrubber
phrase-singulo = singularity
phrase-scrubber = Scrubber
phrase-singulo = Singularity
phrase-smes = SMES
phrase-substation = substation
phrase-tray-scanner = t-ray scanner
phrase-substation = Substation
phrase-tray-scanner = T-ray scanner
phrase-teg = TEG
phrase-tesla = tesla
phrase-tritium = tritium
phrase-magboots = magboots
phrase-holofan = holofan
phrase-anomaly = anomaly
phrase-tesla = Tesla
phrase-tritium = Tritium
phrase-magboots = Magboots
phrase-holofan = Holofan
phrase-anomaly = Anomaly
phrase-ape = A.P.E.
phrase-anomaly-vessel = anomaly vessel
phrase-artifact = artifact
phrase-artifact-container = artifact container
phrase-robotics = robotics
phrase-circuit-board = circuit board
phrase-research = research
phrase-anomaly-vessel = Anomaly vessel
phrase-artifact = Artifact
phrase-artifact-container = Artifact container
phrase-robotics = Robotics
phrase-circuit-board = Circuit board
phrase-research = Research
phrase-research-console = R&D computer
phrase-oracle = Oracle
phrase-sophie = Sophie
phrase-glimmer = glimmer
phrase-psionic = psionic
phrase-lotophagol-oil = lotophagol oil
phrase-mindbreak-phase = mindbreak
phrase-protolathe = protolathe
phrase-circuit-imprinter = circuit imprinter
phrase-bounty = bounty
phrase-cargo-shuttle = cargo shuttle
phrase-crate = crate
phrase-mail = mail
phrase-order = order
phrase-ore-processor = ore processor
phrase-request = request
phrase-spesos = spesos
phrase-restock = restock
phrase-fulton = fulton
phrase-jetpack = jetpack
phrase-crusher = crusher
phrase-pka = proto-kinetic accelerator
phrase-pickaxe = pickaxe
phrase-drill = drill
phrase-ore-bag = ore bag
phrase-salv-magnet = salvage magnet
phrase-expedition = expedition
phrase-topical = topical
phrase-topical-brute-pack = brute pack
phrase-topical-gauze = gauze
phrase-topical-bloodpack = bloodpack
phrase-defibrillator = defibrillator
phrase-cryopod = cryopod
phrase-stasis-bed = stasis bed
phrase-first-aid-kit = first-aid kit
phrase-clone = clone
phrase-cloning-pod = cloning pod
phrase-biomass = biomass
phrase-syringe = syringe
phrase-pill = pill
phrase-alive = alive
phrase-crit = critical
phrase-dead = dead
phrase-healthy = healthy
phrase-handcuffs = handcuffs
phrase-stun-baton = stun baton
phrase-disabler = disabler
phrase-flash = flash
phrase-flashbang = flashbang
phrase-arrest = arrest
phrase-warrant = warrant
phrase-wanted = wanted
phrase-search = search
phrase-detain = detain
phrase-suspect = suspect
phrase-forensic-scanner = forensic scanner
phrase-forensic-pad = forensic pad
phrase-glimmer = Glimmer
phrase-psionic = Psionic
phrase-lotophagol-oil = Lotophagol oil
phrase-mindbreak-phase = Mindbreak
phrase-protolathe = Protolathe
phrase-circuit-imprinter = Circuit imprinter
phrase-bounty = Bounty
phrase-cargo-shuttle = Cargo shuttle
phrase-crate = Crate
phrase-mail = Mail
phrase-order = Order
phrase-ore-processor = Ore processor
phrase-request = Request
phrase-spesos = Spesos
phrase-restock = Restock
phrase-fulton = Fulton
phrase-jetpack = Jetpack
phrase-crusher = Crusher
phrase-pka = Proto-kinetic accelerator
phrase-pickaxe = Pickaxe
phrase-drill = Drill
phrase-ore-bag = Ore bag
phrase-salv-magnet = Salvage magnet
phrase-expedition = Expedition
phrase-topical = Topical
phrase-topical-brute-pack = Brute pack
phrase-topical-gauze = Gauze
phrase-topical-bloodpack = Bloodpack
phrase-defibrillator = Defibrillator
phrase-cryopod = Cryopod
phrase-stasis-bed = Stasis bed
phrase-first-aid-kit = First-aid kit
phrase-clone = Clone
phrase-cloning-pod = Cloning pod
phrase-biomass = Biomass
phrase-syringe = Syringe
phrase-pill = Pill
phrase-alive = Alive
phrase-crit = Critical
phrase-dead = Dead
phrase-healthy = Healthy
phrase-handcuffs = Handcuffs
phrase-stun-baton = Stun baton
phrase-disabler = Disabler
phrase-flash = Flash
phrase-flashbang = Flashbang
phrase-arrest = Arrest
phrase-warrant = Warrant
phrase-wanted = Wanted
phrase-search = Search
phrase-detain = Detain
phrase-suspect = Suspect
phrase-forensic-scanner = Forensic scanner
phrase-forensic-pad = Forensic pad
phrase-log-probe = LogProbe
phrase-meat = meat
phrase-flour = flour
phrase-rice = rice
phrase-dough = dough
phrase-bread = bread
phrase-milk = milk
phrase-egg = egg
phrase-wheat = wheat
phrase-vegetable = vegetable
phrase-fruit = fruit
phrase-sugar = sugar
phrase-alcohol = alcohol
phrase-non-alcoholic = non-alcoholic
phrase-juice = juice
phrase-mop = mop
phrase-bucket = bucket
phrase-space-cleaner = space cleaner
phrase-soap = soap
phrase-trash = trash
phrase-stage = stage
phrase-seed = seed
phrase-plant = plant
phrase-mutagen = mutagen
phrase-robust = robust harvest
phrase-weed = weed
phrase-knife = knife
phrase-witness = witness
phrase-evidence = evidence
phrase-case = case
phrase-sue = sue
phrase-courtroom-timer = courtroom timer
phrase-bailiff = bailiff
phrase-sentence = sentence
phrase-defendant = defendant
phrase-plaintiff = plaintiff
phrase-victim = victim
phrase-defense = defense
phrase-prosecution = prosecution
phrase-statement = statement
phrase-gavel = gavel
phrase-charge = charge
phrase-guilty = guilty
phrase-not-guilty = not guilty
phrase-meat = Meat
phrase-flour = Flour
phrase-rice = Rice
phrase-dough = Dough
phrase-bread = Bread
phrase-milk = Milk
phrase-egg = Egg
phrase-wheat = Wheat
phrase-vegetable = Vegetable
phrase-fruit = Fruit
phrase-sugar = Sugar
phrase-alcohol = Alcohol
phrase-non-alcoholic = Non-alcoholic
phrase-juice = Juice
phrase-mop = Mop
phrase-bucket = Bucket
phrase-space-cleaner = Space cleaner
phrase-soap = Soap
phrase-trash = Trash
phrase-stage = Stage
phrase-seed = Seed
phrase-plant = Plant
phrase-mutagen = Mutagen
phrase-robust = Robust harvest
phrase-weed = Weed
phrase-knife = Knife
phrase-witness = Witness
phrase-evidence = Evidence
phrase-case = Case
phrase-sue = Sue
phrase-courtroom-timer = Courtroom timer
phrase-bailiff = Bailiff
phrase-sentence = Sentence
phrase-defendant = Defendant
phrase-plaintiff = Plaintiff
phrase-victim = Victim
phrase-defense = Defense
phrase-prosecution = Prosecution
phrase-statement = Statement
phrase-gavel = Gavel
phrase-charge = Charge
phrase-guilty = Guilty
phrase-not-guilty = Not guilty
phrase-objection = Objection
phrase-request-time = I need more time
phrase-your-honor = Your Honor
phrase-microwave = microwave
phrase-grill = grill
phrase-iron = iron
phrase-coal = coal
phrase-quartz = quartz
phrase-salt = salt
phrase-artifact-fragment = artifact fragment
phrase-bluespace-crystal = bluespace
phrase-diamond = diamond
phrase-chemicals = chemicals
phrase-box = box
phrase-locker = locker
phrase-machine = machine
phrase-blood = blood
phrase-nukedisk = nuclear disk
phrase-pinpointer = pinpointer
phrase-law = law
phrase-chassis = chassis
phrase-engi-on = on
phrase-engi-off = off
phrase-gas-analyzer = gas analyzer
phrase-planetside = planetside
phrase-holopad = holopad
phrase-upgrade = upgrade
phrase-gloves = gloves
phrase-shoes = shoes
phrase-limbs = limbs
phrase-release = release
phrase-parole = parole
phrase-microwave = Microwave
phrase-grill = Grill
phrase-iron = Iron
phrase-coal = Coal
phrase-quartz = Quartz
phrase-salt = Salt
phrase-artifact-fragment = Artifact fragment
phrase-bluespace-crystal = Bluespace
phrase-diamond = Diamond
phrase-chemicals = Chemicals
phrase-box = Box
phrase-locker = Locker
phrase-machine = Machine
phrase-blood = Blood
phrase-nukedisk = Nuclear disk
phrase-pinpointer = Pinpointer
phrase-law = Law
phrase-chassis = Chassis
phrase-engi-on = On
phrase-engi-off = Off
phrase-gas-analyzer = Gas analyzer
phrase-planetside = Planetside
phrase-holopad = Holopad
phrase-upgrade = Upgrade
phrase-gloves = Gloves
phrase-shoes = Shoes
phrase-limbs = Limbs
phrase-release = Release
phrase-parole = Parole
# colors
phrase-color-red = red
phrase-color-orange = orange
phrase-color-yellow = yellow
phrase-color-green = green
phrase-color-blue = blue
phrase-color-purple = purple
phrase-color-pink = pink
phrase-color-black = black
phrase-color-white = white
phrase-color-bright = bright
phrase-color-light = light
phrase-color-dark = dark
phrase-color-rainbow = rainbow
phrase-color-brown = brown
phrase-color-gray = gray
phrase-color-red = Red
phrase-color-orange = Orange
phrase-color-yellow = Yellow
phrase-color-green = Green
phrase-color-blue = Blue
phrase-color-purple = Purple
phrase-color-pink = Pink
phrase-color-black = Black
phrase-color-white = White
phrase-color-bright = Bright
phrase-color-light = Light
phrase-color-dark = Dark
phrase-color-rainbow = Rainbow
phrase-color-brown = Brown
phrase-color-gray = Gray
# flavors
phrase-sweet = sweet
phrase-sour = sour
phrase-spicy = spicy
phrase-savory = savory
phrase-bitter = bitter
phrase-sweet = Sweet
phrase-sour = Sour
phrase-spicy = Spicy
phrase-savory = Savory
phrase-bitter = Bitter
# plants
phrase-tomato = tomato
phrase-potato = potato
phrase-onion = onion
phrase-aloe = aloe
phrase-poppy = poppy
phrase-galaxythistle = galaxythistle
phrase-tomato = Tomato
phrase-potato = Potato
phrase-onion = Onion
phrase-aloe = Aloe
phrase-poppy = Poppy
phrase-galaxythistle = Galaxythistle
+54 -54
View File
@@ -1,57 +1,57 @@
phrase-hazard-space = spaced
phrase-hazard-low-presure = low pressure
phrase-hazard-high-presure = high pressure
phrase-hazard-heat = overheating
phrase-hazard-cold = cold
phrase-hazard-gas-leak = gas leak
phrase-hazard-explosion = explosion
phrase-hazard-radiation = radiation
phrase-hazard-structure-damage = structural damage
phrase-hazard-chemical-spill = chemical spill
phrase-hazard-shock = shock hazard
phrase-hazard = hazard
phrase-hazard-meteors = meteor
phrase-hazard-poison = poison
phrase-hazard-fire = fire
phrase-hazard-power-outage = power outage
phrase-hazard-low = low power
phrase-hazard-infection = infection
phrase-hazard-viral-threat = viral threat
phrase-hazard-broken-equipment = broken equipment
phrase-hostile-enemy = enemy
phrase-hostile = hostile
phrase-hostile-attacker = attacker
phrase-hostile-lifeform = dangerous lifeform
phrase-hostile-entity = entity
phrase-hostile-bomb = bomb
phrase-hostile-criminal = criminal
phrase-hostile-intruder = intruder
phrase-hostile-infiltrator = infiltrator
phrase-hostile-fugitive = fugitive
phrase-hostile-weapon = weapon
phrase-hostile-gun = gun
phrase-safety-safe = safe
phrase-safety-in-danger = in danger
phrase-safety-injured = injured
phrase-safety-dying = dying
phrase-safety-critical = critical
phrase-safety-bleeding = bleeding
phrase-safety-dead = dead
phrase-safety-alive = alive
phrase-safety-trustworthy = trustworthy
phrase-safety-escape = escape
phrase-safety-leaving = leaving
phrase-safety-need = need
phrase-safety-rescue = rescue
phrase-safety-dangerous = dangerous
phrase-safety-hazardous = hazardous
phrase-hazard-space = Spaced
phrase-hazard-low-presure = Low pressure
phrase-hazard-high-presure = High pressure
phrase-hazard-heat = Overheating
phrase-hazard-cold = Cold
phrase-hazard-gas-leak = Gas leak
phrase-hazard-explosion = Explosion
phrase-hazard-radiation = Radiation
phrase-hazard-structure-damage = Structural damage
phrase-hazard-chemical-spill = Chemical spill
phrase-hazard-shock = Shock hazard
phrase-hazard = Hazard
phrase-hazard-meteors = Meteor
phrase-hazard-poison = Poison
phrase-hazard-fire = Fire
phrase-hazard-power-outage = Power outage
phrase-hazard-low = Low power
phrase-hazard-infection = Infection
phrase-hazard-viral-threat = Viral threat
phrase-hazard-broken-equipment = Broken equipment
phrase-hostile-enemy = Enemy
phrase-hostile = Hostile
phrase-hostile-attacker = Attacker
phrase-hostile-lifeform = Dangerous lifeform
phrase-hostile-entity = Entity
phrase-hostile-bomb = Bomb
phrase-hostile-criminal = Criminal
phrase-hostile-intruder = Intruder
phrase-hostile-infiltrator = Infiltrator
phrase-hostile-fugitive = Fugitive
phrase-hostile-weapon = Weapon
phrase-hostile-gun = Gun
phrase-safety-safe = Safe
phrase-safety-in-danger = In danger
phrase-safety-injured = Injured
phrase-safety-dying = Dying
phrase-safety-critical = Critical
phrase-safety-bleeding = Bleeding
phrase-safety-dead = Dead
phrase-safety-alive = Alive
phrase-safety-trustworthy = Trustworthy
phrase-safety-escape = Escape
phrase-safety-leaving = Leaving
phrase-safety-need = Need
phrase-safety-rescue = Rescue
phrase-safety-dangerous = Dangerous
phrase-safety-hazardous = Hazardous
phrase-safety-here-to-help = Here to help
phrase-safety-how-to-help = How can I help?
phrase-safety-evac = Let's evacuate
phrase-safety-not = not
phrase-safety-lost = lost
phrase-hazard-hot = hot
phrase-hazard-freezing = freezing
phrase-hazard-burning = burning
phrase-hazard-bad-temperature = bad temperature
phrase-hazard-darkness = darkness
phrase-safety-not = Not
phrase-safety-lost = Lost
phrase-hazard-hot = Hot
phrase-hazard-freezing = Freezing
phrase-hazard-burning = Burning
phrase-hazard-bad-temperature = Bad temperature
phrase-hazard-darkness = Darkness
@@ -1,3 +1,5 @@
id-card-access-level-emergency-command = Emergency Command
id-card-access-level-orders = Orders
id-card-access-level-mantis = Psionic Mantis
id-card-access-level-chief-justice = Chief Justice
@@ -32,6 +32,11 @@ trait-conditions-tooltip = [bold]Requirements:[/bold]
trait-conditions-not-met-tooltip = Requirements not met:
{$requirements}
trait-category-full-tooltip = You have reached the maximum amount of traits that you can have in this category.
trait-insufficient-points-tooltip = You do not have enough points to equip this trait.
trait-condition-trait-conflict = You must not have the [color=yellow]{ $trait }[/color] trait.
trait-condition-trait-required = You must have the [color=yellow]{ $trait }[/color] trait.
## Composite conditions
trait-condition-any-of = Any of the following must be true:
• {$requirements}
@@ -49,8 +54,8 @@ trait-condition-department-is = You must be in the [color={$color}]{$department}
trait-condition-department-not = You must not be in the [color={$color}]{$department}[/color] department.
## HasComp borg conditions
trait-condition-borg-not = You must not be a [color=yellow]borg[/color].
trait-condition-borg-not = You must not be a [color=yellow]Silicon[/color].
# Antag conditions
## Antag conditions
trait-condition-antag-is = Must be eligible for [color=red]{$antag}[/color] antag role.
trait-condition-antag-not = Must not be eligible for [color=red]{$antag}[/color] antag role.
@@ -73,3 +73,9 @@ trait-slavic-desc = You kome from ze East, and speak vith strong Slavic accent!
trait-no-paradox-clone-name = No Paradox Clone
trait-no-paradox-clone-desc = For whatever reason, the universe will only ever have one of you. You won't be able to have a paradox clone.
trait-ion-storm-susceptible-name = Susceptible to Ion Storms
trait-ion-storm-susceptible-desc = Your synthetic body lacks protection against ion storms. You have a high chance of being shocked in an event of an ion storm.
trait-mechanical-synthetic-name = Mechanical Synthetic
trait-mechanical-synthetic-desc = Your synthetic body is fully mechanical rather than biomechanical.
@@ -0,0 +1,2 @@
zombie-no-more-threat-announcement-shuttle-call = Long-range sensors indicate that you station has dealt with a biohazardous threat involving Romerol. Station decontamination required. The emergency shuttle has been called. ETA: {$time} {$units}.
zombie-no-more-threat-announcement = Long-range sensors indicate that you station has dealt with a biohazardous threat involving Romerol. Station decontamination required. The emergency shuttle is already en route.
@@ -0,0 +1,6 @@
comp-lanyard-has-lanyard = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } wearing a lanyard, it reads:
comp-lanyard-has-lanyard-blank = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } wearing a lanyard, but it's blank.
comp-lanyard-has-lanyard-cant-read = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } wearing a lanyard, but you can't read it from this distance.
# For listing stamps on all labels, not just lanyards
comp-label-examine-detail-stamped-by = The label has been stamped by: {$stamps}.
comp-lanyard-examine-detail-stamped-by = The lanyard has been stamped by: {$stamps}.
+1 -1
View File
@@ -1,2 +1,2 @@
nuke-ops-no-more-threat-announcement-shuttle-call = Long-range sensors indicate the nuclear threat has been eliminated. The emergency shuttle has been called. ETA: {$time} {$units}. You may recall the shuttle to extend the shift.
nuke-ops-no-more-threat-announcement-shuttle-call = Long-range sensors indicate the nuclear threat has been eliminated. The emergency shuttle has been called. ETA: {$time} {$units}.
nuke-ops-no-more-threat-announcement = Long-range sensors indicate the nuclear threat has been eliminated. The emergency shuttle is already en route.

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