Merge branch 'master' into engitape

Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com>
This commit is contained in:
zelezniciar1 2026-01-27 11:18:13 -05:00 committed by GitHub
commit c051498a2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
116 changed files with 3824 additions and 5133 deletions

View File

@ -565,6 +565,7 @@ namespace Content.Client.Lobby.UI
}
Traits.SetSelectedTraits(selectedTraits);
Traits.UpdateConditions(Profile);
}
// End DeltaV - Traits Integration

View File

@ -1,4 +1,6 @@
using System.Text.RegularExpressions;
using Content.Client._DV.UserInterfaces.BuildInfo; // DeltaV - More info in Escape Menu
using Content.Client.Credits; // DeltaV - More info in Escape Menu
using Content.Client.MainMenu.UI;
using Content.Client.UserInterface.Systems.EscapeMenu;
using Robust.Client;
@ -48,6 +50,8 @@ namespace Content.Client.MainMenu
_mainMenuControl.DirectConnectButton.OnPressed += DirectConnectButtonPressed;
_mainMenuControl.AddressBox.OnTextEntered += AddressBoxEntered;
_mainMenuControl.ChangelogButton.OnPressed += ChangelogButtonPressed;
_mainMenuControl.BuildInfoButton.OnPressed += BuildInfoButtonPressed; // DeltaV - More info in Escape Menu
_mainMenuControl.CreditsButton.OnPressed += CreditsButtonPressed; // DeltaV - More info in Escape Menu
_client.RunLevelChanged += RunLevelChanged;
}
@ -66,6 +70,18 @@ namespace Content.Client.MainMenu
_userInterfaceManager.GetUIController<ChangelogUIController>().ToggleWindow();
}
// Begin DeltaV - More info in Escape Menu
private void BuildInfoButtonPressed(BaseButton.ButtonEventArgs args)
{
_userInterfaceManager.GetUIController<BuildInfoUIController>().ToggleWindow();
}
private void CreditsButtonPressed(BaseButton.ButtonEventArgs args)
{
new CreditsWindow().Open();
}
// End DeltaV - More info in Escape Menu
private void OptionsButtonPressed(BaseButton.ButtonEventArgs args)
{
_userInterfaceManager.GetUIController<OptionsUIController>().ToggleWindow();

View File

@ -48,5 +48,18 @@
<clog:ChangelogButton
Name="ChangelogButton"
Access="Public"/>
<!-- Begin DeltaV - More info in Escape Menu -->
<Button
Name="CreditsButton"
Access="Public"
Text="{Loc server-info-credits-button}"
TextAlign="Center" />
<Button
Name="BuildInfoButton"
Access="Public"
Text="{Loc build-info-button}"
ToolTip="{Loc build-info-tooltip}"
TextAlign="Center" />
<!-- End DeltaV - More info in Escape Menu -->
</BoxContainer>
</Control>

View File

@ -17,5 +17,10 @@
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
<Button Access="Public" Name="DisconnectButton" Text="{Loc 'ui-escape-disconnect'}" />
<Button Access="Public" Name="QuitButton" Text="{Loc 'ui-escape-quit'}" StyleClasses="ButtonColorRed" />
<!-- Begin DeltaV - More info in Escape Menu -->
<PanelContainer StyleClasses="LowDivider" Margin="0 2.5 0 2.5" />
<Button Access="Public" Name="CreditsButton" Text="{Loc server-info-credits-button}" />
<Button Access="Public" Name="BuildInfoButton" Text="{Loc build-info-button}" ToolTip="{Loc build-info-tooltip}" />
<!-- End DeltaV - More info in Escape Menu -->
</BoxContainer>
</ui1:EscapeMenu>

View File

@ -1,4 +1,6 @@
using Content.Client.Gameplay;
using Content.Client._DV.UserInterfaces.BuildInfo; // DeltaV - More info in Escape Menu
using Content.Client.Credits; // DeltaV - More info in Escape Menu
using Content.Client.Gameplay;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Client.UserInterface.Systems.Info;
@ -25,6 +27,7 @@ public sealed class EscapeUIController : UIController, IOnStateEntered<GameplayS
[Dependency] private readonly InfoUIController _info = default!;
[Dependency] private readonly OptionsUIController _options = default!;
[Dependency] private readonly GuidebookUIController _guidebook = default!;
[Dependency] private readonly BuildInfoUIController _buildInfo = default!; // DeltaV - More info in Escape Menu
private Options.UI.EscapeMenu? _escapeWindow;
@ -103,6 +106,19 @@ public sealed class EscapeUIController : UIController, IOnStateEntered<GameplayS
_guidebook.ToggleGuidebook();
};
// Begin DeltaV - More info in Escape Menu
_escapeWindow.BuildInfoButton.OnPressed += _ =>
{
CloseEscapeWindow();
_buildInfo.OpenWindow();
};
_escapeWindow.CreditsButton.OnPressed += _ =>
{
new CreditsWindow().Open();
};
// End DeltaV - More info in Escape Menu
// Hide wiki button if we don't have a link for it.
_escapeWindow.WikiButton.Visible = _cfg.GetCVar(CCVars.InfoLinksWiki) != "";

View File

@ -0,0 +1,51 @@
using Content.Client._DV.Traits.UI;
using Content.Shared._DV.CCVars;
using Content.Shared._DV.Traits;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Client._DV.Traits;
/// <summary>
/// Client system that shows a popup when traits are disabled due to unmet conditions.
/// </summary>
public sealed class DisabledTraitsPopupSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
private DisabledTraitsPopup? _window;
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<DisabledTraitsEvent>(OnDisabledTraits);
}
private void OnDisabledTraits(DisabledTraitsEvent ev)
{
// Don't show if user has opted to skip this popup
if (_cfg.GetCVar(DCCVars.SkipDisabledTraitsPopup))
return;
// Don't show if no traits were actually disabled
if (ev.DisabledTraits.Count == 0)
return;
OpenDisabledTraitsPopup(ev.DisabledTraits);
}
private void OpenDisabledTraitsPopup(Dictionary<ProtoId<TraitPrototype>, List<string>> disabledTraits)
{
// Close existing window if one is open
if (_window != null)
{
_window.Close();
_window = null;
}
_window = new DisabledTraitsPopup(disabledTraits);
_window.OpenCentered();
_window.OnClose += () => _window = null;
}
}

View File

@ -0,0 +1,48 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc disabled-traits-popup-title}"
MinSize="400 200"
SetSize="500 350">
<BoxContainer Orientation="Vertical" Margin="10">
<!-- Header -->
<BoxContainer Orientation="Horizontal" Margin="0 0 0 10">
<TextureRect TexturePath="/Textures/Interface/info.svg.192dpi.png"
TextureScale="0.5 0.5"
VerticalAlignment="Center"
Margin="0 0 10 0" />
<Label Name="TitleLabel"
StyleClasses="LabelHeading"
VAlign="Center" />
</BoxContainer>
<!-- Message -->
<RichTextLabel Name="MessageLabel"
Margin="0 0 0 15" />
<!-- List of disabled traits -->
<Label Text="{Loc disabled-traits-popup-list-header}"
StyleClasses="LabelSubText"
Margin="0 0 0 5" />
<PanelContainer StyleClasses="BackgroundDark" VerticalExpand="True">
<ScrollContainer HScrollEnabled="False" VerticalExpand="True">
<BoxContainer Name="DisabledTraitsContainer"
Orientation="Vertical"
Margin="5" />
</ScrollContainer>
</PanelContainer>
<!-- Footer with checkbox and close button -->
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
<CheckBox Name="SkipCheckBox"
Text="{Loc disabled-traits-popup-skip-checkbox}"
HorizontalAlignment="Center" />
<Button Name="ButtonClose"
Text="{Loc disabled-traits-popup-close-button}"
HorizontalAlignment="Center"
Margin="0 10 0 0"
MinSize="120 0" />
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@ -0,0 +1,101 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._DV.CCVars;
using Content.Shared._DV.Traits;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client._DV.Traits.UI;
[GenerateTypedNameReferences]
public sealed partial class DisabledTraitsPopup : FancyWindow
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private bool _initialSkipState;
public DisabledTraitsPopup(Dictionary<ProtoId<TraitPrototype>, List<string>> disabledTraits)
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
InitializeUI(disabledTraits);
InitializeEvents();
}
private void InitializeUI(Dictionary<ProtoId<TraitPrototype>, List<string>> disabledTraits)
{
TitleLabel.Text = Loc.GetString("disabled-traits-popup-label");
MessageLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(
Loc.GetString("disabled-traits-popup-message")));
_initialSkipState = _cfg.GetCVar(DCCVars.SkipDisabledTraitsPopup);
SkipCheckBox.Pressed = _initialSkipState;
PopulateDisabledTraits(disabledTraits);
}
private void PopulateDisabledTraits(Dictionary<ProtoId<TraitPrototype>, List<string>> disabledTraits)
{
DisabledTraitsContainer.RemoveAllChildren();
foreach (var (traitId, reasons) in disabledTraits)
{
if (!_prototype.TryIndex(traitId, out var trait))
continue;
var container = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Margin = new Thickness(0, 0, 0, 10)
};
// Trait name
var nameLabel = new Label
{
Text = Loc.GetString(trait.Name),
FontColorOverride = Color.FromHex("#ff6b6b"),
StyleClasses = { "LabelSubText" }
};
container.AddChild(nameLabel);
// Reasons why it was disabled
foreach (var reason in reasons)
{
var reasonLabel = new RichTextLabel
{
Margin = new Thickness(10, 2, 0, 0)
};
reasonLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(
Loc.GetString("disabled-traits-popup-reason", ("reason", reason))));
container.AddChild(reasonLabel);
}
DisabledTraitsContainer.AddChild(container);
}
}
private void InitializeEvents()
{
OnClose += SaveSkipState;
ButtonClose.OnPressed += OnClosePressed;
}
private void SaveSkipState()
{
if (SkipCheckBox.Pressed == _initialSkipState)
return;
_cfg.SetCVar(DCCVars.SkipDisabledTraitsPopup, SkipCheckBox.Pressed);
_cfg.SaveToFile();
}
private void OnClosePressed(BaseButton.ButtonEventArgs args)
{
Close();
}
}

View File

