mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 23:57:29 +00:00
Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ad3187963 | ||
|
|
932dd5e429 | ||
|
|
3001253e43 | ||
|
|
d3b13a6c83 | ||
|
|
2ab0163598 | ||
|
|
6cdeb3d9cf | ||
|
|
d7543bd24a | ||
|
|
f7f800c439 | ||
|
|
b6ed84325f | ||
|
|
61aee79dc2 | ||
|
|
55fe6026ea | ||
|
|
6dfa12a509 | ||
|
|
485fd14f05 | ||
|
|
031b37ffcb | ||
|
|
06365bdbf0 | ||
|
|
7f18c5f346 | ||
|
|
d407652eb0 | ||
|
|
9108e2c9b5 | ||
|
|
c7ef614f3d | ||
|
|
fa735f7669 | ||
|
|
6de6421636 | ||
|
|
3cb24c8b68 | ||
|
|
9fad412075 | ||
|
|
fbc3104836 | ||
|
|
e231d9d67c | ||
|
|
468d929c4b | ||
|
|
2f1aaff32c | ||
|
|
4f49ccde68 | ||
|
|
cb3b74e49e | ||
|
|
7d15a39a77 | ||
|
|
3ac816fee1 | ||
|
|
b909d9a2e5 | ||
|
|
7547752159 | ||
|
|
9b7819288e | ||
|
|
424a4569d8 | ||
|
|
12827c0e98 | ||
|
|
d1e61ba0a2 | ||
|
|
652e52dbca | ||
|
|
e7fe4865de | ||
|
|
84b600a39f | ||
|
|
31201c44ba | ||
|
|
4f9f93eb87 | ||
|
|
f8a4d823b9 | ||
|
|
c61d2c6a58 | ||
|
|
e25181e1bc | ||
|
|
7c052442a8 | ||
|
|
19b0de35dd | ||
|
|
1c66c1fe2e | ||
|
|
ef46675a2f | ||
|
|
c0b6929c9a | ||
|
|
c7aa50cf85 | ||
|
|
c69fb99600 | ||
|
|
3ca37d9c2b |
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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`
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -588,15 +589,6 @@ public sealed class FaxSystem : EntitySystem
|
||||
return;
|
||||
|
||||
var faxName = Loc.GetString("fax-machine-popup-source-unknown");
|
||||
|
||||
// BEGIN DeltaV - Refresh Fax IDs before recieving
|
||||
// This will seem odd that we're doing the same if statement twice BUT if its not in the known fax machine list, the
|
||||
// sending fax might not have been online when the current fax machine was refreshed/init'd. So, if its already known,
|
||||
// we don't need to refresh. But if its not known, let's attempt to refresh first, then check the fromAddress in the KnownFaxes.
|
||||
if (fromAddress != null && !component.KnownFaxes.TryGetValue(fromAddress, out var _))
|
||||
Refresh(uid, component);
|
||||
// END DeltaV
|
||||
|
||||
if (fromAddress != null && component.KnownFaxes.TryGetValue(fromAddress, out var fax)) // If message received from unknown fax address
|
||||
faxName = fax;
|
||||
|
||||
@@ -644,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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chat;
|
||||
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,15 +16,22 @@ 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!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
private EmoteSoundsPrototype? _syntheticEmoteSounds; // delta-v - proper synthe emote implementation
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SynthComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<SynthComponent, EmoteEvent>(OnEmote); // delta-v - proper synthe emote implementation
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, SynthComponent component, ComponentStartup args)
|
||||
@@ -38,5 +49,19 @@ public sealed class SynthSystem : EntitySystem
|
||||
_bloodstream.ChangeBloodReagents((uid, bloodstream), new([new(SynthBloodReagent, bloodstream.BloodReferenceSolution.Volume)]));
|
||||
}
|
||||
// End DeltaV
|
||||
|
||||
_tag.AddTag(uid, SyntheticEmotesTag); // delta-v - proper synthe emote implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Start DeltaV - proper synthe emote implementation
|
||||
private void OnEmote(EntityUid uid, SynthComponent component, ref EmoteEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
_syntheticEmoteSounds ??= _proto.Index(SyntheticEmoteSounds);
|
||||
|
||||
args.Handled = _chat.TryPlayEmoteSound(uid, _syntheticEmoteSounds, args.Emote);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -521,7 +521,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,188 +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.
|
||||
type: Fix
|
||||
id: 1964
|
||||
time: '2025-12-16T15:08:42.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/4967
|
||||
- author: ShepardToTheStars
|
||||
changes:
|
||||
- message: Brains are no longer edible.
|
||||
@@ -4394,4 +4210,211 @@
|
||||
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
|
||||
- author: wesk
|
||||
changes:
|
||||
- message: Fixed synthetics from giving everyone borg emotes on round-start.
|
||||
type: Fix
|
||||
id: 2465
|
||||
time: '2026-06-26T17:23:37.0000000+00:00'
|
||||
url: https://github.com/DeltaV-Station/Delta-v/pull/6125
|
||||
Order: 1
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Base structure damage examine values
|
||||
comp-structure-damaged-1 = It looks fully intact.
|
||||
comp-structure-damaged-2 = It has a few scratches.
|
||||
comp-structure-damaged-3 = It has a few large dents.
|
||||
comp-structure-damaged-4 = [color=yellow]It has several big cracks running along its surface.[/color]
|
||||
comp-structure-damaged-5 = [color=orange]It has deep cracks across multiple layers.[/color]
|
||||
comp-structure-damaged-6 = [color=red]It's extremely damaged and on the verge of falling apart.[/color]
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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}.
|
||||
@@ -54,8 +54,6 @@ id-card-access-level-external = External
|
||||
id-card-access-level-nuclear-operative = Nuclear Operative
|
||||
id-card-access-level-syndicate-agent = Syndicate Agent
|
||||
|
||||
id-card-access-level-unused = You shouldn't see this
|
||||
|
||||
id-card-access-level-central-command = Central Command
|
||||
|
||||
id-card-access-level-wizard = Wizard
|
||||
|
||||
+9309
-2749
File diff suppressed because it is too large
Load Diff
@@ -218,6 +218,7 @@
|
||||
- id: NewtonCradle
|
||||
- id: BoxEnvelope
|
||||
- id: BrbSign
|
||||
- id: ClothingNeckLanyard #Harmony addition. Also obtainable through character loadouts and PTech vendor
|
||||
# Begin DeltaV - More folders & colored paper reams in the bureaucracy crate + denied/approved stamp, low chance of paperwork hater stamp
|
||||
- id: BoxFolderBlack
|
||||
amount: 2
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
ClownPDA: 1
|
||||
MimePDA: 1
|
||||
MusicianPDA: 1
|
||||
ClothingNeckLanyard: 5 #Harmony addition. Also obtainable through bureaucracy crates and character loadouts
|
||||
# End Delta-V additions
|
||||
# Begin Delta-V subtractions
|
||||
# EncryptionKeyCargo: 2
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
transmitFrequencyId: SuitSensor
|
||||
savableAddress: false
|
||||
- type: WirelessNetworkConnection
|
||||
range: 1200
|
||||
- type: StationLimitedNetwork
|
||||
range: 3600 # DeltaV - was 1200
|
||||
# - type: StationLimitedNetwork # DeltaV - this stinks and we don't want it to be limited
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
# End CD - Character Records
|
||||
- type: DeviceNetwork
|
||||
deviceNetId: Wireless
|
||||
receiveFrequencyId: CrewMonitor
|
||||
receiveFrequencyId: NTNet # DeltaV - NTNet
|
||||
transmitFrequencyId: ShuttleTimer
|
||||
- type: WirelessNetworkConnection
|
||||
range: 500
|
||||
|
||||
@@ -68,6 +68,10 @@
|
||||
enum.RingerUiKey.Key:
|
||||
type: RingerBoundUserInterface
|
||||
# End DeltaV - AI PDAs
|
||||
# Begin DeltaV - AI fax
|
||||
enum.FaxUiKey.Key:
|
||||
type: StationAiFaxBoundUserInterface
|
||||
# End DeltaV - AI fax
|
||||
- type: IntrinsicUI
|
||||
uis:
|
||||
enum.RadarConsoleUiKey.Key:
|
||||
@@ -78,12 +82,16 @@
|
||||
toggleAction: ActionAGhostShowStationRecords
|
||||
enum.CommunicationsConsoleUiKey.Key:
|
||||
toggleAction: ActionAGhostShowCommunications
|
||||
# Begin DeltaV - AI fax
|
||||
enum.FaxUiKey.Key:
|
||||
toggleAction: ActionStationAiOpenFax
|
||||
# End DeltaV - AI fax
|
||||
- type: CrewMonitoringConsole
|
||||
- type: GeneralStationRecordConsole
|
||||
- type: DeviceNetwork
|
||||
deviceNetId: Wireless
|
||||
receiveFrequencyId: CrewMonitor
|
||||
transmitFrequencyId: ShuttleTimer
|
||||
receiveFrequencyId: NTNet # DeltaV
|
||||
transmitFrequencyId: NTNet # DeltaV
|
||||
- type: RadarConsole
|
||||
followEntity: false
|
||||
- type: CommunicationsConsole
|
||||
@@ -95,6 +103,27 @@
|
||||
startPowerCorruptionAtCharIdx: 4
|
||||
maxPowerCorruptionAtCharIdx: 20
|
||||
- type: StationAiShop # DeltaV - AI Shop System
|
||||
# Begin DeltaV - AI faxes
|
||||
- type: FaxMachine
|
||||
name: Station AI
|
||||
printSound: null
|
||||
paperSlot:
|
||||
insertSound: /Audio/Machines/scanning.ogg
|
||||
ejectSound: /Audio/Machines/tray_eject.ogg
|
||||
whitelist:
|
||||
components:
|
||||
- FaxableObject
|
||||
blacklist:
|
||||
components:
|
||||
- CargoSlip
|
||||
tags:
|
||||
- PaperSlip
|
||||
- type: StationAiFax
|
||||
- type: Pager
|
||||
autoLinkJobs: [ StationAi ]
|
||||
- type: PageSender
|
||||
autoLinkJobs: [ StationAi ]
|
||||
# End DeltaV - AI faxes
|
||||
|
||||
- type: entity
|
||||
id: AiHeldIntellicard
|
||||
@@ -534,6 +563,10 @@
|
||||
containers:
|
||||
program-container: !type:Container
|
||||
Cartridge-Slot: !type:ContainerSlot
|
||||
# Begin DeltaV - AI faxes
|
||||
Paper: !type:ContainerSlot
|
||||
PaperLibrary: !type:Container
|
||||
# End DeltaV - AI faxes
|
||||
- type: ItemSlots
|
||||
- type: PdaBorderColor
|
||||
borderColor: "#45453f"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user