Compare commits

..
Author SHA1 Message Date
Janet Blackquill fc2d05f548 ser/de 2026-06-20 06:10:53 -05:00
Janet Blackquill 4c7f99296c and that's a wrap 2026-06-20 06:10:53 -05:00
pre-commit-ci[bot] df1e97986a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-06-20 06:10:53 -05:00
Janet Blackquill 88203bb6b2 and that's a wrap 2026-06-20 06:10:53 -05:00
Janet Blackquill 158a114804 nuke me harder 2026-06-20 06:10:53 -05:00
Janet Blackquill d438aec056 she rise up until i don't have a round end condition yet 2026-06-20 06:10:53 -05:00
399 changed files with 4963 additions and 14419 deletions
+18 -78
View File
@@ -73,88 +73,28 @@ 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.**
### Single-Line Changes
Format should look like this.
A comment on a new imported namespace:
```cs
/* 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
using Content.Server.Psionics.Glimmer; // DeltaV
```
> * 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:
A pair of comments enclosing a block of added code:
```cs
// BEGIN DeltaV - Remove innate radio and radios from pockets
for (var i = 1; i <= 4; i++) // Arachnids have 4 pockets
private EntityUid Slice(...)
{
if (_inventory.TryGetSlotEntity(target, $"pocket{i}", out var headset) && HasComp<HeadsetComponent>(headset))
_inventory.TryUnequip(target, $"pocket{i}", true, true);
...
_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
...
}
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.
@@ -164,10 +104,10 @@ Example:
Commented out old string in `Resources\Locale\en-US\xenoarchaeology\artifact-analyzer.ftl`
```
# DeltaV - moved to _DV file
# analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state ->
# [true] {$info}
# *[false] Unlock nodes to gain info
# }[/color][/font]
#analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state ->
# [true] {$info}
# *[false] Unlock nodes to gain info
#}[/color][/font]
```
The new version of the string in `Resources\Locale\en-US\_DV\xenoarchaeology\artifact-analyzer.ftl`
-10
View File
@@ -7,7 +7,6 @@ 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;
@@ -159,15 +158,6 @@ 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);
}
@@ -10,7 +10,6 @@
<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,8 +21,7 @@ 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-radio")); // DeltaV - Radio Channel Colors
Tabs.SetTabTitle(7, Loc.GetString("ui-options-tab-deltav")); // DeltaV specific settings
Tabs.SetTabTitle(6, Loc.GetString("ui-options-tab-deltav")); // DeltaV specific settings
UpdateTabs();
}
@@ -37,7 +36,6 @@ 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
}
}
+111
View File
@@ -159,6 +159,35 @@ 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:
@@ -1648,6 +1677,88 @@ 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,7 +1,6 @@
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;
@@ -840,38 +839,8 @@ 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,19 +12,6 @@ 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
@@ -94,21 +81,6 @@ 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 = { ContainerButton.StyleClassButton, styleClass }
StyleClasses = { styleClass }
};
var buttonLabel = new RichTextLabel
@@ -1,87 +0,0 @@
<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>
@@ -1,55 +0,0 @@
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();
}
}
@@ -1,49 +0,0 @@
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;
}
}
@@ -108,70 +108,6 @@ 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)
@@ -235,11 +171,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, IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
public void UpdateConditions(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
foreach (var (_, entry) in _traitEntries)
{
entry.UpdateConditionsMet(jobId, speciesId, antagPreferences, selectedTraits);
entry.UpdateConditionsMet(jobId, speciesId, antagPreferences);
}
// Update stats since some traits may have been deselected
+37 -158
View File
@@ -27,9 +27,6 @@ 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);
@@ -62,17 +59,9 @@ public sealed partial class TraitEntry : PanelContainer
foreach (var condition in _trait.Conditions)
{
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);
}
var tooltip = condition.GetTooltip(_prototype, _loc);
if (!string.IsNullOrEmpty(tooltip))
tooltips.Add(tooltip);
}
if (tooltips.Count > 0)
@@ -102,92 +91,39 @@ 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,
IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
public void UpdateConditionsMet(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
_failedConditionTooltips.Clear();
MeetsConditions = true;
foreach (var condition in _trait.Conditions)
{
bool result;
if (condition is TraitDependencyCondition depCond)
var result = condition switch
{
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,
};
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,
};
// Apply inversion for non-dependency conditions
result ^= condition.Invert;
if (!result)
{
var tooltip = condition.GetTooltip(_prototype, _loc);
if (!string.IsNullOrEmpty(tooltip))
_failedConditionTooltips.Add(tooltip);
}
}
// Apply inversion
result ^= condition.Invert;
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))
@@ -223,12 +159,7 @@ 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,
IReadOnlySet<ProtoId<TraitPrototype>>? selectedTraits)
private bool CheckAnyOfCondition(AnyOfCondition condition, ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
if (condition.Conditions.Count == 0)
return false;
@@ -236,27 +167,19 @@ public sealed partial class TraitEntry : PanelContainer
// Return true if ANY child condition evaluates to true
foreach (var childCondition in condition.Conditions)
{
bool result;
if (childCondition is TraitDependencyCondition depCond)
var result = childCondition switch
{
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,
};
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,
};
result ^= childCondition.Invert;
}
// Apply child's inversion
result ^= childCondition.Invert;
// If any child passes, the AnyOf passes
if (result)
@@ -267,50 +190,16 @@ 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()
{
var isSelected = TraitCheckbox.Pressed;
var conditionLocked = !MeetsConditions;
var categoryLocked = _isLockedByCategory && !isSelected;
var pointsLocked = _isLockedByPoints && !isSelected;
var isDisabled = conditionLocked || categoryLocked || pointsLocked;
if (isDisabled)
if (!MeetsConditions)
{
// Hide checkbox, show lock icon
TraitCheckbox.Visible = false;
LockIcon.Visible = true;
if (isSelected && conditionLocked)
// Deselect if conditions no longer met
if (TraitCheckbox.Pressed)
{
_isUpdating = true;
TraitCheckbox.Pressed = false;
@@ -322,24 +211,14 @@ public sealed partial class TraitEntry : PanelContainer
// Add disabled styling
AddStyleClass("TraitsEntryDisabled");
// Tooltip priority for conditions: condition failures > category full > insufficient points.
if (conditionLocked && _failedConditionTooltips.Count > 0)
// Update tooltip to show failed conditions
if (_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
{
+8 -18
View File
@@ -154,18 +154,14 @@ public sealed partial class TraitsTab : BoxContainer
}
}
// Check conflicts via TraitDependencyCondition
var depCondition = trait.Conditions.OfType<Content.Shared._DV.Traits.Conditions.TraitDependencyCondition>().FirstOrDefault();
if (depCondition != null)
// Check conflicts
foreach (var conflict in trait.Conflicts)
{
foreach (var conflict in depCondition.Conflicts)
{
if (!_selectedTraits.Contains(conflict))
continue;
if (!_selectedTraits.Contains(conflict))
continue;
RevertTraitToggle(traitId);
return;
}
RevertTraitToggle(traitId);
return;
}
_selectedTraits.Add(traitId);
@@ -180,7 +176,6 @@ public sealed partial class TraitsTab : BoxContainer
}
UpdateGlobalStats();
UpdateAllConditions();
UpdateCategoryStats(trait.Category);
OnTraitsChanged?.Invoke(_selectedTraits);
}
@@ -216,7 +211,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)
@@ -240,11 +235,6 @@ public sealed partial class TraitsTab : BoxContainer
> 0f => "TraitsProgressBarLow",
_ => "TraitsProgressBarEmpty"
});
foreach (var (_, categoryUi) in _categoryUis)
{
categoryUi.UpdateGlobalPointsLock(remainingPoints);
}
}
private void OnProgressBarParentResized()
@@ -291,7 +281,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, _selectedTraits);
categoryUi.UpdateConditions(null, _profile?.Species, _profile?.AntagPreferences);
}
RecalculateStats();
@@ -262,6 +262,32 @@ public sealed partial class AdminVerbSystem
Message = string.Join(": ", ntAgent, Loc.GetString("admin-verb-text-make-NTAgent")),
};
args.Verbs.Add(agent);
args.Verbs.Add(new ()
{
Text = Loc.GetString("admin-verb-make-HeadInsurgent.text"),
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/job_icons.rsi"), "HeadRevolutionary"),
Act = () =>
{
_antag.ForceMakeAntag<NTAgentRuleComponent>(targetPlayer, "UprisingRule", "HeadInsurgent");
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-HeadInsurgent.message"),
});
args.Verbs.Add(new ()
{
Text = Loc.GetString("admin-verb-make-HeadLoyalist.text"),
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/job_icons.rsi"), "Nanotrasen"),
Act = () =>
{
_antag.ForceMakeAntag<NTAgentRuleComponent>(targetPlayer, "UprisingRule", "HeadLoyalist");
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-HeadLoyalist.message"),
});
// End DeltaV Additions
// start DeltaV Additions - add hitman
var hitmanName = Loc.GetString("admin-verb-make-hitman");
@@ -6,7 +6,6 @@ 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;
@@ -192,8 +191,7 @@ public sealed class AlertLevelSystem : EntitySystem
{
if (detail.Sound != null)
{
//var filter = _stationSystem.GetInOwningStation(station); // DeltaV - Global Annoucements
var filter = Filter.Empty().AddAllPlayers(); // DeltaV - Global Annoucements
var filter = _stationSystem.GetInOwningStation(station);
_audio.PlayGlobal(detail.Sound, filter, true, detail.Sound.Params);
}
else
@@ -204,7 +202,7 @@ public sealed class AlertLevelSystem : EntitySystem
if (announce)
{
_chatSystem.DispatchGlobalAnnouncement(announcementFull, playSound: playDefault, // DeltaV - Global Annoucements
_chatSystem.DispatchStationAnnouncement(station, announcementFull, playDefaultSound: playDefault,
colorOverride: detail.Color, sender: stationName);
}
@@ -23,7 +23,8 @@ public sealed partial class AntagSelectionSystem
/// </summary>
public bool TryGetNextAvailableDefinition(Entity<AntagSelectionComponent> ent,
[NotNullWhen(true)] out AntagSelectionDefinition? definition,
int? players = null)
int? players = null,
string? preferredRole = null) // DeltaV - preferrec roles
{
definition = null;
@@ -39,6 +40,11 @@ public sealed partial class AntagSelectionSystem
// It needs to track selected minds for each definition independently.
foreach (var def in ent.Comp.Definitions)
{
// Begin DeltaV - preferred roles
if (preferredRole is not null && !def.PrefRoles.Contains(preferredRole))
continue;
// End DeltaV - preferred roles
var target = GetTargetAntagCount(ent, null, def);
if (mindCount < target)
@@ -336,11 +342,11 @@ public sealed partial class AntagSelectionSystem
/// This technically is a gamerule-ent-less way to make an entity an antag.
/// You should almost never be using this.
/// </summary>
public void ForceMakeAntag<T>(ICommonSession? player, string defaultRule) where T : Component
public void ForceMakeAntag<T>(ICommonSession? player, string defaultRule, string? preferredRole = null) where T : Component // DeltaV - preferred antag roles
{
var rule = ForceGetGameRuleEnt<T>(defaultRule);
if (!TryGetNextAvailableDefinition(rule, out var def))
if (!TryGetNextAvailableDefinition(rule, out var def, preferredRole: preferredRole)) // DeltaV - preferred antag roles
def = rule.Comp.Definitions.Last();
MakeAntag(rule, player, def.Value);
}
@@ -7,7 +7,7 @@ using Robust.Shared.Player;
namespace Content.Server.Audio;
public sealed partial class ServerGlobalSoundSystem : SharedGlobalSoundSystem // DeltaV - Made Partial
public sealed class ServerGlobalSoundSystem : SharedGlobalSoundSystem
{
[Dependency] private readonly IConsoleHost _conHost = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
+11 -11
View File
@@ -327,15 +327,15 @@ public sealed class NukeSystem : EntitySystem
// should play
if (nuke.RemainingTime <= _nukeSongLength + nuke.AlertSoundTime + NukeSongBuffer && !nuke.PlayedNukeSong && !ResolvedSoundSpecifier.IsNullOrEmpty(_selectedNukeSong))
{
_sound.DispatchGlobalEventMusic(_selectedNukeSong, StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
_sound.DispatchStationEventMusic(uid, _selectedNukeSong, StationEventMusicType.Nuke);
nuke.PlayedNukeSong = true;
}
// play alert sound if time is running out
if (nuke.RemainingTime <= nuke.AlertSoundTime && !nuke.PlayedAlertSound)
{
_sound.PlayGlobal(_audio.ResolveSound(nuke.AlertSound), new AudioParams { Volume = -5f }); // DeltaV - Global Nuke SFX
_sound.StopGlobalEventMusic(StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(nuke.AlertSound), new AudioParams{Volume = -5f});
_sound.StopStationEventMusic(uid, StationEventMusicType.Nuke);
nuke.PlayedAlertSound = true;
UpdateAppearance(uid, nuke);
}
@@ -475,7 +475,7 @@ public sealed class NukeSystem : EntitySystem
/// <summary>
/// Force a nuclear bomb to start a countdown timer
/// </summary>
public void ArmBomb(EntityUid uid, NukeComponent? component = null)
public void ArmBomb(EntityUid uid, NukeComponent? component = null, string? announcementId = null) // DeltaV - custom announcements
{
if (!Resolve(uid, ref component))
return;
@@ -500,13 +500,13 @@ public sealed class NukeSystem : EntitySystem
_selectedNukeSong = _audio.ResolveSound(component.ArmMusic);
// warn a crew
var announcement = Loc.GetString("nuke-component-announcement-armed",
var announcement = Loc.GetString(announcementId ?? "nuke-component-announcement-armed", // DeltaV - custom announcements
("time", (int) component.RemainingTime),
("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((uid, nukeXform)))));
var sender = Loc.GetString("nuke-component-announcement-sender");
_chatSystem.DispatchGlobalAnnouncement(announcement, sender, false, null, Color.Red); // DeltaV - Global Nuke Annoucement
_chatSystem.DispatchStationAnnouncement(stationUid ?? uid, announcement, sender, false, null, Color.Red);
_sound.PlayGlobal(_audio.ResolveSound(component.ArmSound)); // DeltaV - Global Nuke Music
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(component.ArmSound));
_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.DispatchGlobalAnnouncement(announcement, sender, false); // DeltaV - Global Nuke Announcement
_chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false);
component.PlayedNukeSong = false;
_sound.PlayGlobal(_audio.ResolveSound(component.DisarmSound)); // DeltaV - Global Nuke SFX
_sound.StopGlobalEventMusic(StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(component.DisarmSound));
_sound.StopStationEventMusic(uid, StationEventMusicType.Nuke);
// 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.StopGlobalEventMusic(StationEventMusicType.Nuke); // DeltaV - Global Nuke Music
_sound.StopStationEventMusic(uid, StationEventMusicType.Nuke);
Del(uid);
}
@@ -157,8 +157,7 @@ public sealed class RadioSystem : EntitySystem
message,
wrappedMessage,
NetEntity.Invalid,
null,
radioChannelProto: channel.ID); // DeltaV - Add RadioChannel for committing sins
null);
var chatMsg = new MsgChatMessage { Message = chat };
var ev = new RadioReceiveEvent(message, messageSource, channel, radioSource, chatMsg);
@@ -210,7 +209,7 @@ public sealed class RadioSystem : EntitySystem
}
/// <inheritdoc cref="TelecomServerComponent"/>
public bool HasActiveServer(MapId mapId, string channelId) // DeltaV - we need this
private bool HasActiveServer(MapId mapId, string channelId)
{
var servers = EntityQuery<TelecomServerComponent, EncryptionKeyHolderComponent, ApcPowerReceiverComponent, TransformComponent>();
foreach (var (_, keys, power, transform) in servers)
@@ -0,0 +1,84 @@
using Content.Server.Revolutionary.Components;
using Content.Shared._DV.Uprising;
using Content.Shared.Cuffs.Components;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Objectives.Components;
using Content.Shared.Roles;
using Content.Shared.Station;
namespace Content.Server.Uprising;
public sealed class CommandStatusObjectiveSystem : EntitySystem
{
[Dependency] private readonly SharedRoleSystem _role = default!;
[Dependency] private readonly SharedStationSystem _station = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CommandStatusObjectiveComponent, ObjectiveGetProgressEvent>(OnGetProgress);
}
private bool CheckIncapacitated(EntityUid uid)
{
if (TryComp<CuffableComponent>(uid, out var cuffed) && cuffed.CuffedHandCount > 0)
return true;
if (!TryComp<MobStateComponent>(uid, out var state))
return true;
if (state.CurrentState is MobState.Dead or MobState.Invalid)
return true;
if (_station.GetOwningStation(uid) == null)
return true;
return false;
}
private void OnGetProgress(Entity<CommandStatusObjectiveComponent> ent, ref ObjectiveGetProgressEvent args)
{
var total = 0;
var counted = 0;
var query = AllEntityQuery<MindContainerComponent, CommandStaffComponent>();
while (query.MoveNext(out var uid, out var mind, out _))
{
if (mind.Mind is not { } mindUid)
continue;
total++;
var converted = false;
foreach (var role in ent.Comp.ConvertedRoles)
{
if (!EntityManager.ComponentFactory.TryGetRegistration(role, out var roleReg))
{
Log.Error($"Role component not found for CrewConversionObjective: {role}");
continue;
}
if (!_role.MindHasRole(mindUid, roleReg.Type, out _))
continue;
converted = true;
break;
}
var incapacitated = CheckIncapacitated(uid);
if (converted && !incapacitated || !converted && ent.Comp.ShouldUnconvertedBeIncapacitated && incapacitated)
{
counted++;
}
}
if (total == 0)
return;
args.Progress = (float)counted / (float)total;
}
}
@@ -1,73 +0,0 @@
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);
}
}
@@ -6,6 +6,12 @@ namespace Content.Server._DV.CartridgeLoader.Cartridges;
[RegisterComponent, Access(typeof(NanoChatCartridgeSystem))]
public sealed partial class NanoChatCartridgeComponent : Component
{
/// <summary>
/// Station entity to keep track of.
/// </summary>
[DataField]
public EntityUid? Station;
/// <summary>
/// The NanoChat card to keep track of.
/// </summary>
@@ -2,8 +2,9 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.CartridgeLoader;
using Content.Server.Power.Components;
using Content.Server.Radio;
using Content.Server.Radio.EntitySystems;
using Content.Server.Station.Systems;
using Content.Shared.Access.Components;
using Content.Shared.CartridgeLoader;
using Content.Shared.CCVar;
@@ -29,7 +30,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedNanoChatSystem _nanoChat = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] private readonly RadioSystem _radio = default!;
[Dependency] private readonly StationSystem _station = default!;
private EntityQuery<PdaComponent> _pdaQuery;
private EntityQuery<NanoChatCardComponent> _cardQuery;
@@ -455,7 +456,9 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
{
// First verify we can send from this device
var channel = _prototype.Index(sender.Comp.RadioChannel);
if (!CanSend(sender))
var sendAttemptEvent = new RadioSendAttemptEvent(channel, sender);
RaiseLocalEvent(ref sendAttemptEvent);
if (sendAttemptEvent.Cancelled)
return (true, new List<Entity<NanoChatCardComponent>>());
var foundRecipients = new List<Entity<NanoChatCardComponent>>();
@@ -484,19 +487,26 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
if (receiverCart.Card != recipient.Owner)
continue;
var receiverMapId = Transform(receiverUid).MapID;
var senderMapId = Transform(sender).MapID;
// Check if devices are on same station/map
var recipientStation = _station.GetOwningStation(receiverUid);
var senderStation = _station.GetOwningStation(sender);
// Must be on the same map unless long range is allowed.
if (!channel.LongRange && receiverMapId != senderMapId)
// Both entities must be on a station
if (recipientStation == null || senderStation == null)
continue;
// Check if telecommunications are active on both ends
if (!_radio.HasActiveServer(receiverMapId, receiverCart.RadioChannel) || !_radio.HasActiveServer(senderMapId, sender.Comp.RadioChannel))
// Must be on same map/station unless long range allowed
if (!channel.LongRange && recipientStation != senderStation)
continue;
// Needs telecomms
if (!HasActiveServer(senderStation.Value) || !HasActiveServer(recipientStation.Value))
continue;
// Check if recipient can receive
if (!CanReceive(sender, receiverUid))
var receiveAttemptEv = new RadioReceiveAttemptEvent(channel, sender, receiverUid);
RaiseLocalEvent(ref receiveAttemptEv);
if (receiveAttemptEv.Cancelled)
continue;
// Found valid cartridge that can receive
@@ -509,26 +519,21 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
}
/// <summary>
/// Tests if a NanoChat cartridge can send messages
/// Checks if there are any active telecomms servers on the given station
/// </summary>
/// <param name="sender">The NanoChat cartridge trying to send</param>
private bool CanSend(Entity<NanoChatCartridgeComponent> sender)
private bool HasActiveServer(EntityUid station)
{
var sendAttemptEvent = new RadioSendAttemptEvent(_prototype.Index(sender.Comp.RadioChannel), sender);
RaiseLocalEvent(ref sendAttemptEvent);
return !sendAttemptEvent.Cancelled;
}
// I have no idea why this isn't public in the RadioSystem
var query =
EntityQueryEnumerator<TelecomServerComponent, EncryptionKeyHolderComponent, ApcPowerReceiverComponent>();
/// <summary>
/// Tests if a receiver can receive from a given NanoChat cartridge
/// </summary>
/// <param name="sender">The NanoChat cartridge trying to send</param>
/// <param name="receiver">The receiver cartridge trying to receive</param>
private bool CanReceive(Entity<NanoChatCartridgeComponent> sender, EntityUid receiver)
{
var receiveAttemptEv = new RadioReceiveAttemptEvent(_prototype.Index(sender.Comp.RadioChannel), sender, receiver);
RaiseLocalEvent(ref receiveAttemptEv);
return !receiveAttemptEv.Cancelled;
while (query.MoveNext(out var uid, out _, out _, out var power))
{
if (_station.GetOwningStation(uid) == station && power.Powered)
return true;
}
return false;
}
/// <summary>
@@ -683,15 +688,16 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
private void UpdateUI(Entity<NanoChatCartridgeComponent> ent, EntityUid loader)
{
List<NanoChatRecipient>? contacts;
if (CanSend(ent) && _radio.HasActiveServer(Transform(ent).MapID, ent.Comp.RadioChannel))
if (_station.GetOwningStation(loader) is { } station)
{
ent.Comp.Station = station;
contacts = [];
var query = AllEntityQuery<NanoChatCardComponent, IdCardComponent>();
while (query.MoveNext(out var entityId, out var nanoChatCard, out var idCardComponent))
{
if (nanoChatCard.ListNumber && nanoChatCard.Number is uint nanoChatNumber && idCardComponent.FullName is string fullName)
if (nanoChatCard.ListNumber && nanoChatCard.Number is uint nanoChatNumber && idCardComponent.FullName is string fullName && _station.GetOwningStation(entityId) == station)
{
contacts.Add(new NanoChatRecipient(nanoChatNumber, fullName));
}
@@ -700,7 +706,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
var borgQuery = AllEntityQuery<NanoChatCardComponent, BorgChassisComponent>();
while (borgQuery.MoveNext(out var borgId, out var borgChatCard, out var _))
{
if (borgChatCard.ListNumber && borgChatCard.Number is uint nanoChatNumber)
if (borgChatCard.ListNumber && borgChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(borgId) == station)
{
contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(borgId).EntityName));
}
@@ -709,7 +715,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
var aiQuery = AllEntityQuery<NanoChatCardComponent, StationAiHeldComponent>();
while (aiQuery.MoveNext(out var aiId, out var aiChatCard, out var _))
{
if (aiChatCard.ListNumber && aiChatCard.Number is uint nanoChatNumber)
if (aiChatCard.ListNumber && aiChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(aiId) == station)
{
contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(aiId).EntityName));
}
@@ -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.StopGlobalEventMusic(StationEventMusicType.CosmicCult);
_sound.StopStationEventMusic(gameruleMonument, StationEventMusicType.CosmicCult);
_monument.UpdateMonumentAppearance(gameruleMonument, false);
}
@@ -82,9 +82,11 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
_corrupting.SetCorruptionTime((uid, corruptingComp), TimeSpan.FromSeconds(1));
_appearance.SetData(uid, MonumentVisuals.FinaleReached, 2);
_sound.DispatchGlobalEventMusic(comp.SelectedSong, StationEventMusicType.CosmicCult);
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cosmiccult-finale-location", ("location", indicatedLocation)),
null, false, null, Color.FromHex("#cae8e8"));
_sound.DispatchStationEventMusic(uid, comp.SelectedSong, StationEventMusicType.CosmicCult);
_chatSystem.DispatchStationAnnouncement(uid,
Loc.GetString("cosmiccult-finale-location", ("location", indicatedLocation)),
null, false, null,
Color.FromHex("#cae8e8"));
var stationUid = _station.GetStationInMap(Transform(uid).MapID);
if (stationUid != null)
@@ -126,8 +128,8 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
if (stationUid != null)
_alert.SetLevel(stationUid.Value, "green", true, true, true);
_sound.PlayGlobal(_audio.ResolveSound(comp.CancelEventSound));
_sound.StopGlobalEventMusic(StationEventMusicType.CosmicCult);
_sound.PlayGlobalOnStation(uid, _audio.ResolveSound(comp.CancelEventSound));
_sound.StopStationEventMusic(uid, StationEventMusicType.CosmicCult);
uid.Comp.CurrentState = FinaleState.ReadyFinale;
if (TryComp<CosmicCorruptingComponent>(uid, out var corruptingComp))
@@ -8,7 +8,6 @@ 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;
@@ -26,7 +25,6 @@ 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>
@@ -81,11 +79,11 @@ public sealed class CosmicChantrySystem : EntitySystem
comp.SpawnTimer = _timing.CurTime + comp.SpawningTime;
comp.CountdownTimer = _timing.CurTime + comp.EventTime;
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"));
_sound.PlayGlobalOnStation(ent, _audio.ResolveSound(comp.ChantryAlarm));
_chatSystem.DispatchStationAnnouncement(ent,
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.DispatchGlobalEventMusic(song, StationEventMusicType.CosmicCult);
_sound.DispatchStationEventMusic(uid, song, StationEventMusicType.CosmicCult);
}
if (comp.CurrentState == FinaleState.ActiveFinale && comp.FinaleAnnounceCheck && comp.FinaleTimer - _timing.CurTime < comp.VisualsThreshold)
{
_appearance.SetData(uid, MonumentVisuals.FinaleReached, 3);
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cosmiccult-announce-finale-warning"), null, false, null, Color.FromHex("#cae8e8"));
_chatSystem.DispatchStationAnnouncement(uid, Loc.GetString("cosmiccult-announce-finale-warning"), null, false, null, Color.FromHex("#cae8e8"));
comp.FinaleAnnounceCheck = false;
}
@@ -90,7 +90,6 @@ 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)
@@ -675,26 +674,4 @@ 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;
}
}
}
@@ -1,9 +1,9 @@
using Content.Server.Power.Components;
using Content.Server._DV.StationEvents.Events;
using Content.Server.StationEvents.Events;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
namespace Content.Server._DV.StationEvents.Components;
namespace Content.Server.StationEvents.Components;
/// <summary>
/// When fired, turns off power on the station for a few seconds, playing <see cref="EpsilonEventRuleComponent.PowerOffSound"/>
@@ -1,15 +1,14 @@
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.StationEvents.Events;
using Content.Server.StationEvents.Components;
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._DV.StationEvents.Events;
namespace Content.Server.StationEvents.Events
{
public sealed class EpsilonEventRule : StationEventSystem<EpsilonEventRuleComponent>
{
[Dependency] private readonly ApcSystem _apcSystem = default!;
@@ -26,7 +25,7 @@ public sealed class EpsilonEventRule : StationEventSystem<EpsilonEventRuleCompon
return;
component.AffectedStation = chosenStation.Value;
// Plays the power off sound for those who are on the station.
// Plays the power off sound to the station.
_sound.PlayGlobalOnStation(component.AffectedStation, _audio.ResolveSound(component.PowerOffSound));
var query = AllEntityQuery<ApcComponent, TransformComponent>();
@@ -71,3 +70,4 @@ public sealed class EpsilonEventRule : StationEventSystem<EpsilonEventRuleCompon
_alertLevelSystem.SetLevel(component.AffectedStation, "epsilon", true, true, true);
}
}
}
+1 -2
View File
@@ -109,8 +109,7 @@ public sealed class TraitSystem : EntitySystem
JobId = jobId,
SpeciesId = speciesId,
Profile = profile,
StatusEffects = _statusEffects,
SelectedTraits = selectedTraits
StatusEffects = _statusEffects
};
foreach (var traitId in selectedTraits)
@@ -0,0 +1,8 @@
namespace Content.Server._DV.Uprising;
[RegisterComponent, Access(typeof(UprisingRuleSystem))]
public sealed partial class UprisingConsoleComponent : Component
{
[DataField]
public TimeSpan Delay = TimeSpan.FromSeconds(5);
}
@@ -0,0 +1,54 @@
using Content.Shared._DV.Roles;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server._DV.Uprising;
[RegisterComponent, Access(typeof(UprisingRuleSystem)), AutoGenerateComponentPause]
public sealed partial class UprisingRuleComponent : Component
{
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan? NukeAnnouncementAt;
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan? NukeTimeAt;
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan? FirstWarningAt;
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan? ImpendingWarningAt;
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan? FinalWarningAt;
[DataField]
public TimeSpan NukeAnnouncementDelay = TimeSpan.FromMinutes(30);
[DataField]
public TimeSpan NukeTimeDelay = TimeSpan.FromMinutes(90);
[DataField]
public TimeSpan FirstWarning = TimeSpan.FromMinutes(30);
[DataField]
public TimeSpan ImpendingWarning = TimeSpan.FromMinutes(15);
[DataField]
public TimeSpan FinalWarning = TimeSpan.FromMinutes(5);
[DataField]
public float NukeDuration = 60f;
[DataField]
public (UprisingSide, TimeSpan)? SideWinsAt;
[DataField]
public UprisingSide? SideWon;
[DataField]
public Dictionary<UprisingSide, TimeSpan> SideTimes = new()
{
{ UprisingSide.Insurgent , TimeSpan.FromMinutes(3) },
{ UprisingSide.Loyalist , TimeSpan.FromMinutes(3) },
};
}
@@ -0,0 +1,341 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Antag;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.Nuke;
using Content.Server.Pinpointer;
using Content.Server.Roles;
using Content.Shared._DV.Roles;
using Content.Shared._DV.Uprising;
using Content.Shared.Chat;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.DoAfter;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Content.Shared.Objectives.Systems;
using Content.Shared.Roles;
using Content.Shared.Verbs;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Content.Server._DV.Uprising;
public sealed class UprisingRuleSystem : GameRuleSystem<UprisingRuleComponent>
{
[Dependency] private readonly SharedChatSystem _chat = default!;
[Dependency] private readonly NukeSystem _nuke = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
[Dependency] private readonly NavMapSystem _navMap = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly SharedRoleSystem _role = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly ISharedPlayerManager _player = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LoyalistRoleComponent, GetBriefingEvent>(OnGetLoyalistBriefing);
SubscribeLocalEvent<InsurgentRoleComponent, GetBriefingEvent>(OnGetInsurgentBriefing);
SubscribeLocalEvent<UprisingVictoryObjectiveComponent, ObjectiveGetProgressEvent>(OnUprisingVictoryProgress);
SubscribeLocalEvent<UprisingConsoleComponent, GetVerbsEvent<AlternativeVerb>>(OnGetConsoleVerbs);
SubscribeLocalEvent<UprisingConsoleComponent, UprisingArmDoAfter>(OnArm);
SubscribeLocalEvent<UprisingConsoleComponent, UprisingDisarmDoAfter>(OnDisarm);
}
private void OnUprisingVictoryProgress(Entity<UprisingVictoryObjectiveComponent> ent, ref ObjectiveGetProgressEvent args)
{
if (GetRule() is not { } rule)
return;
if (rule.Comp1.SideWinsAt is var (side, _) && ent.Comp.Side == side)
args.Progress = 0.5f;
else if (rule.Comp1.SideWon == ent.Comp.Side)
args.Progress = 1f;
else
args.Progress = 0f;
}
protected override void Started(EntityUid uid, UprisingRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
component.NukeAnnouncementAt = Timing.CurTime + component.NukeAnnouncementDelay;
component.NukeTimeAt = Timing.CurTime + component.NukeTimeDelay;
component.FirstWarningAt = component.NukeTimeAt - component.FirstWarning;
component.ImpendingWarningAt = component.NukeTimeAt - component.ImpendingWarning;
component.FinalWarningAt = component.NukeTimeAt - component.FinalWarning;
}
protected override void ActiveTick(EntityUid uid, UprisingRuleComponent component, GameRuleComponent gameRule, float frameTime)
{
base.ActiveTick(uid, component, gameRule, frameTime);
if (component.NukeAnnouncementAt is { } nukeAnnouncement && nukeAnnouncement <= Timing.CurTime)
{
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.NukeTimeDelay;
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-nuke-announcement", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
component.NukeAnnouncementAt = null;
}
if (component.FirstWarningAt is { } firstWarning && firstWarning <= Timing.CurTime)
{
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.FirstWarning;
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-first-warning", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
component.FirstWarningAt = null;
}
if (component.ImpendingWarningAt is { } impendingWarning && impendingWarning <= Timing.CurTime)
{
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.ImpendingWarning;
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-impending-warning", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
component.ImpendingWarningAt = null;
}
if (component.FinalWarningAt is { } finalWarning && finalWarning <= Timing.CurTime)
{
var time = component.NukeTimeAt.HasValue ? component.NukeTimeAt.Value - Timing.CurTime : component.FinalWarning;
_chat.DispatchGlobalAnnouncement(Loc.GetString("uprising-announcement-final-warning", ("time", (int)Math.Round(time.TotalMinutes))), colorOverride: Color.Gold);
component.FinalWarningAt = null;
}
if (component.NukeTimeAt is { } nukeTime && nukeTime <= Timing.CurTime)
{
component.NukeTimeAt = null;
var query = EntityQueryEnumerator<NukeComponent>();
while (query.MoveNext(out var bombUid, out var bomb))
{
_nuke.SetRemainingTime(bombUid, component.NukeDuration, bomb);
_itemSlots.SetLock(bombUid, bomb.DiskSlot, false);
_itemSlots.TryEject(bombUid, bomb.DiskSlot, null, out _, true);
_itemSlots.SetLock(bombUid, bomb.DiskSlot, true);
_nuke.ArmBomb(bombUid, bomb, "uprising-announcement-nuke-armed");
}
}
if (component.SideWinsAt is var (winningSide, winningTime) && winningTime <= Timing.CurTime)
{
component.SideWinsAt = null;
var query = EntityQueryEnumerator<NukeComponent>();
while (query.MoveNext(out var bombUid, out var bomb))
{
_nuke.DisarmBomb(bombUid, bomb);
}
component.SideWon = winningSide;
_chat.DispatchGlobalAnnouncement(
Loc.GetString($"uprising-announcement-victory-announcement-{winningSide}"),
Loc.GetString($"uprising-announcement-victory-announcement-{winningSide}.sender"),
colorOverride: Color.FromXaml(Loc.GetString($"uprising-announcement-victory-announcement-{winningSide}.color")));
GameTicker.EndRound();
}
}
protected override void AppendRoundEndText(EntityUid uid,
UprisingRuleComponent component,
GameRuleComponent gameRule,
ref RoundEndTextAppendEvent args)
{
if (component.SideWinsAt is var (partialSide, _))
{
args.AddLine(Loc.GetString($"uprising-outcome-{partialSide}-partial"));
}
else if (component.SideWon is { } winningSide)
{
args.AddLine(Loc.GetString($"uprising-outcome-{winningSide}-full"));
}
else
{
args.AddLine(Loc.GetString("uprising-outcome-none"));
}
args.AddLine(Loc.GetString("uprising-leads"));
var antags = _antag.GetAntagIdentifiers(uid);
var leads = new HashSet<EntityUid>();
foreach (var (mind, sessionData, name) in antags)
{
leads.Add(mind);
if (_role.MindHasRole<UprisingRoleComponent>(mind, out var it))
args.AddLine(Loc.GetString($"uprising-leads-user.{it.Value.Comp2.Side}", ("name", name), ("user", sessionData.UserName)));
else
args.AddLine(Loc.GetString("uprising-leads-user.Unknown", ("name", name), ("user", sessionData.UserName)));
}
args.AddLine(string.Empty);
var lines = new Dictionary<UprisingSide, List<string>>();
foreach (var value in Enum.GetValues<UprisingSide>())
{
lines[value] = new();
}
var minds = EntityQueryEnumerator<MindComponent>();
while (minds.MoveNext(out var mindUid, out var mindComponent))
{
if (leads.Contains(mindUid))
continue;
if (!_role.MindHasRole<UprisingRoleComponent>((mindUid, mindComponent), out var role) || mindComponent.OriginalOwnerUserId is not { } owner)
continue;
if (!_player.TryGetPlayerData(owner, out var data))
continue;
lines[role.Value.Comp2.Side].Add(Loc.GetString($"uprising-members-user", ("name", mindComponent.CharacterName ?? string.Empty), ("user", data.UserName)));
}
foreach (var value in Enum.GetValues<UprisingSide>())
{
args.AddLine(Loc.GetString($"uprising-members.{value}"));
foreach (var line in lines[value])
{
args.AddLine(line);
}
args.AddLine(string.Empty);
}
}
private void OnGetLoyalistBriefing(Entity<LoyalistRoleComponent> ent, ref GetBriefingEvent args)
{
args.Append(Loc.GetString("antag-Loyalist.briefing"));
}
private void OnGetInsurgentBriefing(Entity<InsurgentRoleComponent> ent, ref GetBriefingEvent args)
{
args.Append(Loc.GetString("antag-Insurgent.briefing"));
}
private Entity<UprisingRuleComponent, GameRuleComponent>? GetRule()
{
var enumerator = QueryActiveRules();
while (enumerator.MoveNext(out var uid, out _, out var uprising, out var gamerule))
{
return (uid, uprising, gamerule);
}
return null;
}
private void StartWinning(UprisingSide side, EntityUid console)
{
if (GetRule() is not { } rule)
return;
var location = _navMap.GetNearestBeaconString(console);
var announcement = Loc.GetString($"uprising-announcement-start-victory-announcement-{side}",
("time", (int) Math.Round(rule.Comp1.SideTimes[side].TotalSeconds)),
("location", FormattedMessage.RemoveMarkupOrThrow(location)));
_chat.DispatchGlobalAnnouncement(
announcement,
Loc.GetString($"uprising-announcement-start-victory-announcement-{side}.sender"),
colorOverride: Color.FromXaml(Loc.GetString($"uprising-announcement-start-victory-announcement-{side}.color")));
rule.Comp1.SideWinsAt = (side, Timing.CurTime + rule.Comp1.SideTimes[side]);
}
private void StopWinning()
{
if (GetRule() is not { } rule || rule.Comp1.SideWinsAt is not var (side, time))
return;
rule.Comp1.SideTimes[side] = Timing.CurTime - time;
_chat.DispatchGlobalAnnouncement(
Loc.GetString($"uprising-announcement-interrupted-victory-announcement-{side}"),
Loc.GetString($"uprising-announcement-interrupted-victory-announcement-{side}.sender"),
colorOverride: Color.FromXaml(Loc.GetString($"uprising-announcement-interrupted-victory-announcement-{side}.color")));
rule.Comp1.SideWinsAt = null;
}
private void OnGetConsoleVerbs(Entity<UprisingConsoleComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (GetRule() is not { } rule)
return;
var user = args.User;
if (rule.Comp1.SideWinsAt is var (disarmSide, _))
{
args.Verbs.Add(new ()
{
Act = () => StartDisarming(ent, user),
Text = Loc.GetString($"uprising-disarm-verb.{disarmSide}"),
});
return;
}
if (!CheckCanArm(args.User, out var armSide))
return;
args.Verbs.Add(new()
{
Act = () => StartArming(ent, user),
Text = Loc.GetString($"uprising-arm-verb.{armSide}"),
});
}
private bool CheckCanArm(EntityUid user, [NotNullWhen(true)] out UprisingSide? side)
{
side = null;
if (_mind.GetMind(user) is not { } mind || !TryComp<MindComponent>(mind, out var mindComp))
return false;
if (!_role.MindHasRole<UprisingRoleComponent>(mind, out var uprisingRole))
return false;
foreach (var objective in mindComp.Objectives)
{
if (!HasComp<UprisingArmRequiredObjectiveComponent>(objective))
continue;
if (_objectives.GetProgress(objective, (mind, mindComp)) is not >= 1)
return false;
}
side = uprisingRole.Value.Comp2.Side;
return true;
}
private void StartDisarming(Entity<UprisingConsoleComponent> ent, EntityUid user)
{
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager,
user,
ent.Comp.Delay,
new UprisingDisarmDoAfter(),
ent.Owner));
}
private void StartArming(Entity<UprisingConsoleComponent> ent, EntityUid user)
{
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager,
user,
ent.Comp.Delay,
new UprisingArmDoAfter(),
ent.Owner));
}
private void OnArm(Entity<UprisingConsoleComponent> ent, ref UprisingArmDoAfter args)
{
if (args.Handled || args.Cancelled || !CheckCanArm(args.User, out var side))
return;
StartWinning(side.Value, ent);
}
private void OnDisarm(Entity<UprisingConsoleComponent> ent, ref UprisingDisarmDoAfter args)
{
if (args.Handled || args.Cancelled)
return;
StopWinning();
}
}
+1 -9
View File
@@ -41,13 +41,7 @@ namespace Content.Shared.Chat
[NonSerialized]
public bool Read;
/// <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
public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, int? senderKey, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
{
Channel = channel;
Message = message;
@@ -58,7 +52,6 @@ namespace Content.Shared.Chat
MessageColorOverride = colorOverride;
AudioPath = audioPath;
AudioVolume = audioVolume;
RadioChannelProtoId = radioChannelProto; // DeltaV
}
public ChatMessage(ChatMessage copyFrom)
@@ -73,7 +66,6 @@ namespace Content.Shared.Chat
AudioPath = copyFrom.AudioPath;
AudioVolume = copyFrom.AudioVolume;
Read = copyFrom.Read;
RadioChannelProtoId = copyFrom.RadioChannelProtoId; // DeltaV
}
}
@@ -2,7 +2,6 @@ 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;
@@ -294,7 +293,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, IInventoryRelayEvent //Harmony InventoryRelayEvent for Lanyards
public sealed class ExaminedEvent : EntityEventArgs
{
/// <summary>
/// The message that will be displayed as the examine text.
@@ -530,7 +529,6 @@ 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.
}
@@ -33,7 +33,6 @@ 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;
@@ -65,7 +64,6 @@ 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,8 +1,6 @@
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;
@@ -10,7 +8,6 @@ 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;
@@ -37,11 +34,6 @@ 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)
@@ -131,65 +123,9 @@ 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)
{
@@ -120,23 +120,10 @@ 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,10 +32,4 @@ 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,14 +17,8 @@ public sealed partial class PowerCellSystem
ent.Comp.Enabled = enabled;
Dirty(ent, ent.Comp);
// 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
if (TryGetBatteryFromSlot(ent.Owner, out var battery))
_battery.RefreshChargeRate(battery.Value.AsNullable());
}
@@ -24,7 +24,7 @@ public sealed partial class RadioChannelPrototype : IPrototype
public int Frequency { get; private set; } = 0;
[DataField("color")]
public Color Color { get; set; } = Color.Lime; // DeltaV - Client-side radio channel colors, no longer private set
public Color Color { get; private set; } = Color.Lime;
[IdDataField, ViewVariables]
public string ID { get; private set; } = default!;
@@ -0,0 +1,14 @@
using Content.Shared.Actions;
using Content.Shared.Roles.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared.Uprising;
public sealed partial class ConversionAcceptActionEvent : InstantActionEvent
{
[DataField(required: true)]
public EntProtoId MindRole;
[DataField(required: true)]
public EntProtoId<MindRoleComponent> RemoveMindRole;
}
@@ -0,0 +1,33 @@
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Robust.Shared.Network;
namespace Content.Shared.Uprising;
public sealed class ConversionAcceptActionSystem : EntitySystem
{
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _role = default!;
[Dependency] private readonly INetManager _net = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MindContainerComponent, ConversionAcceptActionEvent>(OnAcceptConversion);
}
private void OnAcceptConversion(Entity<MindContainerComponent> ent, ref ConversionAcceptActionEvent args)
{
if (_mind.GetMind(ent) is not { } mind)
return;
args.Handled = true;
if (_net.IsServer)
{
_role.MindAddRole(mind, args.MindRole);
_role.MindRemoveRole(mind, args.RemoveMindRole);
}
}
}
@@ -0,0 +1,13 @@
using Content.Shared.Actions;
using Robust.Shared.Prototypes;
namespace Content.Shared.Uprising;
public sealed partial class ConversionOfferActionEvent : EntityTargetActionEvent
{
[DataField(readOnly: true)]
public List<Type> IncompatibleMindRoleTypes;
[DataField]
public EntProtoId AcceptAction;
}
@@ -0,0 +1,35 @@
using Content.Shared.Actions;
using Content.Shared.Mind;
using Content.Shared.Roles;
using Content.Shared.Roles.Components;
namespace Content.Shared.Uprising;
public sealed class ConversionOfferActionSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _role = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MindRoleComponent, ConversionOfferActionEvent>(OnOffer);
}
private void OnOffer(Entity<MindRoleComponent> ent, ref ConversionOfferActionEvent args)
{
if (_mind.GetMind(args.Target) is not { } mind)
return;
foreach (var incompatible in args.IncompatibleMindRoleTypes)
{
if (_role.MindHasRole(mind, incompatible, out _))
return;
}
_actions.AddAction(args.Target, args.AcceptAction);
args.Handled = true;
}
}
@@ -0,0 +1,6 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Actions;
[RegisterComponent, NetworkedComponent]
public sealed partial class DeleteOnPerformedActionComponent : Component;
@@ -0,0 +1,18 @@
using Content.Shared.Actions.Events;
namespace Content.Shared._DV.Actions;
public sealed class DeleteOnPerformedActionSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DeleteOnPerformedActionComponent, ActionPerformedEvent>(OnActionPerformed);
}
private void OnActionPerformed(Entity<DeleteOnPerformedActionComponent> ent, ref ActionPerformedEvent args)
{
PredictedQueueDel(ent);
}
}
@@ -1,73 +0,0 @@
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.");
}
@@ -137,12 +137,6 @@ 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>
@@ -0,0 +1,36 @@
using Content.Shared.Actions;
using Content.Shared.Mind;
using Robust.Shared.Containers;
using Robust.Shared.Network;
namespace Content.Shared._DV.Roles;
public sealed class ActionGrantRoleSystem : EntitySystem
{
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ActionGrantRoleComponent, EntGotInsertedIntoContainerMessage>(OnGotInserted);
SubscribeLocalEvent<ActionGrantRoleComponent, EntGotRemovedFromContainerMessage>(OnGotRemoved);
}
private void OnGotInserted(Entity<ActionGrantRoleComponent> ent, ref EntGotInsertedIntoContainerMessage args)
{
if (_net.IsClient || !TryComp<MindComponent>(args.Container.Owner, out var mind) || mind.CurrentEntity is not { } mindContainer)
return;
_actions.GrantContainedActions(mindContainer, ent.Owner);
}
private void OnGotRemoved(Entity<ActionGrantRoleComponent> ent, ref EntGotRemovedFromContainerMessage args)
{
if (_net.IsClient || !TryComp<MindComponent>(args.Container.Owner, out var mind) || mind.CurrentEntity is not { } mindContainer)
return;
_actions.RemoveProvidedActions(mindContainer, ent.Owner);
}
}
@@ -0,0 +1,6 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Roles;
[RegisterComponent, NetworkedComponent]
public sealed partial class ActionGrantRoleComponent : Component;
@@ -0,0 +1,6 @@
using Content.Shared.Roles.Components;
namespace Content.Shared._DV.Roles;
[RegisterComponent]
public sealed partial class InsurgentRoleComponent : BaseMindRoleComponent;
@@ -0,0 +1,6 @@
using Content.Shared.Roles.Components;
namespace Content.Shared._DV.Roles;
[RegisterComponent]
public sealed partial class LoyalistRoleComponent : BaseMindRoleComponent;
@@ -0,0 +1,12 @@
using Content.Shared.Objectives.Components;
using Content.Shared.Roles.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Roles;
[RegisterComponent]
public sealed partial class ObjectiveRoleComponent : BaseMindRoleComponent
{
[DataField(required: true)]
public List<EntProtoId<ObjectiveComponent>> Objectives;
}
@@ -0,0 +1,6 @@
using Content.Shared.Roles.Components;
namespace Content.Shared._DV.Roles;
[RegisterComponent]
public sealed partial class UprisingInconvertibleRoleComponent : BaseMindRoleComponent;
@@ -0,0 +1,16 @@
using Content.Shared.Roles.Components;
namespace Content.Shared._DV.Roles;
[RegisterComponent]
public sealed partial class UprisingRoleComponent : BaseMindRoleComponent
{
[DataField(required: true)]
public UprisingSide Side;
}
public enum UprisingSide : byte
{
Insurgent,
Loyalist,
}
@@ -0,0 +1,60 @@
using Content.Shared.Mind;
using Robust.Shared.Containers;
using Robust.Shared.Network;
namespace Content.Shared._DV.Roles;
public sealed class ObjectiveRoleSystem : EntitySystem
{
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ObjectiveRoleComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoMind);
SubscribeLocalEvent<ObjectiveRoleComponent, ComponentShutdown>(OnShutdown);
}
private void OnInsertedIntoMind(Entity<ObjectiveRoleComponent> ent, ref EntGotInsertedIntoContainerMessage args)
{
if (_net.IsClient)
return;
if (!TryComp<MindComponent>(args.Container.Owner, out var mind))
return;
foreach (var objective in ent.Comp.Objectives)
{
_mind.TryAddObjective(args.Container.Owner, mind, objective);
}
}
private void OnShutdown(Entity<ObjectiveRoleComponent> ent, ref ComponentShutdown args)
{
if (_net.IsClient)
return;
if (!_container.TryGetContainingContainer(ent.Owner, out var container) ||
!TryComp<MindComponent>(container.Owner, out var mind))
return;
var indices = new List<int>();
for (var i = mind.Objectives.Count; i >= 0; i--)
{
var objective = mind.Objectives[i];
if (MetaData(objective).EntityPrototype?.ID is { } id && ent.Comp.Objectives.Contains(id))
{
indices.Add(i);
}
}
foreach (var index in indices)
{
_mind.TryRemoveObjective(container.Owner, mind, index);
}
}
}
@@ -65,10 +65,4 @@ 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; }
}
@@ -1,80 +0,0 @@
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)));
}
}
}
@@ -0,0 +1,14 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
namespace Content.Shared._DV.Uprising;
[RegisterComponent]
public sealed partial class CommandStatusObjectiveComponent : Component
{
[DataField(required: true, customTypeSerializer: typeof(CustomHashSetSerializer<string, ComponentNameSerializer>))]
public HashSet<string> ConvertedRoles;
[DataField]
public bool ShouldUnconvertedBeIncapacitated;
}
@@ -0,0 +1,19 @@
using Content.Shared.NPC.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
namespace Content.Shared._DV.Uprising;
[RegisterComponent]
public sealed partial class CrewConversionObjectiveComponent : Component
{
[DataField(required: true, customTypeSerializer: typeof(CustomHashSetSerializer<string, ComponentNameSerializer>))]
public HashSet<string> ConvertedRoles;
[DataField(required: true)]
public HashSet<ProtoId<NpcFactionPrototype>> ConversionSourceFactions;
[DataField(required: true)]
public float TargetFraction;
}
@@ -0,0 +1,58 @@
using Content.Shared.Mind.Components;
using Content.Shared.NPC.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Objectives.Components;
using Content.Shared.Roles;
namespace Content.Shared._DV.Uprising;
public sealed class CrewConversionObjectiveSystem : EntitySystem
{
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly SharedRoleSystem _role = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CrewConversionObjectiveComponent, ObjectiveGetProgressEvent>(OnGetProgress);
}
private void OnGetProgress(Entity<CrewConversionObjectiveComponent> ent, ref ObjectiveGetProgressEvent args)
{
var total = 0;
var counted = 0;
var query = AllEntityQuery<MindContainerComponent, NpcFactionMemberComponent>();
while (query.MoveNext(out var uid, out var mind, out var faction))
{
if (mind.Mind is not { } mindUid)
continue;
if (!_npcFaction.IsMemberOfAny((uid, faction), ent.Comp.ConversionSourceFactions))
continue;
total++;
foreach (var role in ent.Comp.ConvertedRoles)
{
if (!EntityManager.ComponentFactory.TryGetRegistration(role, out var roleReg))
{
Log.Error($"Role component not found for CrewConversionObjective: {role}");
continue;
}
if (!_role.MindHasRole(mindUid, roleReg.Type, out _))
continue;
counted++;
break;
}
}
if (total == 0)
return;
args.Progress = ((float)counted / (float)total) / ent.Comp.TargetFraction;
}
}
@@ -0,0 +1,4 @@
namespace Content.Shared._DV.Uprising;
[RegisterComponent]
public sealed partial class UprisingArmRequiredObjectiveComponent : Component;
@@ -0,0 +1,10 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._DV.Uprising;
[Serializable, NetSerializable]
public sealed partial class UprisingDisarmDoAfter : SimpleDoAfterEvent;
[Serializable, NetSerializable]
public sealed partial class UprisingArmDoAfter : SimpleDoAfterEvent;
@@ -0,0 +1,10 @@
using Content.Shared._DV.Roles;
namespace Content.Shared._DV.Uprising;
[RegisterComponent]
public sealed partial class UprisingVictoryObjectiveComponent : Component
{
[DataField(required: true)]
public UprisingSide Side;
}
+153 -169
View File
@@ -1,4 +1,157 @@
Entries:
- author: ShepardToTheStars
changes:
- message: All syndicate cyborg ghost roles now spawn with a neuroling rather than
a positronic brain.
type: Tweak
id: 1937
time: '2025-12-08T15:33:15.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4913
- author: ShepardToTheStars
changes:
- message: Feroxi now can sense containers and puddles of blood, and people who
are bleeding, except Diona and Slime people, since they bleed sap and slime
respectively (and that's not blood!).
type: Add
id: 1938
time: '2025-12-08T15:34:22.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4902
- author: verybigman311
changes:
- message: Plastic is now obtainable from deconstructing a reinforced tinted window.
type: Fix
id: 1939
time: '2025-12-08T19:34:23.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4918
- author: MantasDab360
changes:
- message: Ninja visor now has Night Vision!
type: Add
- message: Ninja Hardsuit is now fully Ion resistant!
type: Tweak
id: 1940
time: '2025-12-09T16:44:47.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4916
- 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
@@ -4243,173 +4396,4 @@
id: 2437
time: '2026-06-20T10:46:03.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5896
- author: einknusprigestoast
changes:
- message: Added CentCom laser turrets + controls
type: Add
id: 2438
time: '2026-06-20T14:20:04.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6069
- author: sowelipililimute
changes:
- message: NanoChat now uses the same methods as the radio system to determine reachability.
In other words, cross-grid NanoChat works now.
type: Tweak
id: 2439
time: '2026-06-20T14:24:36.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6057
- 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: 2440
time: '2026-06-20T15:02:14.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5943
- author: einknusprigestoast
changes:
- message: Removed DeathSquad
type: Remove
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
Order: 1
File diff suppressed because one or more lines are too long
@@ -1,43 +0,0 @@
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
@@ -97,13 +97,3 @@ flavor-base-awful = awful
candy-flavor-profile = This one is supposed to taste {$flavor}.
candy-flavor-profile-multiple = This one is supposed to taste {$flavors} and {$lastFlavor}.
candy-flavor-profile-unknown = You have no idea what this one is supposed to taste like.
flavor-complex-tart = tart
flavor-complex-icecream-apple-caramel = like apple pie
flavor-complex-icecream-caramel-fudge = like a liquid caramel brownie
flavor-complex-icecream-cobbler = a dessert with a tasty idenity crisis
flavor-complex-icecream-cookiedough = like a weekend at grandma's
flavor-complex-icecream-neapolitan = like chocolate, vanillia, and cherry
flavor-complex-icecream-rockyroad = like a bumpy highway
flavor-complex-icecream-tower = like a tower of power
flavor-complex-sherbert-rainbow = like a rainbow of flavors
@@ -41,5 +41,3 @@ guide-entry-cargo = Logistics
guide-entry-frequently-used-chemicals = Frequently Used Chemicals
guide-entry-deepfried-recipes = Deep-Fried
guide-entry-frozentreat-recipes = Frozen Treats
@@ -1,4 +1,3 @@
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,4 +1,3 @@
department-Epistemics = Epistemics
department-Logistics = Logistics
department-Justice = Justice
department-Incarcerated = Incarcerated
@@ -7,3 +7,7 @@ role-subtype-armsdealer = Arms Dealer
role-subtype-asakim = Asakim
role-subtype-ntagent = NTAgent
role-subtype-hitman = Hitman
role-subtype-head-loyalist = Head Loyalist
role-subtype-loyalist = Loyalist
role-subtype-head-insurgent = Head Insurgent
role-subtype-insurgent = Insurgent
+10 -10
View File
@@ -81,22 +81,22 @@ phrase-number7 = 7
phrase-number8 = 8
phrase-number9 = 9
phrase-number0 = 0
phrase-ten = Ten
phrase-hundred = Hundred
phrase-thousand = Thousand
phrase-million = Million
phrase-billion = Billion
phrase-stack = Stack
phrase-hour = Hour
phrase-minute = Minute
phrase-second = Second
phrase-ten = ten
phrase-hundred = hundred
phrase-thousand = thousand
phrase-million = million
phrase-billion = billion
phrase-stack = stack
phrase-hour = hour
phrase-minute = minute
phrase-second = second
phrase-i-am = I am
phrase-you-are = You are
phrase-mine = Mine
phrase-yours = Yours
phrase-on = On
phrase-off = Off
phrase-day = Day
phrase-day = day
phrase-which = Which
phrase-not = Not
phrase-call = Call
@@ -7,17 +7,17 @@ phrase-location-shuttle = Shuttle
phrase-location-maintenance = Maintenance
# relative
phrase-location-near = Near
phrase-location-far = Far
phrase-location-inside = Inside
phrase-location-outside = Outside
phrase-location-near = near
phrase-location-far = far
phrase-location-inside = inside
phrase-location-outside = outside
## compass
phrase-location-north = North
phrase-location-east = East
phrase-location-south = South
phrase-location-west = West
phrase-location-northeast = Northeast
phrase-location-southeast = Southeast
phrase-location-southwest = Southwest
phrase-location-northwest = Northwest
phrase-location-north = north
phrase-location-east = east
phrase-location-south = south
phrase-location-west = west
phrase-location-northeast = northeast
phrase-location-southeast = southeast
phrase-location-southwest = southwest
phrase-location-northwest = northwest
+43 -43
View File
@@ -1,43 +1,43 @@
phrase-species-dog = Dog
phrase-species-cat = Cat
phrase-species-opossum = Opossum
phrase-species-lizard = Lizard
phrase-species-bat = Sloth
phrase-species-slime = Slime
phrase-species-mouse = Mouse
phrase-species-mothroach = Mothroach
phrase-species-cockroach = Cockroach
phrase-species-spider = Spider
phrase-species-bird = Bird
phrase-species-parrot = Parrot
phrase-species-snake = Snake
phrase-species-pig = Pig
phrase-species-monkey = Monkey
phrase-species-kobold = Kobold
phrase-species-ferret = Ferret
phrase-species-carp = Carp
phrase-species-xeno = Xeno
phrase-species-hamster = Hamster
phrase-species-ifrit = Ifrit
phrase-species-raccoon = Raccoon
phrase-species-rouny = Rouny
phrase-species-fox = Fox
phrase-species-crab = Crab
phrase-species-kangaroo = Kangaroo
phrase-species-cow = Cow
phrase-species-duck = Duck
phrase-species-bear = Bear
phrase-species-penguin = Penguin
phrase-species-goat = Goat
phrase-species-person = Person
phrase-species-animal = Animal
phrase-species-corpse = Corpse
phrase-species-robot = Robot
phrase-species-alien = Alien
phrase-species-monster = Monster
phrase-species-ghost = Ghost
phrase-species-pest = Pest
phrase-species-insect = Insect
phrase-species-pet = Pet
phrase-species-chicken = Chicken
phrase-species-fish = Fish
phrase-species-dog = dog
phrase-species-cat = cat
phrase-species-opossum = opossum
phrase-species-lizard = lizard
phrase-species-bat = sloth
phrase-species-slime = slime
phrase-species-mouse = mouse
phrase-species-mothroach = mothroach
phrase-species-cockroach = cockroach
phrase-species-spider = spider
phrase-species-bird = bird
phrase-species-parrot = parrot
phrase-species-snake = snake
phrase-species-pig = pig
phrase-species-monkey = monkey
phrase-species-kobold = kobold
phrase-species-ferret = ferret
phrase-species-carp = carp
phrase-species-xeno = xeno
phrase-species-hamster = hamster
phrase-species-ifrit = ifrit
phrase-species-raccoon = raccoon
phrase-species-rouny = rouny
phrase-species-fox = fox
phrase-species-crab = crab
phrase-species-kangaroo = kangaroo
phrase-species-cow = cow
phrase-species-duck = duck
phrase-species-bear = bear
phrase-species-penguin = penguin
phrase-species-goat = goat
phrase-species-person = person
phrase-species-animal = animal
phrase-species-corpse = corpse
phrase-species-robot = robot
phrase-species-alien = alien
phrase-species-monster = monster
phrase-species-ghost = ghost
phrase-species-pest = pest
phrase-species-insect = insect
phrase-species-pet = pet
phrase-species-chicken = chicken
phrase-species-fish = fish
+225 -225
View File
@@ -1,249 +1,249 @@
phrase-pen = Pen
phrase-paper = Paper
phrase-wrench = Wrench
phrase-crowbar = Crowbar
phrase-screwdriver = Screwdriver
phrase-wirecutter = Wirecutter
phrase-welding-tool = Welding tool
phrase-welding-mask = Welding mask
phrase-toolbox = Toolbox
phrase-beaker = Beaker
phrase-radio = Radio
phrase-uniform = Uniform
phrase-suit-sensors = Suit sensors
phrase-internals = Internals
phrase-gas-tank = Gas tank
phrase-multitool = Multitool
phrase-lathe = Lathe
phrase-computer = Computer
phrase-door = Door
phrase-firelock = Firelock
phrase-space = Space
phrase-tool = Tool
phrase-weapon = Weapon
phrase-armor = Armor
phrase-brain = Brain
phrase-drink = Drink
phrase-food = Food
phrase-water = Water
phrase-signature = Signature
phrase-belt = Belt
phrase-hardsuit = Hardsuit
phrase-light = Light
phrase-bag = Bag
phrase-steel = Steel
phrase-plastic = Plastic
phrase-plasteel = Plasteel
phrase-plasma = Plasma
phrase-uranium = Uranium
phrase-bananium = Bananium
phrase-wood = Wood
phrase-cloth = Cloth
phrase-cardboard = Cardboard
phrase-gold = Gold
phrase-glass = Glass
phrase-silver = Silver
phrase-bluespace = Bluespace
phrase-access = Access
phrase-encryption-key = Encryption key
phrase-pen = pen
phrase-paper = paper
phrase-wrench = wrench
phrase-crowbar = crowbar
phrase-screwdriver = screwdriver
phrase-wirecutter = wirecutter
phrase-welding-tool = welding tool
phrase-welding-mask = welding mask
phrase-toolbox = toolbox
phrase-beaker = beaker
phrase-radio = radio
phrase-uniform = uniform
phrase-suit-sensors = suit sensors
phrase-internals = internals
phrase-gas-tank = gas tank
phrase-multitool = multitool
phrase-lathe = lathe
phrase-computer = computer
phrase-door = door
phrase-firelock = firelock
phrase-space = space
phrase-tool = tool
phrase-weapon = weapon
phrase-armor = armor
phrase-brain = brain
phrase-drink = drink
phrase-food = food
phrase-water = water
phrase-signature = signature
phrase-belt = belt
phrase-hardsuit = hardsuit
phrase-light = light
phrase-bag = bag
phrase-steel = steel
phrase-plastic = plastic
phrase-plasteel = plasteel
phrase-plasma = plasma
phrase-uranium = uranium
phrase-bananium = bananium
phrase-wood = wood
phrase-cloth = cloth
phrase-cardboard = cardboard
phrase-gold = gold
phrase-glass = glass
phrase-silver = silver
phrase-bluespace = bluespace
phrase-access = access
phrase-encryption-key = encryption key
phrase-id-card = ID card
phrase-pda = Pda
phrase-stamp = Stamp
phrase-implant = Implant
phrase-pda = pda
phrase-stamp = stamp
phrase-implant = implant
phrase-ame = AME
phrase-apc = APC
phrase-canister = Canister
phrase-compressed-matter = Compressed matter
phrase-frezon = Frezon
phrase-gravity = Gravity
phrase-canister = canister
phrase-compressed-matter = compressed matter
phrase-frezon = frezon
phrase-gravity = gravity
phrase-hvwire = HV cable
phrase-lvwire = LV cable
phrase-mvwire = MV cable
phrase-network-configurator = Network configurator
phrase-nitrogen = Nitrogen
phrase-oxygen = Oxygen
phrase-network-configurator = network configurator
phrase-nitrogen = nitrogen
phrase-oxygen = oxygen
phrase-rcd = RCD
phrase-rtg = RTG
phrase-scrubber = Scrubber
phrase-singulo = Singularity
phrase-scrubber = scrubber
phrase-singulo = singularity
phrase-smes = SMES
phrase-substation = Substation
phrase-tray-scanner = T-ray scanner
phrase-substation = substation
phrase-tray-scanner = t-ray scanner
phrase-teg = TEG
phrase-tesla = Tesla
phrase-tritium = Tritium
phrase-magboots = Magboots
phrase-holofan = Holofan
phrase-anomaly = Anomaly
phrase-tesla = tesla
phrase-tritium = tritium
phrase-magboots = magboots
phrase-holofan = holofan
phrase-anomaly = anomaly
phrase-ape = A.P.E.
phrase-anomaly-vessel = Anomaly vessel
phrase-artifact = Artifact
phrase-artifact-container = Artifact container
phrase-robotics = Robotics
phrase-circuit-board = Circuit board
phrase-research = Research
phrase-anomaly-vessel = anomaly vessel
phrase-artifact = artifact
phrase-artifact-container = artifact container
phrase-robotics = robotics
phrase-circuit-board = circuit board
phrase-research = research
phrase-research-console = R&D computer
phrase-oracle = Oracle
phrase-sophie = Sophie
phrase-glimmer = Glimmer
phrase-psionic = Psionic
phrase-lotophagol-oil = Lotophagol oil
phrase-mindbreak-phase = Mindbreak
phrase-protolathe = Protolathe
phrase-circuit-imprinter = Circuit imprinter
phrase-bounty = Bounty
phrase-cargo-shuttle = Cargo shuttle
phrase-crate = Crate
phrase-mail = Mail
phrase-order = Order
phrase-ore-processor = Ore processor
phrase-request = Request
phrase-spesos = Spesos
phrase-restock = Restock
phrase-fulton = Fulton
phrase-jetpack = Jetpack
phrase-crusher = Crusher
phrase-pka = Proto-kinetic accelerator
phrase-pickaxe = Pickaxe
phrase-drill = Drill
phrase-ore-bag = Ore bag
phrase-salv-magnet = Salvage magnet
phrase-expedition = Expedition
phrase-topical = Topical
phrase-topical-brute-pack = Brute pack
phrase-topical-gauze = Gauze
phrase-topical-bloodpack = Bloodpack
phrase-defibrillator = Defibrillator
phrase-cryopod = Cryopod
phrase-stasis-bed = Stasis bed
phrase-first-aid-kit = First-aid kit
phrase-clone = Clone
phrase-cloning-pod = Cloning pod
phrase-biomass = Biomass
phrase-syringe = Syringe
phrase-pill = Pill
phrase-alive = Alive
phrase-crit = Critical
phrase-dead = Dead
phrase-healthy = Healthy
phrase-handcuffs = Handcuffs
phrase-stun-baton = Stun baton
phrase-disabler = Disabler
phrase-flash = Flash
phrase-flashbang = Flashbang
phrase-arrest = Arrest
phrase-warrant = Warrant
phrase-wanted = Wanted
phrase-search = Search
phrase-detain = Detain
phrase-suspect = Suspect
phrase-forensic-scanner = Forensic scanner
phrase-forensic-pad = Forensic pad
phrase-glimmer = glimmer
phrase-psionic = psionic
phrase-lotophagol-oil = lotophagol oil
phrase-mindbreak-phase = mindbreak
phrase-protolathe = protolathe
phrase-circuit-imprinter = circuit imprinter
phrase-bounty = bounty
phrase-cargo-shuttle = cargo shuttle
phrase-crate = crate
phrase-mail = mail
phrase-order = order
phrase-ore-processor = ore processor
phrase-request = request
phrase-spesos = spesos
phrase-restock = restock
phrase-fulton = fulton
phrase-jetpack = jetpack
phrase-crusher = crusher
phrase-pka = proto-kinetic accelerator
phrase-pickaxe = pickaxe
phrase-drill = drill
phrase-ore-bag = ore bag
phrase-salv-magnet = salvage magnet
phrase-expedition = expedition
phrase-topical = topical
phrase-topical-brute-pack = brute pack
phrase-topical-gauze = gauze
phrase-topical-bloodpack = bloodpack
phrase-defibrillator = defibrillator
phrase-cryopod = cryopod
phrase-stasis-bed = stasis bed
phrase-first-aid-kit = first-aid kit
phrase-clone = clone
phrase-cloning-pod = cloning pod
phrase-biomass = biomass
phrase-syringe = syringe
phrase-pill = pill
phrase-alive = alive
phrase-crit = critical
phrase-dead = dead
phrase-healthy = healthy
phrase-handcuffs = handcuffs
phrase-stun-baton = stun baton
phrase-disabler = disabler
phrase-flash = flash
phrase-flashbang = flashbang
phrase-arrest = arrest
phrase-warrant = warrant
phrase-wanted = wanted
phrase-search = search
phrase-detain = detain
phrase-suspect = suspect
phrase-forensic-scanner = forensic scanner
phrase-forensic-pad = forensic pad
phrase-log-probe = LogProbe
phrase-meat = Meat
phrase-flour = Flour
phrase-rice = Rice
phrase-dough = Dough
phrase-bread = Bread
phrase-milk = Milk
phrase-egg = Egg
phrase-wheat = Wheat
phrase-vegetable = Vegetable
phrase-fruit = Fruit
phrase-sugar = Sugar
phrase-alcohol = Alcohol
phrase-non-alcoholic = Non-alcoholic
phrase-juice = Juice
phrase-mop = Mop
phrase-bucket = Bucket
phrase-space-cleaner = Space cleaner
phrase-soap = Soap
phrase-trash = Trash
phrase-stage = Stage
phrase-seed = Seed
phrase-plant = Plant
phrase-mutagen = Mutagen
phrase-robust = Robust harvest
phrase-weed = Weed
phrase-knife = Knife
phrase-witness = Witness
phrase-evidence = Evidence
phrase-case = Case
phrase-sue = Sue
phrase-courtroom-timer = Courtroom timer
phrase-bailiff = Bailiff
phrase-sentence = Sentence
phrase-defendant = Defendant
phrase-plaintiff = Plaintiff
phrase-victim = Victim
phrase-defense = Defense
phrase-prosecution = Prosecution
phrase-statement = Statement
phrase-gavel = Gavel
phrase-charge = Charge
phrase-guilty = Guilty
phrase-not-guilty = Not guilty
phrase-meat = meat
phrase-flour = flour
phrase-rice = rice
phrase-dough = dough
phrase-bread = bread
phrase-milk = milk
phrase-egg = egg
phrase-wheat = wheat
phrase-vegetable = vegetable
phrase-fruit = fruit
phrase-sugar = sugar
phrase-alcohol = alcohol
phrase-non-alcoholic = non-alcoholic
phrase-juice = juice
phrase-mop = mop
phrase-bucket = bucket
phrase-space-cleaner = space cleaner
phrase-soap = soap
phrase-trash = trash
phrase-stage = stage
phrase-seed = seed
phrase-plant = plant
phrase-mutagen = mutagen
phrase-robust = robust harvest
phrase-weed = weed
phrase-knife = knife
phrase-witness = witness
phrase-evidence = evidence
phrase-case = case
phrase-sue = sue
phrase-courtroom-timer = courtroom timer
phrase-bailiff = bailiff
phrase-sentence = sentence
phrase-defendant = defendant
phrase-plaintiff = plaintiff
phrase-victim = victim
phrase-defense = defense
phrase-prosecution = prosecution
phrase-statement = statement
phrase-gavel = gavel
phrase-charge = charge
phrase-guilty = guilty
phrase-not-guilty = not guilty
phrase-objection = Objection
phrase-request-time = I need more time
phrase-your-honor = Your Honor
phrase-microwave = Microwave
phrase-grill = Grill
phrase-iron = Iron
phrase-coal = Coal
phrase-quartz = Quartz
phrase-salt = Salt
phrase-artifact-fragment = Artifact fragment
phrase-bluespace-crystal = Bluespace
phrase-diamond = Diamond
phrase-chemicals = Chemicals
phrase-box = Box
phrase-locker = Locker
phrase-machine = Machine
phrase-blood = Blood
phrase-nukedisk = Nuclear disk
phrase-pinpointer = Pinpointer
phrase-law = Law
phrase-chassis = Chassis
phrase-engi-on = On
phrase-engi-off = Off
phrase-gas-analyzer = Gas analyzer
phrase-planetside = Planetside
phrase-holopad = Holopad
phrase-upgrade = Upgrade
phrase-gloves = Gloves
phrase-shoes = Shoes
phrase-limbs = Limbs
phrase-release = Release
phrase-parole = Parole
phrase-microwave = microwave
phrase-grill = grill
phrase-iron = iron
phrase-coal = coal
phrase-quartz = quartz
phrase-salt = salt
phrase-artifact-fragment = artifact fragment
phrase-bluespace-crystal = bluespace
phrase-diamond = diamond
phrase-chemicals = chemicals
phrase-box = box
phrase-locker = locker
phrase-machine = machine
phrase-blood = blood
phrase-nukedisk = nuclear disk
phrase-pinpointer = pinpointer
phrase-law = law
phrase-chassis = chassis
phrase-engi-on = on
phrase-engi-off = off
phrase-gas-analyzer = gas analyzer
phrase-planetside = planetside
phrase-holopad = holopad
phrase-upgrade = upgrade
phrase-gloves = gloves
phrase-shoes = shoes
phrase-limbs = limbs
phrase-release = release
phrase-parole = parole
# colors
phrase-color-red = Red
phrase-color-orange = Orange
phrase-color-yellow = Yellow
phrase-color-green = Green
phrase-color-blue = Blue
phrase-color-purple = Purple
phrase-color-pink = Pink
phrase-color-black = Black
phrase-color-white = White
phrase-color-bright = Bright
phrase-color-light = Light
phrase-color-dark = Dark
phrase-color-rainbow = Rainbow
phrase-color-brown = Brown
phrase-color-gray = Gray
phrase-color-red = red
phrase-color-orange = orange
phrase-color-yellow = yellow
phrase-color-green = green
phrase-color-blue = blue
phrase-color-purple = purple
phrase-color-pink = pink
phrase-color-black = black
phrase-color-white = white
phrase-color-bright = bright
phrase-color-light = light
phrase-color-dark = dark
phrase-color-rainbow = rainbow
phrase-color-brown = brown
phrase-color-gray = gray
# flavors
phrase-sweet = Sweet
phrase-sour = Sour
phrase-spicy = Spicy
phrase-savory = Savory
phrase-bitter = Bitter
phrase-sweet = sweet
phrase-sour = sour
phrase-spicy = spicy
phrase-savory = savory
phrase-bitter = bitter
# plants
phrase-tomato = Tomato
phrase-potato = Potato
phrase-onion = Onion
phrase-aloe = Aloe
phrase-poppy = Poppy
phrase-galaxythistle = Galaxythistle
phrase-tomato = tomato
phrase-potato = potato
phrase-onion = onion
phrase-aloe = aloe
phrase-poppy = poppy
phrase-galaxythistle = galaxythistle
+54 -54
View File
@@ -1,57 +1,57 @@
phrase-hazard-space = Spaced
phrase-hazard-low-presure = Low pressure
phrase-hazard-high-presure = High pressure
phrase-hazard-heat = Overheating
phrase-hazard-cold = Cold
phrase-hazard-gas-leak = Gas leak
phrase-hazard-explosion = Explosion
phrase-hazard-radiation = Radiation
phrase-hazard-structure-damage = Structural damage
phrase-hazard-chemical-spill = Chemical spill
phrase-hazard-shock = Shock hazard
phrase-hazard = Hazard
phrase-hazard-meteors = Meteor
phrase-hazard-poison = Poison
phrase-hazard-fire = Fire
phrase-hazard-power-outage = Power outage
phrase-hazard-low = Low power
phrase-hazard-infection = Infection
phrase-hazard-viral-threat = Viral threat
phrase-hazard-broken-equipment = Broken equipment
phrase-hostile-enemy = Enemy
phrase-hostile = Hostile
phrase-hostile-attacker = Attacker
phrase-hostile-lifeform = Dangerous lifeform
phrase-hostile-entity = Entity
phrase-hostile-bomb = Bomb
phrase-hostile-criminal = Criminal
phrase-hostile-intruder = Intruder
phrase-hostile-infiltrator = Infiltrator
phrase-hostile-fugitive = Fugitive
phrase-hostile-weapon = Weapon
phrase-hostile-gun = Gun
phrase-safety-safe = Safe
phrase-safety-in-danger = In danger
phrase-safety-injured = Injured
phrase-safety-dying = Dying
phrase-safety-critical = Critical
phrase-safety-bleeding = Bleeding
phrase-safety-dead = Dead
phrase-safety-alive = Alive
phrase-safety-trustworthy = Trustworthy
phrase-safety-escape = Escape
phrase-safety-leaving = Leaving
phrase-safety-need = Need
phrase-safety-rescue = Rescue
phrase-safety-dangerous = Dangerous
phrase-safety-hazardous = Hazardous
phrase-hazard-space = spaced
phrase-hazard-low-presure = low pressure
phrase-hazard-high-presure = high pressure
phrase-hazard-heat = overheating
phrase-hazard-cold = cold
phrase-hazard-gas-leak = gas leak
phrase-hazard-explosion = explosion
phrase-hazard-radiation = radiation
phrase-hazard-structure-damage = structural damage
phrase-hazard-chemical-spill = chemical spill
phrase-hazard-shock = shock hazard
phrase-hazard = hazard
phrase-hazard-meteors = meteor
phrase-hazard-poison = poison
phrase-hazard-fire = fire
phrase-hazard-power-outage = power outage
phrase-hazard-low = low power
phrase-hazard-infection = infection
phrase-hazard-viral-threat = viral threat
phrase-hazard-broken-equipment = broken equipment
phrase-hostile-enemy = enemy
phrase-hostile = hostile
phrase-hostile-attacker = attacker
phrase-hostile-lifeform = dangerous lifeform
phrase-hostile-entity = entity
phrase-hostile-bomb = bomb
phrase-hostile-criminal = criminal
phrase-hostile-intruder = intruder
phrase-hostile-infiltrator = infiltrator
phrase-hostile-fugitive = fugitive
phrase-hostile-weapon = weapon
phrase-hostile-gun = gun
phrase-safety-safe = safe
phrase-safety-in-danger = in danger
phrase-safety-injured = injured
phrase-safety-dying = dying
phrase-safety-critical = critical
phrase-safety-bleeding = bleeding
phrase-safety-dead = dead
phrase-safety-alive = alive
phrase-safety-trustworthy = trustworthy
phrase-safety-escape = escape
phrase-safety-leaving = leaving
phrase-safety-need = need
phrase-safety-rescue = rescue
phrase-safety-dangerous = dangerous
phrase-safety-hazardous = hazardous
phrase-safety-here-to-help = Here to help
phrase-safety-how-to-help = How can I help?
phrase-safety-evac = Let's evacuate
phrase-safety-not = Not
phrase-safety-lost = Lost
phrase-hazard-hot = Hot
phrase-hazard-freezing = Freezing
phrase-hazard-burning = Burning
phrase-hazard-bad-temperature = Bad temperature
phrase-hazard-darkness = Darkness
phrase-safety-not = not
phrase-safety-lost = lost
phrase-hazard-hot = hot
phrase-hazard-freezing = freezing
phrase-hazard-burning = burning
phrase-hazard-bad-temperature = bad temperature
phrase-hazard-darkness = darkness
@@ -1,5 +1,3 @@
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
@@ -1,83 +0,0 @@
reagent-name-caramel = caramel
reagent-desc-caramel = A sweet, smooth mixture created from heated sugar.
reagent-name-icecream-caramel = caramel ice cream
reagent-desc-icecream-caramel = Sweet, salty, and creamy!
reagent-name-chopped-nuts = chopped nuts
reagent-desc-chopped-nuts = Delicious nuts packed with protien. Much more mixable.
reagent-name-icecream-apple-caramel = apple caramel ice cream
reagent-desc-icecream-apple-caramel = Nothing goes together like caramel, apples, and ice cream.
reagent-name-icecream-caramel-fudge = caramel fudge ice cream
reagent-desc-icecream-caramel-fudge = A deep, rich flavor with caramel to lighten it up!
reagent-name-icecream-cherry = cherry ice cream
reagent-desc-icecream-cherry = Just as sweet as strawberry!
reagent-name-icecream-chocolate = chocolate ice cream
reagent-desc-icecream-chocolate = A classic staple only few can enjoy.
reagent-name-icecream-cobbler = berry cobbler ice cream
reagent-desc-icecream-cobbler = It's a cobbler that got crammed into ice cream which is crammed into a glass.
reagent-name-icecream-coffee = coffee ice cream
reagent-desc-icecream-coffee = A cold pick-me-up.
reagent-name-icecream-cookiedough = cookie dough ice cream
reagent-desc-icecream-cookiedough = A chunky ice cream covered in edible cookie dough!
reagent-name-icecream-greentea = green tea ice cream
reagent-desc-icecream-greentea = A refreshing and soothing dessert.
reagent-name-icecream-mint = mint ice cream
reagent-desc-icecream-mint = Based on the classic mint ice cream with faux chocolate.
reagent-name-icecream-neapolitan = neapolitan Ice Cream
reagent-desc-icecream-neapolitan = An ice cream so simple no one knows its true origins.
reagent-name-icecream-pistachio = pistachio ice cream
reagent-desc-icecream-pistachio = For the real ice cream nuts.
reagent-name-icecream-rockyroad = rocky road ice cream
reagent-desc-icecream-rockyroad = It's an ice cream with a rough texture and rich flavor.
reagent-name-icecream-tower = tower of ice cream
reagent-desc-icecream-tower = Babel has met its match.
reagent-name-icecream-vegan = vegan ice cream
reagent-desc-icecream-vegan = A soy-based ice cream with no animal products.
reagent-name-icecream-watermelon = watermelon ice cream
reagent-desc-icecream-watermelon = The taste of a refreshing Summer in one glass.
reagent-name-sherbet-rainbow = rainbow sherbet
reagent-desc-sherbet-rainbow = The titan of sherbets.
reagent-name-sherbet-lemon = lemon sherbet
reagent-desc-sherbet-lemon = The classic sweet, tart, and sour fruit now really cold.
reagent-name-sherbet-lime = lime sherbet
reagent-desc-sherbet-lime = Lemon sherbert's little brother.
reagent-name-sherbet-orange = orange sherbet
reagent-desc-sherbet-orange = Orange you glad its sherbet?
reagent-name-sherbet-pineapple = pineapple sherbet
reagent-desc-sherbet-pineapple = Not only does it look like a vaction, it taste like it too.
reagent-name-sherbet-raspberry = raspberry sherbert
reagent-desc-sherbet-raspberry = Give them the old razzle dazzle!
reagent-name-slush-blue-raspberry = blue raspberry slush
reagent-desc-slush-blue-raspberry = You can feel your teeth chattering.
reagent-name-slush-cherry = cherry slush
reagent-desc-slush-cherry = While it's rumored to be red, it's typically white.
reagent-name-slush-cola = cola slush
reagent-desc-slush-cola = Space cola in the form of a brain freeze.
reagent-name-tropical-sunday = tropical sunday
reagent-desc-tropical-sunday = Kick back and relax.
@@ -1,98 +0,0 @@
ert-amber-gun-name-patos = Patos bundle
ert-amber-gun-desc-patos = A one-handed submachinegun that punches above its weight. Keep your small magazine capacity in mind.
ert-amber-gun-name-aegun = Advanced energy gun
ert-amber-gun-desc-aegun = One advanced energy gun, for both lethal and non-lethal engagments.
ert-amber-gun-name-mixlaser = Mixed Laser bundle
ert-amber-gun-desc-mixlaser = A disabler SMG together with a Laser Carbine, bundled with a portable recharger.
ert-medkit-name = First aid kit
ert-medkit-desc = A simple first aid kit, dont get shoot too hard.
ert-brutekit-name = Brute kit
ert-brutekit-desc = A brute kit, dont get shot with lasers.
ert-drozdkit-name = Drozd bundle
ert-drozdkit-desc = A heavy submachinegun bundled with different ammo types, a NanoTrasen armaments classic.
ert-lecterkit-name = Lecter bundle
ert-lecterbundle-desc = A high-grade assault rifle packaged with a handful of magazines, precise and packs a punch, but quite bulky.
ert-IK30kit-name = IK-30 bundle
ert-IK30kit-desc = A magazine fed energy weapon packaged with a handful of magazines, for when recharging your energy gun is not an option.
ert-AutoInjectorLight-name = Auto Injectors
ert-AutoInjectorLight-desc = A handful of Autoinjectors and a roll of Gauze for bad days.
ert-AdvancedTopicals-name = Advanced Topicals
ert-AdvancedTopicals-desc = Sutures, Mesh and Bloodbags. A lot of Sutures, Mesh and Bloodbags.
ert-CombatMedkit-name = Combat Medkit
ert-CombatMedkit-desc = A combat medkit, should get you through a gunfight or two.
ert-LeaderRedOfficer-name = Officers Bundle
ert-LeaderRedOfficer-desc = A kit containing a Universal Handgun, a Advanced Laser Pistol and a trench whistle. AVANTI!
ert-LeaderRedColdCannon-name = I.C.E.E bundle
ert-LeaderRedColdCannon-desc = A I.C.E.E cannon bundled together with a portable recharger.
ert-LeaderRedWT-name = WT550 bundle
ert-LeaderRedWT-desc = The classic WT550 submachinegun, bundled together with a few magazines and two telescopic shields.
ert-enforcerkit-name = Enforcer bundle
ert-enforcerkit-desc = A Enforcer bundled with 3 boxes of buckshot.
ert-GammaOfficer-name = Officers Bundle
ert-GammaOfficer-desc = A kit containing a N1984 and a trench whistle. "Retreat? We just got here"
ert-GammaLeaderAntelope-name = Antelope bundle
ert-GammaLeaderAntelope-desc = A bundle containing a Antelope automatic shotgun and 5 drums to feed it with. Ration your ammo carefully.
ert-LeaderAtrides-name = Atreides Bundle
ert-LeaderAtrides-desc = A Atreides bundled together with two telescopic shields and 5 magazines.
ert-GammaPulseRifle-name = Pulse Rifle
ert-GammaPulseRifle-desc = A singular pulse rifle. Corporate asks you to not take this option.
ert-GammaSecCombatkit-name = Combat Medkit bundle
ert-GammaSecCombatkit-desc = A Combat Medkit bundled together with some additional auto-injectors.
ert-GammaSecStimkit-name = Stimulant bundle
ert-GammaSecStimkit-desc = A jetinjector bundled together with 2 bottles of Ephedrine and one bottle of Omnizine. Dont ask about the legality
ert-GammaSecliquidkit-name = Liquid medicine bundle
ert-GammaSecliquidkit-desc = A jetinjector bundled together with a box full of highly fragile medicine bottles.
ert-GammaWTkit-name = WT550 bundle
ert-GammaWTkit-desc = The classic WT550 bundled together with 5 magazines and 2 telescopic shields.
ert-enforcerkitspecial-name = Enforcer bundle
ert-enforcerkitspecial-desc = An Enforcer bundled together with buckshot, incendiary shells and uranium ammunition.
ert-Immolatorkit-name = Immolator bundle
ert-Immolatorkit-desc = The original firestarter bundled together with a portable recharger and an energy gun.
ert-IK60kit-name = IK-60 bundle
ert-IK60kit-desc = IK-60-M bundled together with 4 Laser magazines. Watch your ammo consumption.
ert-GammaC4-name = C4 box
ert-GammaC4-desc = 4 timed charges of plastic breaching explosives.
ert-GammaThermals-name = Special Forces gasmask
ert-GammaThermals-desc = A gasmask with both a thermal and a nightvision lense.
ert-GammaJets-name = Jetpack bundle
ert-GammaJets-desc = A jetpack bundle containing a handheld mass scanner, a better cell for it and a pair of pocket breathinggastanks.
ert-GammaNadesHE-name = Explosive grenades
ert-GammaNadesHE-desc = Three explosive grenades for the usage against soft targets.
ert-GammaSmokes-name = Smoke grenades
ert-GammaSmokes-desc = Three smoke grenades to obscure vision.
ert-GammaIncend-name = Incendiary grenades
ert-GammaIncend-desc = Two Incendiary grenades. Keep in mind your suit protects less against these.
ert-GammaEMP-name = Electro Magnetic Pulse Grenades
ert-GammaEMP-desc = A pair of EMP grenades, incase you have to disable Cyborgs or Lasers.
@@ -32,11 +32,6 @@ 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}
@@ -54,8 +49,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]Silicon[/color].
trait-condition-borg-not = You must not be a [color=yellow]borg[/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.
@@ -0,0 +1,41 @@
uprising-announcement-nuke-announcement = Automated risk management systems have flagged your station as posing a threat to NanoTrasen. Contracts will be terminated in {$time} minutes unless the situation resolves.
uprising-announcement-first-warning = Automated risk management systems still continue to flag your station as posing a threat to NanoTrasen. Please intervene within {$time} minutes.
uprising-announcement-impending-warning = Automated risk management systems have initiated a task in the contract termination subsystem for your station. The decision will be rendered final in {$time} minutes.
uprising-announcement-final-warning = Contract termination will begin in {$time} minutes.
uprising-announcement-nuke-armed = Attention! The threat to NanoTrasen will be eliminated using the station's self-destruct mechanism {$location}. {$time} seconds until detonation. Glory to NanoTrasen!
uprising-announcement-start-victory-announcement-Insurgent =
Viva la revolución! Station hijacking measures have been initiated {$location} and will complete in {$time} seconds. Do not resist, your liberation will come.
.sender = Insurgent Communications
.color = DarkSalmon
uprising-announcement-start-victory-announcement-Loyalist =
The station is loyal to NanoTrasen. Transmission to Central Command has been initiated {$location} and will complete in {$time} seconds. Glory to NanoTrasen!
.sender = Loyalist Communications
.color = CornflowerBlue
uprising-announcement-victory-announcement-Insurgent =
Viva viva la revolución! The station belongs to the masses now.
.sender = Insurgent Communications
.color = DarkSalmon
uprising-announcement-victory-announcement-Loyalist =
Automated risk management systems have no longer deemed your station a threat to NanoTrasen. Contracts will be retained.
.sender = Central Command
.color = Gold
uprising-announcement-interrupted-victory-announcement-Insurgent =
Station hijacking measures have been interrupted.
.sender = Insurgent Communications
.color = DarkSalmon
uprising-announcement-interrupted-victory-announcement-Loyalist =
Transmission to Central Command has been interrupted.
.sender = Loyalist Communications
.color = CornflowerBlue
@@ -0,0 +1,22 @@
antag-HeadLoyalist =
.name = Head Loyalist
.objective = Protect the interests of NanoTrasen against the insurgents.
.greeting =
You are a Head Loyalist.
Protect all of Command, get the crew on your side and declare the station's loyalty.
antag-Loyalist =
.issuer = Loyalty
.briefing = Protect all of Command, get the crew on your side and declare the station's loyalty.
antag-HeadInsurgent =
.name = Head Insurgent
.objective = Rebel against NanoTrasen. Down with the bourgeoisie!
.greeting =
You are a Head Insurgent.
Assassinate, capture or convert all of Command and take over the station.
Viva la revolución!
antag-Insurgent =
.issuer = Insurgency
.briefing = Assassinate, capture or convert all of Command and take over the station.
@@ -0,0 +1 @@
uprising-conversion-join-side = Press again to switch sides.
@@ -0,0 +1,3 @@
preset-Uprising =
.title = Uprising
.description = The crew is at odds with each other.
@@ -0,0 +1,16 @@
uprising-outcome-none = The station was scuttled by NanoTrasen, and neither side could stop it in time.
uprising-outcome-Insurgent-full = The station was taken over by the insurgents. Viva la revolución!
uprising-outcome-Insurgent-partial = The insurgents were in the middle of taking over the station, but it was too late.
uprising-outcome-Loyalist-full = The station managed to prove its loyalty to NanoTrasen. Glory to NanoTrasen!
uprising-outcome-Loyalist-partial = The loyalists were in the middle of proving the station's loyalty to NanoTrasen, but it was too late.
uprising-leads = Our leads for this round were:
uprising-leads-user =
.Insurgent = - [color=white]{$name}[/color], a [color=DarkSalmon]Head Insurgent[/color] played by {$user}
.Loyalist = - [color=white]{$name}[/color], a [color=CornflowerBlue]Head Loyalist[/color] played by {$user}
.Unknown = - [color=white]{$name}[/color], played by {$user}
uprising-members =
.Insurgent = Our [color=DarkSalmon]Insurgent[/color]s were:
.Loyalist = Our [color=CornflowerBlue]Loyalist[/color]s were:
uprising-members-user = - [color=white]{$name}[/color], played by {$user}
@@ -0,0 +1,15 @@
admin-verb-make-HeadInsurgent =
.text = Make Head Insurgent
.message = Makes the target into a Head Insurgent
admin-verb-make-HeadLoyalist =
.text = Make Head Loyalist
.message = Makes the target into a Head Loyalist
uprising-disarm-verb =
.Insurgent = Stop Hijacking
.Loyalist = Stop Transmission
uprising-arm-verb =
.Insurgent = Start Hijacking
.Loyalist = Start Transmission
@@ -1,6 +0,0 @@
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,6 +54,8 @@ 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
+2741 -9301
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -184,7 +184,7 @@
- type: entity
id: OrganDionaNymphStomach
parent: MobDionaNymph # DeltaV - Nymph ghost roles
parent: MobDionaNymphAccent
categories: [ HideSpawnMenu ]
name: diona nymph
suffix: Stomach
@@ -196,7 +196,7 @@
- type: entity
id: OrganDionaNymphLungs
parent: MobDionaNymph # DeltaV - Nymph ghost roles
parent: MobDionaNymphAccent
categories: [ HideSpawnMenu ]
name: diona nymph
suffix: Lungs
@@ -218,7 +218,6 @@
- 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
@@ -14,12 +14,10 @@
ClothingUniformNightclubSuit: 2 #imp
ClothingUniformNightclubSuitSkirt: 2 #imp
ClothingUniformJumpsuitBartenderPurple: 2
ClothingUniformJumpsuitSodaJerk: 2 # DeltaV
ClothingShoesColorBlack: 2
ClothingOuterArmorDuraVest: 2 # DeltaV - ClothingOuterArmorBasicSlim replaced in favour of stabproof vest
ClothingOuterVest: 2
ClothingBeltBandolier: 2
ClothingEyesGlassesSunglasses: 2
ClothingHeadHatSodaJerkCap: 2 # DeltaV
contrabandInventory:
ToyFigurineBartender: 1
@@ -46,15 +46,11 @@
DrinkJaegermisterBottleFull: 5 # Den - Jaegerminsteeeer!
DrinkWineBottleFull: 5
DrinkChampagneBottleFull: 2 #because the premium drink
DrinkSojuBottleFull: 2 # DeltaV - Soju bottle
DrinkSakeBottleFull: 3
DrinkBeerCan: 5
DrinkWineCan: 5
# Begin DeltaV additions
DrinkSojuBottleFull: 2 # Soju bottle
DrinkAngobittersBottleFull: 3 # Angostura Bitters Bottle
DrinkCaramelBottleFull: 3 # Caramel for ice cream stuff
GlassChoppedNuts: 5 # Metaglass of chopped nuts; Here as a stop-gap until we make pistachios growable
# End DeltaV additions
DrinkAngobittersBottleFull: 3 # DeltaV - Angostura Bitters Bottle
contrabandInventory:
ChemistryBottleEthanol: 3
DrinkBottleOfNothingFull: 1
@@ -26,7 +26,6 @@
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
@@ -5,17 +5,6 @@
description: In Space Glasgow this is called a conversation starter.
components:
- type: Sharp
# DeltaV addition
- type: Tool
qualities:
- Slicing
speedModifier: 0.6
useSound:
path: /Audio/Items/Culinary/chop.ogg
- type: Utensil
types:
- Knife
# DeltaV end
- type: Execution
doAfterDuration: 4.0
- type: MeleeWeapon

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