@ -1,5 +1,7 @@
using System.Linq;
using Content.Shared._DV.Traits;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
@ -169,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(string? jobId, string? speciesId)
public void UpdateConditions(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
foreach (var (_, entry) in _traitEntries)
{
entry.UpdateConditionsMet(jobId, speciesId);
entry.UpdateConditionsMet(jobId, speciesId, antagPreferences);
}
// Update stats since some traits may have been deselected

View File

@ -7,11 +7,27 @@
HorizontalExpand="True"
Margin="10 8">
<!-- Checkbox / Toggle -->
<CheckBox Name="TraitCheckbox"
StyleClasses="TraitsEntryCheckbox"
VerticalAlignment="Top"
Margin="0 2 10 0"/>
<!-- Toggle Container -->
<BoxContainer Name="ToggleContainer"
Orientation="Horizontal"
VerticalAlignment="Top"
Margin="0 2 10 0"
SetWidth="24">
<!-- Checkbox -->
<CheckBox Name="TraitCheckbox"
StyleClasses="TraitsEntryCheckbox"
VerticalAlignment="Center"/>
<!-- Lock Icon -->
<TextureRect Name="LockIcon"
VerticalAlignment="Center"
HorizontalAlignment="Center"
SetSize="20 20"
Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/VerbIcons/lock.svg.192dpi.png"
Visible="False"/>
</BoxContainer>
<!-- Trait Info -->
<BoxContainer Orientation="Vertical"
@ -31,7 +47,6 @@
<!-- Description -->
<RichTextLabel Name="TraitDescriptionLabel"
StyleClasses="TraitsEntryDescriptionLabel"
HorizontalExpand="True"
Margin="0 4 0 0"/>
</BoxContainer>
</BoxContainer>

View File

@ -1,5 +1,7 @@
using Content.Shared._DV.Traits;
using Content.Shared._DV.Traits.Conditions;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
@ -18,7 +20,8 @@ public sealed partial class TraitEntry : PanelContainer
public event Action<bool>? OnToggled;
public bool IsSelected => TraitCheckbox.Pressed;
public int TraitCost { get; }
public readonly int TraitCost;
public bool MeetsConditions { get; private set; } = true;
private readonly TraitPrototype _trait;
private bool _isUpdating;
@ -88,7 +91,7 @@ public sealed partial class TraitEntry : PanelContainer
/// <summary>
/// Updates whether conditions are met based on current job/species.
/// </summary>
public void UpdateConditionsMet(string? jobId, string? speciesId)
public void UpdateConditionsMet(ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
_failedConditionTooltips.Clear();
MeetsConditions = true;
@ -100,6 +103,9 @@ public sealed partial class TraitEntry : PanelContainer
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,
};
@ -118,39 +124,80 @@ public sealed partial class TraitEntry : PanelContainer
UpdateDisabledState();
}
private bool CheckSpeciesCondition(IsSpeciesCondition condition, string? speciesId)
private bool CheckSpeciesCondition(IsSpeciesCondition condition, ProtoId<SpeciesPrototype>? speciesId)
{
if (string.IsNullOrEmpty(speciesId))
if (!_prototype.TryIndex(speciesId, out var species))
return false;
return speciesId == condition.Species.Id;
return species == condition.Species;
}
private bool CheckJobCondition(HasJobCondition condition, string? jobId)
private bool CheckJobCondition(HasJobCondition condition, ProtoId<JobPrototype>? jobId)
{
if (string.IsNullOrEmpty(jobId))
if (!_prototype.TryIndex(jobId, out var job))
return false;
return jobId == condition.Job;
return job == condition.Job;
}
private bool CheckDepartmentCondition(InDepartmentCondition condition, string? jobId)
private bool CheckDepartmentCondition(InDepartmentCondition condition, ProtoId<JobPrototype>? jobId)
{
if (string.IsNullOrEmpty(jobId))
if (!_prototype.TryIndex(jobId, out var job))
return false;
if (!_prototype.TryIndex(condition.Department, out var department))
return false;
return department.Roles.Contains(jobId);
return department.Roles.Contains(job);
}
private bool CheckAntagEligibleCondition(IsAntagEligibleCondition condition, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
if (antagPreferences is null)
return false;
return antagPreferences.Contains(condition.Antag);
}
private bool CheckAnyOfCondition(AnyOfCondition condition, ProtoId<JobPrototype>? jobId, ProtoId<SpeciesPrototype>? speciesId, IReadOnlySet<ProtoId<AntagPrototype>>? antagPreferences)
{
if (condition.Conditions.Count == 0)
return false;
// Return true if ANY child condition evaluates to true
foreach (var childCondition in condition.Conditions)
{
var result = childCondition switch
{
IsSpeciesCondition speciesCond => CheckSpeciesCondition(speciesCond, speciesId),
HasJobCondition jobCond => CheckJobCondition(jobCond, jobId),
InDepartmentCondition deptCond => CheckDepartmentCondition(deptCond, jobId),
HasCompCondition compCond => !compCond.Invert, // can't check in lobby
AnyOfCondition nestedAnyOf => CheckAnyOfCondition(nestedAnyOf, jobId, speciesId, antagPreferences), // Recursive!
IsAntagEligibleCondition antagEligibleCond => CheckAntagEligibleCondition(antagEligibleCond, antagPreferences),
_ => true,
};
// Apply child's inversion
result ^= childCondition.Invert;
// If any child passes, the AnyOf passes
if (result)
return true;
}
// None of the children passed
return false;
}
private void UpdateDisabledState()
{
TraitCheckbox.Disabled = !MeetsConditions;
if (!MeetsConditions)
{
// Hide checkbox, show lock icon
TraitCheckbox.Visible = false;
LockIcon.Visible = true;
// Deselect if conditions no longer met
if (TraitCheckbox.Pressed)
{
@ -161,7 +208,7 @@ public sealed partial class TraitEntry : PanelContainer
OnToggled?.Invoke(false);
}
// Show why it's disabled
// Add disabled styling
AddStyleClass("TraitsEntryDisabled");
// Update tooltip to show failed conditions
@ -175,8 +222,15 @@ public sealed partial class TraitEntry : PanelContainer
}
else
{
// Show checkbox, hide lock icon
TraitCheckbox.Visible = true;
LockIcon.Visible = false;
// Remove disabled styling - stylesheet restores normal colors
RemoveStyleClass("TraitsEntryDisabled");
UpdateConditionTooltips(); // Reset to normal tooltips
// Reset to normal tooltips
UpdateConditionTooltips();
}
}
@ -187,7 +241,7 @@ public sealed partial class TraitEntry : PanelContainer
if (!MeetsConditions)
{
// Prevent selection if conditions not met
// This shouldn't happen since checkbox is hidden, but just in case
_isUpdating = true;
TraitCheckbox.Pressed = false;
_isUpdating = false;
@ -206,8 +260,6 @@ public sealed partial class TraitEntry : PanelContainer
_isUpdating = false;
}
public bool MeetsConditions { get; private set; } = true;
private void UpdateSelectedStyle()
{
if (TraitCheckbox.Pressed)

View File

@ -77,6 +77,14 @@ public sealed class TraitsSheetlet<T> : Sheetlet<T> where T : PalettedStylesheet
};
entrySelectedBox.SetContentMarginOverride(StyleBox.Margin.All, 0);
var entryDisabledBox = new StyleBoxFlat
{
BackgroundColor = Color.FromHex("#1a1a22"),
BorderColor = Color.FromHex("#2a2a2a"),
BorderThickness = new Thickness(1)
};
entryDisabledBox.SetContentMarginOverride(StyleBox.Margin.All, 0);
var progressBarBgBox = new StyleBoxFlat
{
BackgroundColor = bgDark,
@ -162,7 +170,7 @@ public sealed class TraitsSheetlet<T> : Sheetlet<T> where T : PalettedStylesheet
E<Button>()
.Class("TraitsCategoryHeaderButton")
.Prop(Button.StylePropertyStyleBox, categoryHeaderButtonBox),
.Prop(ContainerButton.StylePropertyStyleBox, categoryHeaderButtonBox),
E<Label>()
.Class("TraitsCategoryExpandIcon")
@ -200,10 +208,15 @@ public sealed class TraitsSheetlet<T> : Sheetlet<T> where T : PalettedStylesheet
.Panel(entryPanelBox),
E<PanelContainer>()
.Class("TraitsEntryPanel")
.Class("TraitsEntrySelected")
.Class("TraitsEntryPanel", "TraitsEntrySelected")
.Panel(entrySelectedBox),
// Disabled entry styling
E<PanelContainer>()
.Class("TraitsEntryPanel", "TraitsEntryDisabled")
.Panel(entryDisabledBox)
.Modulate(new Color(1f, 1f, 1f, 0.5f)),
E<Label>()
.Class("TraitsEntryNameLabel")
.Font(sheet.BaseFont.GetFont(11))

View File

@ -1,6 +1,8 @@
using System.Linq;
using Content.Shared._DV.CCVars;
using Content.Shared._DV.Traits;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
@ -31,6 +33,8 @@ public sealed partial class TraitsTab : BoxContainer
private string _currentSearchText = string.Empty;
private bool _awaitingLayoutUpdate;
private HumanoidCharacterProfile? _profile;
public TraitsTab()
{
RobustXamlLoader.Load(this);
@ -207,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 * percentage);
GlobalPointsBar.SetWidth = (int)((parentWidth - 2 ) * percentage);
_awaitingLayoutUpdate = false;
}
else if (!_awaitingLayoutUpdate)
@ -261,8 +265,25 @@ public sealed partial class TraitsTab : BoxContainer
}
}
/// <summary>
/// Updates all trait conditions based on current job and species.
/// Call this when job or species changes in the character creator.
/// </summary>
/// <param name="profile">Current profile</param>
public void UpdateConditions(HumanoidCharacterProfile profile)
{
_profile = profile;
UpdateAllConditions();
}
private void UpdateAllConditions()
{
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);
}
RecalculateStats();
}

View File

@ -0,0 +1,41 @@
using Content.Client._DV.UserInterfaces.BuildInfo.Controls;
using JetBrains.Annotations;
using Robust.Client.UserInterface.Controllers;
namespace Content.Client._DV.UserInterfaces.BuildInfo;
[UsedImplicitly]
public sealed class BuildInfoUIController : UIController
{
private BuildInfoWindow _changeLogWindow = default!;
public void OpenWindow()
{
EnsureWindow();
_changeLogWindow.OpenCentered();
_changeLogWindow.MoveToFront();
}
private void EnsureWindow()
{
if (_changeLogWindow is { Disposed: false })
{
// return; ADD THIS BACK WHEN I DON'T NEED HOT RELOAD
}
_changeLogWindow = UIManager.CreateWindow<BuildInfoWindow>();
}
public void ToggleWindow()
{
if (_changeLogWindow is { IsOpen: true })
{
_changeLogWindow.Close();
}
else
{
OpenWindow();
}
}
}

View File

@ -0,0 +1,7 @@
<ui:BuildInfoRow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client._DV.UserInterfaces.BuildInfo.Controls"
Orientation="Horizontal">
<Label Name="Label" HorizontalAlignment="Left" />
<Label Name="Value" HorizontalAlignment="Right" HorizontalExpand="True" Margin="4" />
<Button Name="CopyButton" HorizontalAlignment="Right" Text="{Loc ui-options-function-text-copy}" />
</ui:BuildInfoRow>

View File

@ -0,0 +1,38 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._DV.UserInterfaces.BuildInfo.Controls;
[GenerateTypedNameReferences]
public sealed partial class BuildInfoRow : BoxContainer
{
[Dependency] private readonly IClipboardManager _clipboard = default!;
public string? LabelText
{
get => Label.Text;
set => Label.Text = value;
}
public string? ValueText
{
get => Value.Text;
set => Value.Text = value;
}
private void CopyValue(BaseButton.ButtonEventArgs args)
{
if (ValueText is { } value)
_clipboard.SetText(value);
}
public BuildInfoRow()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
CopyButton.OnPressed += CopyValue;
}
}

View File

@ -0,0 +1,14 @@
<buildInfo:BuildInfoWindow xmlns="https://spacestation14.io"
xmlns:buildInfo="clr-namespace:Content.Client._DV.UserInterfaces.BuildInfo.Controls"
Title="{Loc build-info-window-title}"
Resizable="True">
<BoxContainer Orientation="Vertical" Margin="4">
<buildInfo:BuildInfoRow Name="EngineVersion" LabelText="{Loc build-info-engine-version-label}" />
<buildInfo:BuildInfoRow Name="Fork" LabelText="{Loc build-info-fork-label}" />
<buildInfo:BuildInfoRow Name="Version" LabelText="{Loc build-info-version-label}" />
<buildInfo:BuildInfoRow Name="DownloadUrl" LabelText="{Loc build-info-download-url-label}" />
<buildInfo:BuildInfoRow Name="BuildHash" LabelText="{Loc build-info-build-hash-label}" />
<buildInfo:BuildInfoRow Name="BuildManifestUrl" LabelText="{Loc build-info-build-manifest-url-label}" />
<buildInfo:BuildInfoRow Name="BuildManifestHash" LabelText="{Loc build-info-build-manifest-hash-label}" />
</BoxContainer>
</buildInfo:BuildInfoWindow>

View File

@ -0,0 +1,27 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
using Robust.Shared.Configuration;
namespace Content.Client._DV.UserInterfaces.BuildInfo.Controls;
[GenerateTypedNameReferences]
public sealed partial class BuildInfoWindow : DefaultWindow
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
public BuildInfoWindow()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
Version.ValueText = _cfg.GetCVar(CVars.BuildVersion);
EngineVersion.ValueText = _cfg.GetCVar(CVars.BuildEngineVersion);
Fork.ValueText = _cfg.GetCVar(CVars.BuildForkId);
DownloadUrl.ValueText = _cfg.GetCVar(CVars.BuildDownloadUrl);
BuildHash.ValueText = _cfg.GetCVar(CVars.BuildHash);
BuildManifestUrl.ValueText = _cfg.GetCVar(CVars.BuildManifestUrl);
BuildManifestHash.ValueText = _cfg.GetCVar(CVars.BuildManifestHash);
}
}

View File

@ -598,7 +598,7 @@ public sealed partial class TraitSystemTest
BindingFlags.NonPublic | BindingFlags.Instance);
var validTraits = (HashSet<ProtoId<TraitPrototype>>)method?.Invoke(traitSys,
new object[] { player, selectedTraits, null, null, null });
new object[] { player, selectedTraits, null, null, null, null, new Dictionary<ProtoId<TraitPrototype>, List<string>>() });
Assert.Multiple(() =>
{
@ -639,7 +639,7 @@ public sealed partial class TraitSystemTest
BindingFlags.NonPublic | BindingFlags.Instance);
var validTraits = (HashSet<ProtoId<TraitPrototype>>)method?.Invoke(traitSys,
new object[] { player, selectedTraits, null, null, null });
new object[] { player, selectedTraits, null, null, null, null, new Dictionary<ProtoId<TraitPrototype>, List<string>>()});
Assert.That(validTraits?.Count, Is.EqualTo(2), "Should respect category maxTraits limit");
@ -673,7 +673,7 @@ public sealed partial class TraitSystemTest
BindingFlags.NonPublic | BindingFlags.Instance);
var validTraits = (HashSet<ProtoId<TraitPrototype>>)method?.Invoke(traitSys,
new object[] { player, selectedTraits, null, null, null });
new object[] { player, selectedTraits, null, null, null, null, new Dictionary<ProtoId<TraitPrototype>, List<string>>() });
Assert.That(validTraits?.Count, Is.EqualTo(2), "Should respect category maxPoints limit");
@ -706,7 +706,7 @@ public sealed partial class TraitSystemTest
BindingFlags.NonPublic | BindingFlags.Instance);
var validTraits = (HashSet<ProtoId<TraitPrototype>>)method?.Invoke(traitSys,
new object[] { player, selectedTraits, null, null, null });
new object[] { player, selectedTraits, null, null, null, null, new Dictionary<ProtoId<TraitPrototype>, List<string>>() });
Assert.That(validTraits?.Contains("TestTraitHasComp"), Is.True, "Trait with met condition should be valid");
@ -739,7 +739,7 @@ public sealed partial class TraitSystemTest
BindingFlags.NonPublic | BindingFlags.Instance);
var validTraits = (HashSet<ProtoId<TraitPrototype>>)method?.Invoke(traitSys,
new object[] { player, selectedTraits, null, null, null });
new object[] { player, selectedTraits, null, null, null, null, new Dictionary<ProtoId<TraitPrototype>, List<string>>() });
Assert.That(validTraits?.Contains("TestTraitHasComp"),
Is.False,

View File

@ -24,7 +24,7 @@ namespace Content.Server.Chemistry.EntitySystems
/// <seealso cref="ReagentDispenserComponent"/>
/// </summary>
[UsedImplicitly]
public sealed class ReagentDispenserSystem : EntitySystem
public sealed partial class ReagentDispenserSystem : EntitySystem // Frontier - Made Partial, Auto-Label
{
[Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
@ -41,8 +41,8 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<ReagentDispenserComponent, ComponentStartup>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState, after: [typeof(SharedStorageSystem)]);
SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState, after: [typeof(SharedStorageSystem)]);
//SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState, after: [typeof(SharedStorageSystem)]); // Frontier - Auto-Label
SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserSetDispenseAmountMessage>(OnSetDispenseAmountMessage);
@ -51,8 +51,8 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<ReagentDispenserComponent, ReagentDispenserClearContainerSolutionMessage>(OnClearContainerSolutionMessage);
SubscribeLocalEvent<ReagentDispenserComponent, MapInitEvent>(OnMapInit, before: new[] { typeof(ItemSlotsSystem) });
InitializeAutoLabeling(); // Frontier - Auto-Label
}
private void SubscribeUpdateUiState<T>(Entity<ReagentDispenserComponent> ent, ref T ev)
{
UpdateUiState(ent);

View File

@ -6,6 +6,8 @@ using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
@ -45,13 +47,16 @@ public sealed class TraitSystem : EntitySystem
!jobProto.ApplyTraits)
return;
// Get species ID for condition checking
string? speciesId = null;
// Use the species ID from the profile if for some reason we can't get the humanoid appearance
ProtoId<SpeciesPrototype>? speciesId = args.Profile.Species;
if (TryComp<HumanoidAppearanceComponent>(args.Mob, out var humanoid))
speciesId = humanoid.Species;
// Track disabled traits and reasons
var disabledTraits = new Dictionary<ProtoId<TraitPrototype>, List<string>>();
// Validate and collect valid traits
var validTraits = ValidateTraits(args.Mob, args.Profile.TraitPreferences, args.Player, args.JobId, speciesId);
var validTraits = ValidateTraits(args.Mob, args.Profile.TraitPreferences, args.Player, args.JobId, speciesId, args.Profile, disabledTraits);
// Apply valid traits
foreach (var traitId in validTraits)
@ -61,6 +66,12 @@ public sealed class TraitSystem : EntitySystem
ApplyTrait(args.Mob, trait);
}
// Send disabled traits notification to client if any were rejected
if (disabledTraits.Count > 0)
{
RaiseNetworkEvent(new DisabledTraitsEvent(disabledTraits), args.Player);
}
}
/// <summary>
@ -71,7 +82,9 @@ public sealed class TraitSystem : EntitySystem
IReadOnlySet<ProtoId<TraitPrototype>> selectedTraits,
ICommonSession? session,
string? jobId,
string? speciesId)
string? speciesId,
HumanoidCharacterProfile? profile,
Dictionary<ProtoId<TraitPrototype>, List<string>> disabledTraits)
{
var validTraits = new HashSet<ProtoId<TraitPrototype>>();
var totalPoints = 0;
@ -90,6 +103,7 @@ public sealed class TraitSystem : EntitySystem
LogMan = _log,
JobId = jobId,
SpeciesId = speciesId,
Profile = profile,
};
foreach (var traitId in selectedTraits)
@ -100,25 +114,31 @@ public sealed class TraitSystem : EntitySystem
continue;
}
var rejectionReasons = new List<string>();
// Check global trait count limit
if (traitCount >= _maxTraitCount)
{
Log.Warning($"Trait {traitId} rejected: global trait count limit ({_maxTraitCount}) exceeded");
rejectionReasons.Add(Loc.GetString("disabled-traits-reason-global-limit"));
disabledTraits[traitId] = rejectionReasons;
continue;
}
// Check global points limit
if (totalPoints + trait.Cost > _maxTraitPoints)
{
Log.Warning(
$"Trait {traitId} rejected: global points limit ({_maxTraitPoints}) would be exceeded");
Log.Warning($"Trait {traitId} rejected: global points limit ({_maxTraitPoints}) would be exceeded");
rejectionReasons.Add(Loc.GetString("disabled-traits-reason-points-limit"));
disabledTraits[traitId] = rejectionReasons;
continue;
}
// Check category limits
if (!ValidateCategoryLimits(trait, categoryTraitCounts, categoryPointTotals))
if (!ValidateCategoryLimits(trait, categoryTraitCounts, categoryPointTotals, rejectionReasons))
{
Log.Warning($"Trait {traitId} rejected: category limits exceeded");
disabledTraits[traitId] = rejectionReasons;
continue;
}
@ -130,6 +150,11 @@ public sealed class TraitSystem : EntitySystem
if (trait.Conflicts.Contains(validTraitId))
{
Log.Warning($"Trait {traitId} rejected: conflicts with {validTraitId}");
if (_prototype.TryIndex(validTraitId, out var conflictTrait))
{
rejectionReasons.Add(Loc.GetString("disabled-traits-reason-conflict",
("trait", Loc.GetString(conflictTrait.Name))));
}
hasConflict = true;
break;
}
@ -139,18 +164,24 @@ public sealed class TraitSystem : EntitySystem
validTrait.Conflicts.Contains(traitId))
{
Log.Warning($"Trait {traitId} rejected: {validTraitId} conflicts with it");
rejectionReasons.Add(Loc.GetString("disabled-traits-reason-conflict",
("trait", Loc.GetString(validTrait.Name))));
hasConflict = true;
break;
}
}
if (hasConflict)
{
disabledTraits[traitId] = rejectionReasons;
continue;
}
// Check all conditions
if (!CheckConditions(trait, conditionCtx))
if (!CheckConditions(trait, conditionCtx, rejectionReasons))
{
Log.Warning($"Trait {traitId} rejected: conditions not met");
disabledTraits[traitId] = rejectionReasons;
continue;
}
@ -176,7 +207,8 @@ public sealed class TraitSystem : EntitySystem
private bool ValidateCategoryLimits(
TraitPrototype trait,
Dictionary<ProtoId<TraitCategoryPrototype>, int> categoryTraitCounts,
Dictionary<ProtoId<TraitCategoryPrototype>, int> categoryPointTotals)
Dictionary<ProtoId<TraitCategoryPrototype>, int> categoryPointTotals,
List<string> rejectionReasons)
{
if (!_prototype.TryIndex(trait.Category, out var category))
return true; // Unknown category, allow it
@ -186,11 +218,19 @@ public sealed class TraitSystem : EntitySystem
// Check category trait count limit
if (category.MaxTraits.HasValue && currentCount >= category.MaxTraits.Value)
{
rejectionReasons.Add(Loc.GetString("disabled-traits-reason-category-limit",
("category", Loc.GetString(category.Name))));
return false;
}
// Check category points limit
if (category.MaxPoints.HasValue && currentPoints + trait.Cost > category.MaxPoints.Value)
{
rejectionReasons.Add(Loc.GetString("disabled-traits-reason-category-points",
("category", Loc.GetString(category.Name))));
return false;
}
return true;
}
@ -198,12 +238,20 @@ public sealed class TraitSystem : EntitySystem
/// <summary>
/// Checks all conditions on a trait.
/// </summary>
private bool CheckConditions(TraitPrototype trait, TraitConditionContext ctx)
private bool CheckConditions(TraitPrototype trait, TraitConditionContext ctx, List<string> rejectionReasons)
{
foreach (var condition in trait.Conditions)
{
if (!condition.Evaluate(ctx))
return false;
if (condition.Evaluate(ctx))
continue;
// Get human-readable reason from the condition
var tooltip = condition.GetTooltip(ctx.Proto, Loc);
if (!string.IsNullOrEmpty(tooltip))
rejectionReasons.Add(tooltip);
return false;
}
return true;

View File

@ -0,0 +1,22 @@
namespace Content.Server.Chemistry.Components;
/// <summary>
/// Frontier - Extends ReagentDispenserComponent.
///
/// Used primarily for the auto-labeling functionality.
/// </summary>
public sealed partial class ReagentDispenserComponent : Component
{
/// <summary>
/// Returns if the component's entity has the ability to auto-label.
/// </summary>
[DataField]
public bool CanAutoLabel = true;
/// <summary>
/// Returns if the entity has auto-labeling toggled on.
/// Will have no effect if <see cref="CanAutoLabel"/> is false.
/// </summary>
[ViewVariables]
public bool AutoLabelToggle = true;
}

View File

@ -0,0 +1,97 @@
using Content.Server.Chemistry.Components;
using Robust.Shared.Containers;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Verbs;
using Content.Shared.Examine;
using Content.Server.Popups;
using Content.Shared.Labels.EntitySystems;
using Content.Shared.Storage.EntitySystems;
namespace Content.Server.Chemistry.EntitySystems;
public sealed partial class ReagentDispenserSystem : EntitySystem
{
[Dependency] private readonly LabelSystem _label = default!;
[Dependency] private readonly PopupSystem _popup = default!;
private void InitializeAutoLabeling()
{
SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(OnEntInserted, after: [typeof(SharedStorageSystem)]);
SubscribeLocalEvent<ReagentDispenserComponent, GetVerbsEvent<ExamineVerb>>(OnExamineVerb);
SubscribeLocalEvent<ReagentDispenserComponent, ExaminedEvent>(OnExamined);
}
private void OnEntInserted(Entity<ReagentDispenserComponent> ent, ref EntInsertedIntoContainerMessage ev)
{
TryApplyAutoLabel(ent, ev.Entity);
SubscribeUpdateUiState(ent, ref ev);
}
private void OnExamineVerb(Entity<ReagentDispenserComponent> ent, ref GetVerbsEvent<ExamineVerb> args)
{
if (!ent.Comp.CanAutoLabel)
return;
args.Verbs.Add(new ExamineVerb()
{
Act = () =>
{
SetAutoLabel(ent, !ent.Comp.AutoLabelToggle);
},
Text = ent.Comp.AutoLabelToggle ?
Loc.GetString("reagent-dispenser-component-set-auto-label-off-verb")
: Loc.GetString("reagent-dispenser-component-set-auto-label-on-verb"),
Priority = -1, // Not important, low priority.
CloseMenu = true
});
}
private void OnExamined(Entity<ReagentDispenserComponent> ent, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange || !ent.Comp.CanAutoLabel)
return;
if (ent.Comp.AutoLabelToggle)
args.PushMarkup(Loc.GetString("reagent-dispenser-component-examine-auto-label-on"));
else
args.PushMarkup(Loc.GetString("reagent-dispenser-component-examine-auto-label-off"));
}
private void SetAutoLabel(Entity<ReagentDispenserComponent> ent, bool autoLabel)
{
if (!ent.Comp.CanAutoLabel)
return;
ent.Comp.AutoLabelToggle = autoLabel;
var popupMessage = autoLabel ? Loc.GetString("reagent-dispenser-component-verb-auto-label-turn-on")
: Loc.GetString("reagent-dispenser-component-verb-auto-label-turn-off");
_popup.PopupEntity(popupMessage, ent.Owner);
}
private void TryApplyAutoLabel(Entity<ReagentDispenserComponent> dispenser, EntityUid insertedEntity)
{
if (!dispenser.Comp.CanAutoLabel)
return;
if (!dispenser.Comp.AutoLabelToggle)
return;
if (!_solutionContainerSystem.TryGetDrainableSolution(insertedEntity, out _, out var sol))
return;
if (sol.GetPrimaryReagentId() is not { } reagentProtoId)
return;
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagentProtoId.Prototype, out var reagent))
return;
var reagentQuantity = sol.GetReagentQuantity(reagentProtoId);
var totalQuantity = sol.Volume;
if (reagentQuantity == totalQuantity)
_label.Label(insertedEntity, reagent.LocalizedName);
else
_label.Label(insertedEntity, Loc.GetString("reagent-dispenser-component-impure-auto-label", ("reagent", reagent.LocalizedName), ("purity", 100.0f * reagentQuantity / totalQuantity)));
}
}

View File

@ -16,7 +16,7 @@ using Robust.Shared.Utility;
namespace Content.Shared.Clothing.EntitySystems;
public sealed class ToggleableClothingSystem : EntitySystem
public sealed partial class ToggleableClothingSystem : EntitySystem // DeltaV - Made Partial
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _netMan = default!;
@ -140,10 +140,12 @@ public sealed class ToggleableClothingSystem : EntitySystem
|| toggleCom.Container == null)
return;
var parent = Transform(uid).ParentUid; // DeltaV - Allow hats under toggleable helms
if (!_inventorySystem.TryUnequip(Transform(uid).ParentUid, toggleCom.Slot, force: true))
return;
_containerSystem.Insert(uid, toggleCom.Container);
TryEquipUnderClothing(parent, component); // DeltaV - Allow hats under toggleable helms
args.Handled = true;
}
@ -156,11 +158,17 @@ public sealed class ToggleableClothingSystem : EntitySystem
if (_timing.ApplyingState)
return;
var wasAttachedUnequipped = false; // DeltaV - Allow hats under toggleable helms
// If the attached clothing is not currently in the container, this just assumes that it is currently equipped.
// This should maybe double check that the entity currently in the slot is actually the attached clothing, but
// if its not, then something else has gone wrong already...
if (component.Container != null && component.Container.ContainedEntity == null && component.ClothingUid != null)
_inventorySystem.TryUnequip(args.Equipee, component.Slot, force: true, triggerHandContact: true);
wasAttachedUnequipped = _inventorySystem.TryUnequip(args.Equipee, component.Slot, force: true, triggerHandContact: true); // DeltaV - Allow hats under toggleable helms
// DeltaV - If the toggleable helm was uneqipped, try to equip whats in the under clothing container
if (wasAttachedUnequipped && !TryEquipUnderClothing(args.Equipee, component))
TryDropUnderClothing(component);
}
private void OnRemoveToggleable(EntityUid uid, ToggleableClothingComponent component, ComponentRemove args)
@ -240,15 +248,30 @@ public sealed class ToggleableClothingSystem : EntitySystem
return;
var parent = Transform(target).ParentUid;
// Begin DeltaV - Allow hats under toggleable helms!
var wasAttachedUnequipped = false; // We want to track if the toggleable item was unequipped, assume false for now.
if (component.Container.ContainedEntity == null)
_inventorySystem.TryUnequip(user, parent, component.Slot, force: true);
else if (_inventorySystem.TryGetSlotEntity(parent, component.Slot, out var existing))
{
_popupSystem.PopupClient(Loc.GetString("toggleable-clothing-remove-first", ("entity", existing)),
user, user);
}
wasAttachedUnequipped = _inventorySystem.TryUnequip(user, parent, component.Slot, force: true);
else
{
if (_inventorySystem.TryGetSlotEntity(parent, component.Slot, out var existing)
&& !TryStoreUnderClothing(existing.Value, component))
{
_popupSystem.PopupClient(Loc.GetString("toggleable-clothing-remove-first", ("entity", existing)),
user, user);
return;
}
_inventorySystem.TryEquip(user, parent, component.ClothingUid.Value, component.Slot, triggerHandContact: true);
}
// If the toggleable clothing was uneqipped, try to equip whats in the under clothing container
if (wasAttachedUnequipped && !TryEquipUnderClothing(user, parent, component))
TryDropUnderClothing(component);
// END DeltaV
}
private void OnGetActions(EntityUid uid, ToggleableClothingComponent component, GetItemActionsEvent args)
@ -264,6 +287,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
private void OnInit(EntityUid uid, ToggleableClothingComponent component, ComponentInit args)
{
component.Container = _containerSystem.EnsureContainer<ContainerSlot>(uid, component.ContainerId);
component.UnderClothingContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, component.UnderClothingContainerId); // Wayfarer - Allow hats under toggleable helms!
}
/// <summary>

View File

@ -141,6 +141,12 @@ public sealed partial class DCCVars
public static readonly CVarDef<int> MaxTraitPoints =
CVarDef.Create("traits.max_points", 15, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Whether to skip showing the disabled traits popup when spawning.
/// </summary>
public static readonly CVarDef<bool> SkipDisabledTraitsPopup =
CVarDef.Create("traits.skip_disabled_traits_popup", false, CVar.CLIENT | CVar.ARCHIVE);
/*
* Feedback webhook
*/

View File

@ -0,0 +1,27 @@
using Robust.Shared.Containers;
namespace Content.Shared.Clothing.Components;
/// <summary>
/// Extends upstream's ToggleableClothingComponent.
///
/// This portion of the ToggleableClothingComponent stores the clothing item under the toggled piece.
/// Currently only supports a single piece of clothing, but pretty much all entities with ToggleableClothing
/// are just hardsuit helmets.
/// </summary>
public sealed partial class ToggleableClothingComponent : Component
{
public const string DefaultUnderneathClothingContainerId = "toggleable-under-clothing";
/// <summary>
/// The container ID of <see cref="UnderClothingContainer"/>.
/// </summary>
[DataField, AutoNetworkedField]
public string UnderClothingContainerId = DefaultUnderneathClothingContainerId;
/// <summary>
/// The container where the item that the toggled clothing replaced is put.
/// </summary>
[ViewVariables]
public ContainerSlot? UnderClothingContainer;
}

View File

@ -0,0 +1,94 @@
using Content.Shared.Clothing.Components;
namespace Content.Shared.Clothing.EntitySystems;
/// <summary>
/// Extends upstream's ToggleableClothingSystem.
///
/// Provides methods that store and re-equip clothing when toggleable clothing is put on or taken off.
/// Sidenote; god, I hate naming things.
/// </summary>
public sealed partial class ToggleableClothingSystem : EntitySystem
{
/// <summary>
/// Tries to store clothing in <see cref="ToggleableClothingComponent.UnderClothingContainer"/>
/// </summary>
/// <param name="clothing">The clothing to be stored.</param>
/// <param name="component">The ToggleableClothingComponent to store the clothing in.</param>
/// <returns>True if clothing can be inserted and was inserted.</returns>
private bool TryStoreUnderClothing(EntityUid clothing, ToggleableClothingComponent component)
{
if (component.UnderClothingContainer == null)
return false;
// There is already something in there? Either way, return false because we
// expect one thing.
if (component.UnderClothingContainer.ContainedEntity.HasValue)
return false;
return _containerSystem.Insert(clothing, component.UnderClothingContainer);
}
/// <summary>
/// Tries to equip any stored clothing kept in <see cref="ToggleableClothingComponent.UnderClothingContainer"/>.
/// </summary>
/// <param name="actor">The person wearing the ToggleableClothing.</param>
/// <param name="component">The ToggleableClothingComponent to check for an stored items.</param>
/// <returns>True if something was equipped OR if there is nothing to equip.</returns>
private bool TryEquipUnderClothing(EntityUid actor, ToggleableClothingComponent component)
{
return TryEquipUnderClothing(actor, actor, component);
}
/// <summary>
/// Tries to equip any stored clothing kept in <see cref="ToggleableClothingComponent.UnderClothingContainer"/>.
/// </summary>
/// <param name="actor">The person trying to equip the clothing.</param>
/// <param name="target">The person who to equip the clothing on.</param>
/// <param name="component">The ToggleableClothingComponent to check for an stored items.</param>
/// <returns>True if something was equipped OR if there is nothing to equip.</returns>
private bool TryEquipUnderClothing(EntityUid actor, EntityUid target, ToggleableClothingComponent component)
{
// if there is no UnderClothingContainer, then why are we here?
if (component.UnderClothingContainer == null)
return true;
// if nothing is contained so technically dropping nothing counts as a success
if (!component.UnderClothingContainer.ContainedEntity.HasValue)
return true;
return _inventorySystem.TryEquip(actor, target, component.UnderClothingContainer.ContainedEntity.Value, component.Slot, force: true);
}
/// <summary>
/// Tries to equip any stored clothing kept in <see cref="ToggleableClothingComponent.UnderClothingContainer"/>.
/// </summary>
/// <param name="actor">The person trying to equip the clothing.</param>
/// <param name="component">The AttachedClothing of the ToggleableClothing to check for an stored items.</param>
/// <returns>True if something was equipped OR if there is nothing to equip.</returns>
private bool TryEquipUnderClothing(EntityUid actor, AttachedClothingComponent component)
{
if (!TryComp<ToggleableClothingComponent>(component.AttachedUid, out var toggleableComp))
return false;
return TryEquipUnderClothing(actor, toggleableComp);
}
/// <summary>
/// Tries to drop any stored clothing kept in <see cref="ToggleableClothingComponent.UnderClothingContainer"/>.
/// </summary>
/// <param name="component">The ToggleableClothingComponent that is holding the item to be dropped.</param>
/// <returns>True if there is not an item to be dropped OR it was successfully dropped.</returns>
private bool TryDropUnderClothing(ToggleableClothingComponent component)
{
// if there is no UnderClothingContainer, then why are we here?
if (component.UnderClothingContainer == null)
return true;
// if nothing is contained so technically dropping nothing counts as a success
if (!component.UnderClothingContainer.ContainedEntity.HasValue)
return true;
return _containerSystem.TryRemoveFromContainer(component.UnderClothingContainer.ContainedEntity.Value);
}
}

View File

@ -0,0 +1,19 @@
using Content.Shared._DV.Traits.Assorted;
using Content.Shared.Cloning.Events;
namespace Content.Shared._DV.Medical;
public sealed class UncloneableSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<UncloneableComponent, CloningAttemptEvent>(OnCloningAttempt);
}
private void OnCloningAttempt(Entity<UncloneableComponent> ent, ref CloningAttemptEvent args)
{
args.Cancelled = true;
}
}

View File

@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Traits.Assorted;
/// <summary>
/// This entity cannot be cloned but can still be revived by defibrillators.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class UncloneableComponent : Component;

View File

@ -0,0 +1,64 @@
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Traits.Conditions;
/// <summary>
/// Condition that passes if ANY of the child conditions pass.
/// Use this to create "must meet at least one of these requirements" checks.
/// </summary>
public sealed partial class AnyOfCondition : BaseTraitCondition
{
/// <summary>
/// List of conditions to check. Passes if any condition evaluates to true.
/// </summary>
[DataField(required: true)]
public List<BaseTraitCondition> Conditions = new();
protected override bool EvaluateImplementation(TraitConditionContext ctx)
{
// Inversion doesn't make sense for AnyOfCondition - use inverted child conditions instead
if (Invert)
{
throw new InvalidOperationException(
"AnyOfCondition does not support Invert. To require none of the conditions, " +
"invert the individual child conditions instead.");
}
// Empty list should fail
if (Conditions.Count == 0)
return false;
// Return true if ANY condition passes
foreach (var condition in Conditions)
{
if (condition.Evaluate(ctx))
return true;
}
return false;
}
public override string GetTooltip(IPrototypeManager proto, ILocalizationManager loc)
{
if (Conditions.Count == 0)
return string.Empty;
var requirements = new List<string>();
foreach (var condition in Conditions)
{
var tooltip = condition.GetTooltip(proto, loc);
if (!string.IsNullOrEmpty(tooltip))
requirements.Add(tooltip);
}
if (requirements.Count == 0)
return string.Empty;
// AAAAAAAAAAA
var joinedRequirements = string.Join("\n• ", requirements);
// Handle inversion in tooltip (ANY becomes NONE when inverted)
return Loc.GetString("trait-condition-any-of", ("requirements", joinedRequirements));
}
}

View File

@ -1,3 +1,4 @@
using Content.Shared.Preferences;
using JetBrains.Annotations;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
@ -57,4 +58,9 @@ public sealed class TraitConditionContext
/// The species ID of the player, if available.
/// </summary>
public string? SpeciesId { get; init; }
/// <summary>
/// The <see cref="HumanoidCharacterProfile"/> of the player, if available.
/// </summary>
public HumanoidCharacterProfile? Profile { get; init; }
}

View File

@ -15,6 +15,12 @@ public sealed partial class HasCompCondition : BaseTraitCondition
[DataField(required: true, customTypeSerializer: typeof(ComponentNameSerializer))]
public string Component = string.Empty;
/// <summary>
/// The tooltip text to display, if any.
/// </summary>
[DataField]
public LocId? Tooltip;
protected override bool EvaluateImplementation(TraitConditionContext ctx)
{
if (string.IsNullOrEmpty(Component))
@ -35,6 +41,10 @@ public sealed partial class HasCompCondition : BaseTraitCondition
public override string GetTooltip(IPrototypeManager proto, ILocalizationManager loc)
{
// If there's a custom tooltip supplied, use that
if (Tooltip is not null)
return Loc.GetString(Tooltip);
// No tooltip for this condition since we're dealing with comps
return string.Empty;
}

View File

@ -0,0 +1,37 @@
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Traits.Conditions;
/// <summary>
/// Condition that checks if the player has enabled a specific antag preference.
/// </summary>
public sealed partial class IsAntagEligibleCondition : BaseTraitCondition
{
/// <summary>
/// The antag prototype ID to check for eligibility.
/// </summary>
[DataField(required: true)]
public ProtoId<AntagPrototype> Antag;
protected override bool EvaluateImplementation(TraitConditionContext ctx)
{
if (ctx.Profile == null)
return false;
// Check if the player has this antag preference enabled
return ctx.Profile.AntagPreferences.Contains(Antag);
}
public override string GetTooltip(IPrototypeManager proto, ILocalizationManager loc)
{
if (!proto.TryIndex(Antag, out var antagProto))
return string.Empty;
var antagName = loc.GetString(antagProto.Name);
return Invert
? loc.GetString("trait-condition-antag-not", ("antag", antagName))
: loc.GetString("trait-condition-antag-is", ("antag", antagName));
}
}

View File

@ -0,0 +1,17 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._DV.Traits;
/// <summary>
/// Sent from server to client when a player spawns with traits that were disabled due to unmet conditions.
/// </summary>
[Serializable, NetSerializable]
public sealed class DisabledTraitsEvent(Dictionary<ProtoId<TraitPrototype>, List<string>> disabledTraits)
: EntityEventArgs
{
/// <summary>
/// Dictionary mapping disabled trait IDs to lists of reasons why they were disabled.
/// </summary>
public Dictionary<ProtoId<TraitPrototype>, List<string>> DisabledTraits = disabledTraits;
}

View File

@ -1,100 +1,4 @@
Entries:
- author: Proxysseia
changes:
- message: reduced the emote cooldown to 3s from 5s
type: Tweak
id: 1594
time: '2025-08-14T10:25:33.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4185
- author: kotobdev
changes:
- message: Blinking is now an emote, and plays a silly noise.
type: Add
id: 1595
time: '2025-08-14T20:57:10.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4155
- author: Quanteey
changes:
- message: Merged upstream until June 20.
type: Add
id: 1596
time: '2025-08-14T17:21:58.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4142
- author: NoElkaTheGod
changes:
- message: Cosmic cult can now create a multispectral inhibitor, which jams any
radio communication in a large area, by putting a telecommunication server on
a glyph of cessation.
type: Add
id: 1597
time: '2025-08-14T23:51:18.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4181
- author: NoElkaTheGod
changes:
- message: Entropy is now less efficient for progressing the cosmic cult's monument.
type: Tweak
- message: Siphoning entropy now deals it's damage much slower, and the total amount
is randomized to make it less metagameable.
type: Tweak
id: 1598
time: '2025-08-15T06:24:29.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4099
- author: Proxysseia
changes:
- message: IK30 has had its fire rate halved and now requires two hands to fire
type: Tweak
id: 1599
time: '2025-08-15T18:54:13.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4189
- author: NoElkaTheGod
changes:
- message: Cosmic cult win conditions are more fair now. Check PR for details.
type: Tweak
id: 1600
time: '2025-08-16T12:24:12.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4156
- author: Proxysseia
changes:
- message: Laser Rifles now deal more damage
type: Tweak
- message: Laser Cannons now deal more damage
type: Tweak
- message: Laser Rifles and their practice counterparts can be shot more before
needing to recharge
type: Tweak
id: 1601
time: '2025-08-16T18:16:57.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4198
- author: kotobdev
changes:
- message: Mantises now get two mindbreaker pills in their locker.
type: Add
id: 1602
time: '2025-08-17T00:12:24.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4207
- author: Quanteey
changes:
- message: The AI shop is now properly localized
type: Fix
id: 1603
time: '2025-08-17T12:56:47.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4210
- author: Quanteey
changes:
- message: Fixed IK-30 and IK-60 spread glitches when wielded
type: Fix
id: 1604
time: '2025-08-17T21:31:39.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4212
- author: Proxysseia
changes:
- message: Emergency Lasers now come in boxes of 10
type: Tweak
- message: Emergency Lasers now do almost as much damage as a real gun
type: Tweak
id: 1605
time: '2025-08-18T10:13:26.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4197
- author: NoElkaTheGod
changes:
- message: Fixed ghosts not being able to see things like the monument or telegnostic
@ -4477,4 +4381,104 @@
id: 2093
time: '2026-01-23T19:35:58.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5243
- author: MilonPL
changes:
- message: The traits points bar will now show up correctly.
type: Fix
id: 2094
time: '2026-01-24T20:55:31.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5275
- author: MilonPL
changes:
- message: The uncloneable trait no longer prevents your character from getting
revived by defibrillators.
type: Fix
id: 2095
time: '2026-01-24T21:29:58.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5273
- author: Halo3moth
changes:
- message: The energy magnums "magnum" rounds no longer pierce windows
type: Fix
id: 2096
time: '2026-01-25T13:48:54.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5270
- author: Toby222
changes:
- message: Added buttons for build info and credits to the escape menu
type: Tweak
id: 2097
time: '2026-01-25T14:42:09.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5260
- author: MilonPL
changes:
- message: Added the "Marked as Protected" trait which makes you immune to becoming
a target objective.
type: Add
id: 2098
time: '2026-01-25T16:30:18.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5274
- author: AeraAuling
changes:
- message: Shadow damage no longer removes all your blood immediately
type: Tweak
id: 2099
time: '2026-01-25T16:31:20.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5268
- author: ShepardToTheStars
changes:
- message: You can now toggle your hardsuit helmet on over your hat or beret!
type: Add
id: 2100
time: '2026-01-25T16:31:55.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5263
- author: Pharaz4
changes:
- message: Bingus can no longer drink the **ENTIRE** keg.
type: Fix
- message: You can eat rice dishes with chopsticks now.
type: Add
- message: Fixed Smartfridge visuals always showing their wire panels open.
type: Fix
- message: Projectiles and Mr. Butlertion are now insulated against wizardry.
type: Fix
- message: Wording fix to initial infected's prompt. It now states that they can
explicitly kill people.
type: Fix
id: 2101
time: '2026-01-25T16:31:39.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5278
- author: MantasDab360, whatston3, ShepardToTheStars
changes:
- message: Chemical Dispenser can now toggle between auto labeling!
type: Add
id: 2102
time: '2026-01-25T16:46:12.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5261
- author: keekee38
changes:
- message: a new compact submachine gun for security should be shipping in soon!
type: Add
- message: added machine pistol magazines to the security techfab's recipes.
type: Tweak
- message: added rubber SMG mags to the security techfab.
type: Tweak
id: 2103
time: '2026-01-26T10:46:23.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5212
- author: Halo3moth
changes:
- message: Security long coats have been resprited to be more in line with other
security gear. (we ran out of red dye)
type: Tweak
id: 2104
time: '2026-01-27T05:50:41.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5288
- author: EmberAstra
changes:
- message: Plasteel can now be printed at the Engineering Techfab.
type: Add
id: 2105
time: '2026-01-27T16:05:20.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5284
Order: 1

View File

@ -695,4 +695,27 @@
id: 81
time: '2026-01-20T23:30:07.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5241
- author: Colin-Tel
changes:
- message: 'Division: Added AI cameras on their own network, cyborg control computer,
a medical crash cart, and AI restoration computers.'
type: Add
- message: 'Division: Added more weapons to the armory.'
type: Add
- message: 'Division: Salvage''s gun safe is now in the LO office.'
type: Tweak
- message: 'Division: The arrivals area now fits the arrivals shuttle, and both
arrivals and evac have anti-meteor zones put on them.'
type: Tweak
id: 82
time: '2026-01-24T20:16:21.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5271
- author: Fieldcommand
changes:
- message: 'Pebble: CJ has a bathroom, toilets have loot chance, and other small
tweaks.'
type: Tweak
id: 83
time: '2026-01-25T00:13:10.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/5164
Order: 3

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,3 @@
zombie-bioterrorist-role-greeting = You are a syndicate sponsored bioterrorist sent to overtake the station by use of the Romerol virus. Coordinate with your team, get supplies and prepare for your eventual transformation. Death to nanotrasen!
zombie-bioterrorist-romerol-active = The romerol in your blood is now active, you are ready to transform!
zombie-patientzero-role-greeting = You are an initial infected. Get supplies and prepare for your eventual transformation. You may sabotage the station and kill crew before turning. Your goal is to overtake the station while infecting as many people as possible.

View File

@ -2,3 +2,4 @@ trait-category-disabilities = Disabilities
trait-category-medical = Medical
trait-category-mental = Mental
trait-category-accents = Accents
trait-category-meta = Meta

View File

@ -5,6 +5,22 @@ trait-editor-search-placeholder = Search traits...
trait-editor-footer-hint = Hover over traits for details
trait-editor-footer-info = Negative costs grant bonus points
## Disabled Traits Popup
disabled-traits-popup-title = Traits Disabled
disabled-traits-popup-label = Traits Disabled
disabled-traits-popup-message = Some of your selected traits could not be applied because they did not meet the required conditions.
disabled-traits-popup-list-header = The following traits were disabled:
disabled-traits-popup-reason = • {$reason}
disabled-traits-popup-skip-checkbox = Don't show this again
disabled-traits-popup-close-button = Close
## Disabled Traits Reasons
disabled-traits-reason-global-limit = Global trait limit reached
disabled-traits-reason-points-limit = Not enough trait points remaining
disabled-traits-reason-category-limit = Category "{$category}" trait limit reached
disabled-traits-reason-category-points = Category "{$category}" points limit reached
disabled-traits-reason-conflict = Conflicts with selected trait: {$trait}
## Category suffixes
trait-category-traits = {$selected} / {$max} traits
trait-category-traits-unlimited = {$selected} traits
@ -16,6 +32,10 @@ trait-conditions-tooltip = [bold]Requirements:[/bold]
trait-conditions-not-met-tooltip = Requirements not met:
{$requirements}
## Composite conditions
trait-condition-any-of = Any of the following must be true:
• {$requirements}
## Species conditions
trait-condition-species-is = You must be a [color=yellow]{$species}[/color].
trait-condition-species-not = You must not be a [color=yellow]{$species}[/color].
@ -27,3 +47,10 @@ trait-condition-job-not = You must not be a [color={$color}]{$job}[/color].
## Department conditions
trait-condition-department-is = You must be in the [color={$color}]{$department}[/color] department.
trait-condition-department-not = You must not be in the [color={$color}]{$department}[/color] department.
## HasComp borg conditions
trait-condition-borg-not = You must not be a [color=yellow]borg[/color].
# 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.

View File

@ -64,3 +64,6 @@ trait-redshirt-desc =
trait-anglish-name = Anglish tung
trait-anglish-desc = Your tung is of the Saxons, and say unlike others aboard this skyship.
trait-protected-name = Marked as Protected
trait-protected-desc = Due to your position, value, or circumstances, your survival is considered preferable. You won't be targeted by kill objectives.

View File

@ -0,0 +1,10 @@
build-info-window-title = Build Info
build-info-button = Build Info
build-info-tooltip = Metadata about the game you're running
build-info-version-label = Game Version:
build-info-engine-version-label = Engine Version:
build-info-fork-label = Fork ID:
build-info-download-url-label = Client Download:
build-info-build-hash-label = Client Hash:
build-info-build-manifest-url-label = Manifest URL:
build-info-build-manifest-hash-label = Manifest Hash:

View File

@ -0,0 +1,8 @@
# Frontier
reagent-dispenser-component-impure-auto-label = {$reagent} ({$purity}%)
reagent-dispenser-component-set-auto-label-on-verb = Turn on auto-labeler
reagent-dispenser-component-set-auto-label-off-verb = Turn off auto-labeler
reagent-dispenser-component-examine-auto-label-on = The auto-labeler is turned [color=darkgreen]on[/color].
reagent-dispenser-component-examine-auto-label-off = The auto-labeler is turned [color=red]off[/color].
reagent-dispenser-component-verb-auto-label-turn-on = The auto-labeler has been turned on.
reagent-dispenser-component-verb-auto-label-turn-off = The auto-labeler has been turned off.

View File

@ -7,7 +7,8 @@ zombieteors-description = The undead have been unleashed on the station amid a c
zombie-not-enough-ready-players = Not enough players readied up for the game! There were {$readyPlayersCount} players readied up out of {$minimumPlayers} needed. Can't start Zombies.
zombie-no-one-ready = No players readied up! Can't start Zombies.
zombie-patientzero-role-greeting = You are an initial infected. Get supplies and prepare for your eventual transformation. Your goal is to overtake the station while infecting as many people as possible.
# Delta V - Moved to _DV file
# zombie-patientzero-role-greeting = You are an initial infected. Get supplies and prepare for your eventual transformation. Your goal is to overtake the station while infecting as many people as possible.
zombie-healing = You feel a stirring in your flesh
zombie-infection-warning = You feel the zombie virus take hold
zombie-infection-underway = Your blood begins to thicken

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -549,11 +549,11 @@ WeaponSubMachineGunVectorRubber: null
PosterContrabandLustyExomorph: PosterContrabandRealExomorph
PosterContrabandBustyBackdoorExoBabes6: PosterContrabandRouny
# 2025-02-20
MagazineBoxAntiMaterielBig: null
MagazineBoxCaselessRifle10x24: null
MagazineBoxCaselessRifleBig: null
MagazinePistolHighCapacityRubber: null
# 2025-02-20 # DeltaV - We still have these. Migrated to _DV namespace.
#MagazineBoxAntiMaterielBig: null
#MagazineBoxCaselessRifle10x24: null
#MagazineBoxCaselessRifleBig: null
#MagazinePistolHighCapacityRubber: null
# 2025-03-18
EpinephrineChemistryBottle: ChemistryBottleEpinephrine

View File

@ -271,6 +271,7 @@
Piercing: 0.2
Poison: 0.0
Radiation: 0.0
Shadow: 0.0 # DeltaV
Shock: 0.0
Slash: 0.25
Structural: 0.0

View File

@ -162,6 +162,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: entity
parent: ClothingNeckBase
@ -188,6 +189,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: TypingIndicatorClothing
proto: moth
@ -207,6 +209,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: TypingIndicatorClothing
proto: alien

View File

@ -100,6 +100,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
storagebase: !type:Container
ents: []
@ -137,6 +138,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: GroupExamine
- type: Tag
tags:
@ -201,6 +203,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
storagebase: !type:Container
ents: []

View File

@ -1189,6 +1189,7 @@
- type: ContainerContainer # Delta V-Brings back clownsuit but make it make sense
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
#Mime Hardsuit
- type: entity

View File

@ -60,6 +60,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: Tag
tags:
- CorgiWearable
@ -173,6 +174,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: Tag
tags:
- CorgiWearable

View File

@ -136,6 +136,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: ProtectedFromStepTriggers
slots: WITHOUT_POCKET
- type: Tag
@ -255,6 +256,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: ProtectedFromStepTriggers
slots: WITHOUT_POCKET
@ -275,6 +277,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: Construction
graph: ClothingOuterSuitIan
node: suit
@ -308,6 +311,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
- type: entity
parent: ClothingOuterSuitCarp

View File

@ -50,6 +50,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {} # DeltaV - Allow hats under toggleable clothing
storagebase: !type:Container
ents: []

View File

@ -12,7 +12,9 @@
- type: Edible
trash:
- FoodBowlBig
utensil: Spoon
utensil:
- Spoon
- Fork # Delta V - Mainly so people can eat rice dishes with a chopstick
- type: SolutionContainerManager
solutions:
food:

View File

@ -25,26 +25,6 @@
- type: Appearance
# Boxes
- type: entity
parent: BaseMagazineBoxAntiMateriel
id: MagazineBoxAntiMaterielBig
name: ammunition box (.60 anti-materiel)
components:
- type: BallisticAmmoProvider
capacity: 30
proto: CartridgeAntiMateriel
- type: Sprite
layers:
- state: base-b
map: ["enum.GunVisualLayers.Base"]
- state: magb-1
map: ["enum.GunVisualLayers.Mag"]
- type: MagazineVisuals
magState: magb
steps: 4
zeroVisible: false
- type: Appearance
- type: entity
parent: BaseMagazineBoxAntiMateriel
id: MagazineBoxAntiMateriel

View File

@ -25,46 +25,6 @@
- type: Appearance
# Boxes
- type: entity
parent: BaseMagazineBoxCaselessRifle
id: MagazineBoxCaselessRifle10x24
name: ammunition box (.25 caseless)
components:
- type: BallisticAmmoProvider
capacity: 200
proto: CartridgeCaselessRifle
- type: Sprite
layers:
- state: base-10x24
map: ["enum.GunVisualLayers.Base"]
- state: mag10-1
map: ["enum.GunVisualLayers.Mag"]
- type: MagazineVisuals
magState: mag10
steps: 4
zeroVisible: false
- type: Appearance
- type: entity
parent: BaseMagazineBoxCaselessRifle
id: MagazineBoxCaselessRifleBig
name: ammunition box (.25 caseless)
components:
- type: BallisticAmmoProvider
capacity: 200
proto: CartridgeCaselessRifle
- type: Sprite
layers:
- state: base-b
map: ["enum.GunVisualLayers.Base"]
- state: magb-1
map: ["enum.GunVisualLayers.Mag"]
- type: MagazineVisuals
magState: magb
steps: 4
zeroVisible: false
- type: Appearance
- type: entity
parent: BaseMagazineBoxCaselessRifle
id: MagazineBoxCaselessRifle

View File

@ -45,7 +45,7 @@
whitelist:
tags:
- CartridgePistol
capacity: 16
capacity: 20 # Delta-V, was 16
- type: Item
size: Small
- type: ContainerContainer
@ -247,6 +247,7 @@
- type: entity
id: MagazinePistolHighCapacityEmpty
name: machine pistol magazine (.35 auto any)
description: 20-round double-stack single feed magazine for machine pistols. #Delta V
suffix: empty
parent: BaseMagazinePistolHighCapacity
components:
@ -268,6 +269,7 @@
- type: entity
id: MagazinePistolHighCapacity
name: machine pistol magazine (.35 auto)
description: 20-round double-stack single feed magazine for machine pistols. Intended to hold general-purpose kinetic ammunition. #Delta V
parent: BaseMagazinePistolHighCapacity
components:
- type: BallisticAmmoProvider
@ -292,6 +294,7 @@
- type: entity
id: MagazinePistolHighCapacityPractice
name: machine pistol magazine (.35 auto practice)
description: 20-round double-stack single feed magazine for machine pistols. Intended to hold non-harmful chalk ammunition. #Delta V
parent: BaseMagazinePistolHighCapacity
components:
- type: BallisticAmmoProvider
@ -313,20 +316,6 @@
- state: inhand-right-stripe
color: "#ea5800"
- type: entity
id: MagazinePistolHighCapacityRubber
name: machine pistol magazine (.35 auto rubber)
parent: BaseMagazinePistolHighCapacity
components:
- type: BallisticAmmoProvider
proto: CartridgePistolRubber # DeltaV: upstream still has this for some reason but not using rubber
- type: Sprite
layers:
- state: rubber
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- type: entity
id: MagazinePistolSubMachineGun
name: SMG magazine (.35 auto)
@ -443,4 +432,3 @@
- state: inhand-left-mag
right:
- state: inhand-right-mag

View File

@ -1366,6 +1366,16 @@
types:
Heat: 24
Piercing: 11
- type: Fixtures # Delta V - ensures that it doesnt pierce windows
fixtures:
projectile:
shape:
!type:PhysShapeAabb
bounds: "-0.1,-0.1,0.1,0.1"
hard: true
mask:
- Impassable
- BulletImpassable
- type: entity
name: magnum window-piercing bolt

View File

@ -24,6 +24,8 @@
- TegGenerator
- TegCirculator
- XenoArtifact
- Roboisseur # Delta V - Butlerton is scary
- Projectile
event: !type:ChangeComponentsSpellEvent
toAdd:
- type: Animate

View File

@ -19,13 +19,8 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot
showEnts: false
occludes: true
ent: null
toggleable-under-clothing: !type:ContainerSlot {}
storagebase: !type:Container
showEnts: false
occludes: true
ents: []
- type: entity
parent: ClothingOuterHardsuitSyndie

View File

@ -43,14 +43,9 @@
clothingPrototype: ClothingHeadHatHoodWinterMailCarrier
- type: ContainerContainer
containers:
storagebase: !type:Container
showEnts: False
occludes: True
ents: []
toggleable-clothing: !type:ContainerSlot
showEnts: False
occludes: True
ent: null
toggleable-under-clothing: !type:ContainerSlot {}
storagebase: !type:Container
- type: Sprite
sprite: Nyanotrasen/Clothing/OuterClothing/WinterCoats/mail_winter_coat.rsi
- type: Clothing

View File

@ -174,6 +174,7 @@
- type: entity
id: XenoArtifactEffectUniversalIntercom
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-intercom
@ -205,6 +206,7 @@
- type: entity
id: XenoArtifactBecomeRandomInstrument
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-become-instrument
@ -220,6 +222,7 @@
- type: entity
id: XenoArtifactStorage
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-storage
@ -236,6 +239,7 @@
- type: entity
id: XenoArtifactPhasing
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-phasing
@ -245,6 +249,7 @@
- type: entity
id: XenoArtifactWandering
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-wandering
@ -260,6 +265,7 @@
- type: entity
id: XenoArtifactSolutionStorage
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-solution-storage
@ -293,6 +299,7 @@
- type: entity
id: XenoArtifactSpeedUp
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-speedup
@ -306,6 +313,7 @@
- type: entity
id: XenoArtifactDrill
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-drill
@ -324,6 +332,7 @@
- type: entity
id: XenoArtifactGenerateEnergy
parent: BaseOneTimeXenoArtifactEffect # todo - increment power, but only once per node
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-generator
@ -342,6 +351,7 @@
- type: entity
id: XenoArtifactGun
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gun
@ -375,6 +385,7 @@
- type: entity
id: XenoArtifactGhost
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-ghost
@ -397,6 +408,7 @@
- type: entity
id: XenoArtifactOmnitool
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-omnitool
@ -446,6 +458,7 @@
- type: entity
id: XenoArtifactEffectBadFeeling
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-feeling
@ -478,6 +491,7 @@
- type: entity
id: XenoArtifactEffectGoodFeeling
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-feeling
@ -509,6 +523,7 @@
- type: entity
id: XenoArtifactEffectJunkSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-junk
@ -534,6 +549,7 @@
- type: entity
id: XenoArtifactEffectLightFlicker
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-flicker
@ -543,6 +559,7 @@
- type: entity
id: XenoArtifactPotassiumWave
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-potassium
@ -572,6 +589,7 @@
- type: entity
id: XenoArtifactFloraSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-flora
@ -589,6 +607,7 @@
- type: entity
id: XenoArtifactChemicalPuddle
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
# todo: make description say what exact chemical is produced, maybe add mixes into possible chemicals
@ -628,6 +647,7 @@
- type: entity
id: XenoArtifactThrowThingsAround
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-throw
@ -637,6 +657,7 @@
- type: entity
id: XenoArtifactColdWave
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-cold
@ -647,6 +668,7 @@
- type: entity
id: XenoArtifactHeatWave
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-heat
@ -657,6 +679,7 @@
- type: entity
id: XenoArtifactFoamMild
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
# todo: separate in 1 for each chemical for description? actually sounds like a very good idea
@ -680,6 +703,7 @@
- type: entity
id: XenoArtifactRandomInstrumentSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-create-instrument
@ -702,6 +726,7 @@
- type: entity
id: XenoArtifactMonkeySpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-monkey
@ -727,6 +752,7 @@
- type: entity
id: XenoArtifactRadioactive
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-radioactive-mild
@ -742,6 +768,7 @@
- type: entity
id: XenoArtifactChargeBattery
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-charge
@ -754,6 +781,7 @@
- type: entity
id: XenoArtifactKnock
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-knock
@ -764,6 +792,7 @@
- type: entity
id: XenoArtifactMagnet
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-magnet
@ -780,6 +809,7 @@
- type: entity
id: XenoArtifactMagnetNegative
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-magnet
@ -796,6 +826,7 @@
- type: entity
id: XenoArtifactStealth
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-stealth
@ -811,6 +842,7 @@
- type: entity
id: XenoArtifactRareMaterialSpawn
parent: BaseXenoArtifactEffect # todo: splice into different well-named effects, amounts should reflect how rare material is
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-rare-materials
@ -843,6 +875,7 @@
- type: entity
id: XenoArtifactRareMaterialSpawnSilver
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-rare-materials
@ -868,6 +901,7 @@
- type: entity
id: XenoArtifactRareMaterialSpawnPlasma
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-plasma
@ -893,6 +927,7 @@
- type: entity
id: XenoArtifactRareMaterialSpawnGold
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gold
@ -918,6 +953,7 @@
- type: entity
id: XenoArtifactRareMaterialSpawnUranium
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-uranium
@ -943,6 +979,7 @@
- type: entity
id: XenoArtifactAngryCarpSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-carp
@ -968,6 +1005,7 @@
- type: entity
id: XenoArtifactFaunaSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-fauna
@ -1017,6 +1055,7 @@
- type: entity
id: XenoArtifactHostileFaunaSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
description: Create hostile fauna
components:
- type: XenoArtifactNode
@ -1044,6 +1083,7 @@
- type: entity
id: XenoArtifactCashSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-cash
@ -1075,6 +1115,7 @@
- type: entity
id: XenoArtifactShatterWindows
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-shatter
@ -1096,6 +1137,7 @@
- type: entity
id: XenoArtifactFoamGood
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-foam-medicine
@ -1119,6 +1161,7 @@
- type: entity
id: XenoArtifactFoamDangerous
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-foam-poison
@ -1143,6 +1186,7 @@
- type: entity
id: XenoArtifactPuddleRare
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-puddle-rare
@ -1175,6 +1219,7 @@
- type: entity
id: XenoArtifactAnomalySpawn
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-anomaly
@ -1192,6 +1237,7 @@
- type: entity
id: XenoArtifactIgnite
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-ignite
@ -1205,6 +1251,7 @@
- type: entity
id: XenoArtifactTeleport
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-teleport
@ -1214,6 +1261,7 @@
- type: entity
id: XenoArtifactEmp
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-emp
@ -1228,6 +1276,7 @@
- type: entity
id: XenoArtifactPolyMonkey
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-poly-monkey
@ -1237,6 +1286,7 @@
- type: entity
id: XenoArtifactPolyLizard
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-poly-lizard
@ -1247,6 +1297,7 @@
- type: entity
id: XenoArtifactPolyLuminous
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-poly-luminous
@ -1257,6 +1308,7 @@
- type: entity
id: XenoArtifactRadioactiveStrong
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-radioactive-strong
@ -1272,6 +1324,7 @@
- type: entity
id: XenoArtifactMaterialSpawnGlass
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-glass
@ -1289,6 +1342,7 @@
- type: entity
id: XenoArtifactMaterialSpawnSteel
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-steel
@ -1306,6 +1360,7 @@
- type: entity
id: XenoArtifactMaterialSpawnPlastic
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-plastic
@ -1323,6 +1378,7 @@
- type: entity
id: XenoArtifactPortal
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-portal
@ -1332,6 +1388,7 @@
- type: entity
id: XenoArtifactArtifactSpawn
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-artifact
@ -1354,6 +1411,7 @@
- type: entity
id: XenoArtifactShuffle
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-shuffle
@ -1367,6 +1425,7 @@
- type: entity
id: XenoArtifactHealAll
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-heal
@ -1385,6 +1444,7 @@
- type: entity
id: XenoArtifactTesla
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-tesla
@ -1403,6 +1463,7 @@
- type: entity
id: XenoArtifactSingularity
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-singularity
@ -1421,6 +1482,7 @@
- type: entity
id: XenoArtifactExplosionScary
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-explosion-radioactive
@ -1438,6 +1500,7 @@
- type: entity
id: XenoArtifactBoom
parent: BaseOneTimeXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-explosion-default
@ -1454,6 +1517,7 @@
- type: entity
id: XenoArtifactEffectCreationGasPlasma
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gas-plasma
@ -1465,6 +1529,7 @@
- type: entity
id: XenoArtifactEffectCreationGasTritium
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gas-tritium
@ -1476,6 +1541,7 @@
- type: entity
id: XenoArtifactEffectCreationGasAmmonia
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gas-ammonia
@ -1487,6 +1553,7 @@
- type: entity
id: XenoArtifactEffectCreationGasFrezon
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gas-frezon
@ -1498,6 +1565,7 @@
- type: entity
id: XenoArtifactEffectCreationGasNitrousOxide
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gas-nitrous
@ -1509,6 +1577,7 @@
- type: entity
id: XenoArtifactEffectCreationGasCarbonDioxide
parent: BaseXenoArtifactEffect
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails # DeltaV
specificTip: xenoarch-effect-tip-specific-gas-carbon

View File

@ -147,3 +147,15 @@
amount: 2
- id: BoxShotgunPractice
amount: 2
- type: entity
parent: GunSafeBaseSecure
id: GunSafeSubMachineGunPatos
name: patos safe
components:
- type: StorageFill
contents:
- id: WeaponSubMachineGunPatos
amount: 2
- id: MagazinePistolHighCapacity
amount: 4

View File

@ -14,6 +14,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot { }
toggleable-under-clothing: !type:ContainerSlot {}
- type: Armor
modifiers:
coefficients:
@ -96,6 +97,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {}
- type: Tag
tags:
- CorgiWearable

View File

@ -22,6 +22,7 @@
- type: ContainerContainer
containers:
toggleable-clothing: !type:ContainerSlot {}
toggleable-under-clothing: !type:ContainerSlot {}
storagebase: !type:Container
ents: []

View File

@ -81,3 +81,14 @@
children:
- id: SalvageGunSafeRevolverSnub
- id: SalvageGunSafeVulcan
- type: entity
parent: BaseGunSafeSpawner
id: SubMachineGunSafeSpawner
suffix: SubMachineGun
components:
- type: EntityTableSpawner
table: !type:GroupSelector
children:
- id: GunSafeSubMachineGunDrozd
- id: GunSafeSubMachineGunPatos

View File

@ -1,3 +1,23 @@
- type: entity
parent: BaseMagazineBoxAntiMateriel
id: MagazineBoxAntiMaterielBig
name: ammunition box (.60 anti-materiel)
components:
- type: BallisticAmmoProvider
capacity: 30
proto: CartridgeAntiMateriel
- type: Sprite
layers:
- state: base-b
map: ["enum.GunVisualLayers.Base"]
- state: magb-1
map: ["enum.GunVisualLayers.Mag"]
- type: MagazineVisuals
magState: magb
steps: 4
zeroVisible: false
- type: Appearance
- type: entity
parent: MagazineBoxAntiMateriel
id: MagazineBoxLMGAntiMateriel

View File

@ -0,0 +1,39 @@
- type: entity
parent: BaseMagazineBoxCaselessRifle
id: MagazineBoxCaselessRifle10x24
name: ammunition box (.25 caseless)
components:
- type: BallisticAmmoProvider
capacity: 200
proto: CartridgeCaselessRifle
- type: Sprite
layers:
- state: base-10x24
map: ["enum.GunVisualLayers.Base"]
- state: mag10-1
map: ["enum.GunVisualLayers.Mag"]
- type: MagazineVisuals
magState: mag10
steps: 4
zeroVisible: false
- type: Appearance
- type: entity
parent: BaseMagazineBoxCaselessRifle
id: MagazineBoxCaselessRifleBig
name: ammunition box (.25 caseless)
components:
- type: BallisticAmmoProvider
capacity: 200
proto: CartridgeCaselessRifle
- type: Sprite
layers:
- state: base-b
map: ["enum.GunVisualLayers.Base"]
- state: magb-1
map: ["enum.GunVisualLayers.Mag"]
- type: MagazineVisuals
magState: magb
steps: 4
zeroVisible: false
- type: Appearance

View File

@ -1,4 +1,48 @@
# .35 auto
- type: entity
id: MagazinePistolHighCapacityRubber
name: machine pistol magazine (.35 auto rubber)
description: 20-round double-stack single feed magazine for machine pistols. Intended to hold less-lethal rubber ammunition. #Delta V
parent: BaseMagazinePistolHighCapacity
components:
- type: BallisticAmmoProvider
proto: CartridgePistolRubber
- type: Sprite
layers:
- state: rubber
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- type: entity
id: MagazinePistolHighCapacityIncendiary
name: machine pistol magazine (.35 auto incendiary)
description: 20-round double-stack single feed magazine for machine pistols. Intended to hold self-igniting incendiary ammunition. #Delta V
parent: BaseMagazinePistolHighCapacity
components:
- type: BallisticAmmoProvider
proto: CartridgePistolIncendiary
- type: Sprite
layers:
- state: base
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- type: entity
id: MagazinePistolHighCapacityUranium
name: machine pistol magazine (.35 auto uranium)
description: 20-round double-stack single feed magazine for machine pistols. Intended to hold exotic uranium-core ammunition. #Delta V
parent: BaseMagazinePistolHighCapacity
components:
- type: BallisticAmmoProvider
proto: CartridgePistolUranium
- type: Sprite
layers:
- state: uranium
map: ["enum.GunVisualLayers.Base"]
- state: mag-1
map: ["enum.GunVisualLayers.Mag"]
- type: entity
parent: BaseMagazinePistol

View File

@ -48,3 +48,66 @@
steps: 1
zeroVisible: true
- type: Appearance
- type: entity
name: Patos
parent: BaseWeaponSubMachineGun
id: WeaponSubMachineGunPatos
description: A compact and lightweight personal defense weapon, with an aggressive compensator. Uses .35 auto ammo.
components:
- type: Sprite
sprite: _DV/Objects/Weapons/Guns/SMGs/patos.rsi
layers:
- state: base
map: ["enum.GunVisualLayers.Base"]
- state: mag-0
map: ["enum.GunVisualLayers.Mag"]
- type: Item
size: Normal
shape:
- 0,0,1,1
- type: Clothing
sprite: _DV/Objects/Weapons/Guns/SMGs/patos.rsi
slots:
- Back
- suitStorage
- type: Gun
minAngle: 4
maxAngle: 24
fireRate: 7
angleIncrease: 4.5
angleDecay: 24
selectedMode: FullAuto
availableModes:
- SemiAuto
- FullAuto
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/smg.ogg
defaultDirection: 1, 0
- type: ChamberMagazineAmmoProvider
soundRack:
path: /Audio/Weapons/Guns/Cock/smg_cock.ogg
- type: ItemSlots
slots:
gun_magazine:
name: Magazine
startingItem: MagazinePistolHighCapacity
insertSound: /Audio/Weapons/Guns/MagIn/smg_magin.ogg
ejectSound: /Audio/Weapons/Guns/MagOut/smg_magout.ogg
priority: 2
whitelist:
tags:
- MagazinePistolHighCapacity
whitelistFailPopup: gun-magazine-whitelist-fail
gun_chamber:
name: Chamber
startingItem: CartridgePistol
priority: 1
whitelist:
tags:
- CartridgePistol
- type: MagazineVisuals
magState: mag
steps: 5
zeroVisible: true
- type: Appearance

View File

@ -5,9 +5,13 @@
description: A refrigerated storage unit for keeping items cold and fresh.
components:
- type: StationAiWhitelist
- type: Vocalizer
hideChat: true
- type: VocalizerRequiresPower
- type: DatasetVocalizer
dataset: SmartFridgeAds
- type: Speech
- type: Appearance
- type: Sprite
sprite: Structures/Machines/smartfridge.rsi
snapCardinals: true

View File

@ -93,6 +93,7 @@
- type: Edible
edible: Drink
solution: tank
destroyOnEmpty: false
- type: Spillable
solution: tank
- type: DumpableSolution

View File

@ -4,6 +4,7 @@
id: EngineeringStaticDeltaV
recipes:
- HolotapeProjector
- SheetPlasteel
## Dynamic

View File

@ -17,6 +17,7 @@
recipes:
- MagazineBoxSpecialPractice
- SpeedLoaderSpecialPractice
- MagazinePistolHighCapacityPractice
- type: latheRecipePack
id: SecurityAmmoStaticDeltaV
@ -29,6 +30,7 @@
- BoxShellTranquilizer
- MagazineShotgunBeanbag
- MagazineLaser
- MagazinePistolHighCapacity
- type: latheRecipePack
id: SecurityShotgunDrumsStatic
@ -50,6 +52,8 @@
- MagazineBoxLightRifleRubber
- MagazineBoxRifleRubber
- MagazineBoxSpecialRubber
- MagazinePistolSubMachineGunRubber
- MagazinePistolHighCapacityRubber
- type: latheRecipePack
parent: SecurityDisablers
@ -69,6 +73,8 @@
- MagazineBoxSpecialHoly
- MagazineBoxSpecialMindbreaker
- GamblagatorCapacitor
- MagazinePistolHighCapacityUranium
- MagazinePistolHighCapacityIncendiary
- type: latheRecipePack
parent: SecurityHardsuits

View File

@ -59,3 +59,11 @@
parent: BaseFauxTileRecipe
id: FauxTileMowedAstroSnow
result: FloorTileItemMowedAstroSnow
- type: latheRecipe
id: SheetPlasteel
result: SheetPlasteel
completetime: 1
materials:
Steel: 200
Plasma: 100

View File

@ -133,6 +133,22 @@
Steel: 25
Plastic: 500
- type: latheRecipe
parent: BaseAmmoRecipe
id: MagazinePistolSubMachineGunRubber
result: MagazinePistolSubMachineGunRubber
materials:
Steel: 25
Plastic: 360
- type: latheRecipe
parent: BaseAmmoRecipe
id: MagazinePistolHighCapacityRubber
result: MagazinePistolHighCapacityRubber
materials:
Steel: 25
Plastic: 240
- type: latheRecipe
parent: BaseAmmoRecipe
id: SpeedLoaderMagnumRubber
@ -169,6 +185,40 @@
materials:
Plastic: 750
# Machine pistol mags, aside from rubber
- type: latheRecipe
parent: BaseAmmoRecipe
id: MagazinePistolHighCapacity
result: MagazinePistolHighCapacity
materials:
Steel: 210
- type: latheRecipe
parent: BaseAmmoRecipe
id: MagazinePistolHighCapacityPractice
result: MagazinePistolHighCapacityPractice
materials:
Steel: 125
- type: latheRecipe
parent: BaseAmmoRecipe
id: MagazinePistolHighCapacityIncendiary
result: MagazinePistolHighCapacityIncendiary
materials:
Steel: 25
Plastic: 225
- type: latheRecipe
parent: BaseAmmoRecipe
id: MagazinePistolHighCapacityUranium
result: MagazinePistolHighCapacityUranium
materials:
Steel: 25
Plastic: 200
Uranium: 200
# Armour vests
- type: latheRecipe

View File

@ -39,6 +39,7 @@
- BoxShotgunUranium
- SpeedLoaderSpecialUranium
- MagazineBoxSpecialUranium
- MagazinePistolHighCapacityUranium
# Incendiary
- BoxShotgunIncendiary
- MagazineRifleIncendiary
@ -54,6 +55,7 @@
- SpeedLoaderSpecialUranium
- MagazineBoxSpecialIncendiary
- MagazineBoxSpecialHoly
- MagazinePistolHighCapacityIncendiary
- type: technology
id: EnergyGuns

View File

@ -56,8 +56,7 @@
effects:
- !type:AddCompsEffect
components:
- type: Unrevivable
cloneable: false
- type: Uncloneable
- type: trait
id: Unborgable
@ -98,6 +97,7 @@
- !type:HasCompCondition
component: BorgChassis
invert: true
tooltip: trait-condition-borg-not
- !type:IsSpeciesCondition
species: IPC
invert: true

View File

@ -0,0 +1,25 @@
- type: trait
id: MarkedAsProtected
name: trait-protected-name
description: trait-protected-desc
category: Meta
conditions:
- !type:InDepartmentCondition
department: Command
invert: true
- !type:InDepartmentCondition
department: Security
invert: true
- !type:IsAntagEligibleCondition
antag: Thief
invert: true
- !type:IsAntagEligibleCondition
antag: Traitor
invert: true
- !type:IsAntagEligibleCondition
antag: TraitorSleeper
invert: true
effects:
- !type:AddCompsEffect
components:
- type: TargetObjectiveImmune

View File

@ -56,6 +56,7 @@
- !type:HasCompCondition
component: BorgChassis
invert: true
tooltip: trait-condition-borg-not
effects:
- !type:AddCompsEffect
components:
@ -168,6 +169,7 @@
- !type:HasCompCondition
component: BorgChassis
invert: true
tooltip: trait-condition-borg-not
effects:
- !type:AddCompsEffect
components:

View File

@ -23,3 +23,10 @@
name: trait-category-medical
priority: 40
accentColor: "#ef4444" # Red
- type: traitCategory
id: Meta
name: trait-category-meta
priority: 50
maxTraits: 1
accentColor: "#eab308" # Yellow

View File

@ -15,6 +15,7 @@
- type: entity
parent: BaseXenoArtifactEffect
id: XenoArtifactRaiseGlimmer
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails
specificTip: xenoarch-effect-tip-specific-glimmer
@ -28,6 +29,7 @@
- type: entity
parent: XenoArtifactRaiseGlimmer
id: XenoArtifactRaiseGlimmerLarge
categories: [ HideSpawnMenu ]
components:
- type: GlimmerArtifact
range:
@ -38,6 +40,7 @@
- type: entity
parent: XenoArtifactRaiseGlimmer
id: XenoArtifactLowerGlimmer
categories: [ HideSpawnMenu ]
components:
- type: GlimmerArtifact
range:
@ -48,6 +51,7 @@
- type: entity
parent: BaseOneTimeXenoArtifactEffect
id: XenoArtifactMakePsionic
categories: [ HideSpawnMenu ]
components:
- type: XAEDetails
specificTip: xenoarch-effect-tip-specific-psionic

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

View File

@ -0,0 +1,51 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "sprites made by keke38",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "base"
},
{
"name": "mag-0"
},
{
"name": "mag-1"
},
{
"name": "mag-2"
},
{
"name": "mag-3"
},
{
"name": "mag-4"
},
{
"name": "bolt-open"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
},
{
"name": "equipped-BACKPACK",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE",
"directions": 4
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

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