Merge remote-tracking branch 'origin/master'

This commit is contained in:
Vigers Ray 2026-01-26 21:32:47 +01:00
commit 8595a41e2a
75 changed files with 1510 additions and 286 deletions

View file

@ -31,7 +31,6 @@ public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
_menu = this.CreateWindow<CrewMonitoringWindow>();
_menu.Set(stationName, gridUid);
_menu.SetBoundUserInterface(this);
}
protected override void UpdateState(BoundUserInterfaceState state)
@ -43,7 +42,6 @@ public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
case CrewMonitoringState st:
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
_menu?.ShowSensors(st.Sensors, Owner, xform?.Coordinates);
_menu?.UpdateCorpseAlertToggle(st.CorpseAlertEnabled);
break;
}
}

View file

@ -34,13 +34,6 @@
HorizontalAlignment="Center"
Visible="false"/>
</ScrollContainer>
<!-- Sunrise - Start -->
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 2 0 3">
<Label Text ="{Loc crew-monitoring-corpse-alert}" VerticalAlignment="Center" Margin="0 0 6 0"/>
<CheckBox Name="CorpseAlertToggle" Text="{Loc 'crew-monitoring-corpse-alert-off'}"
HorizontalAlignment="Center" Margin="0 0 0 0" SetWidth="260"/>
</BoxContainer>
<!-- Sunrise - End -->
</BoxContainer>
</BoxContainer>

View file

@ -4,7 +4,6 @@ using System.Numerics;
using Content.Client.Pinpointer.UI;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Medical.CrewMonitoring;
using Content.Shared.Medical.SuitSensor;
using Content.Shared.StatusIcon;
using Robust.Client.AutoGenerated;
@ -26,14 +25,12 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ILocalizationManager _loc = default!; // Sunrise - Added
private readonly SharedTransformSystem _transformSystem;
private readonly SpriteSystem _spriteSystem;
private NetEntity? _trackedEntity;
private bool _tryToScrollToListFocus;
private Texture? _blipTexture;
private CrewMonitoringBoundUserInterface? _boundUserInterface;
public CrewMonitoringWindow()
{
@ -44,11 +41,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
_spriteSystem = _entManager.System<SpriteSystem>();
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
// Sunrise - Start: Alert
CorpseAlertToggle.OnToggled += OnCorpseAlertTogglePressed;
UpdateCorpseAlertToggle(false);
// Sunrise - End: Alert
}
public void Set(string stationName, EntityUid? mapUid)
@ -66,11 +58,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
NavMap.ForceNavMapUpdate();
}
public void SetBoundUserInterface(CrewMonitoringBoundUserInterface boundUserInterface)
{
_boundUserInterface = boundUserInterface;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
@ -460,34 +447,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
NavMap.TrackedEntities.Clear();
NavMap.LocalizedNames.Clear();
}
// Sunrise - Start: Alert
public void UpdateCorpseAlertToggle(bool enabled)
{
CorpseAlertToggle.Pressed = enabled;
CorpseAlertToggle.Text = _loc.GetString(enabled ? "crew-monitoring-corpse-alert-on" : "crew-monitoring-corpse-alert-off");
CorpseAlertToggle.Label.FontColorOverride = enabled ? Color.LimeGreen : Color.Red;
CorpseAlertToggle.Disabled = false;
}
private void OnCorpseAlertTogglePressed(BaseButton.ButtonToggledEventArgs args)
{
CorpseAlertToggle.Pressed = args.Pressed;
CorpseAlertToggle.Disabled = true;
_boundUserInterface?.SendMessage(new CrewMonitoringToggleCorpseAlertMessage());
}
// Sunrise - End: Alert
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_boundUserInterface = null;
}
}
}
public sealed class CrewMonitoringButton : Button

View file

@ -1,58 +0,0 @@
using Content.Shared.Medical.CrewMonitoring;
using Robust.Client.UserInterface;
using System.Linq;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.AccessOverriderComponent;
namespace Content.Client.Medical.CrewMonitoring.Brigmedic;
public class BrigmedicCrewMonitoringBoundUserInterface : BoundUserInterface
{
[ViewVariables]
protected CrewMonitoringWindow? _menu;
public BrigmedicCrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_accessOverriderSystem = EntMan.System<SharedAccessOverriderSystem>();
}
protected readonly SharedAccessOverriderSystem _accessOverriderSystem = default!;
protected override void Open()
{
base.Open();
EntityUid? gridUid = null;
var stationName = string.Empty;
if (EntMan.TryGetComponent<TransformComponent>(Owner, out var xform))
{
gridUid = xform.GridUid;
if (EntMan.TryGetComponent<MetaDataComponent>(gridUid, out var metaData))
{
stationName = metaData.EntityName;
}
}
_menu = this.CreateWindow<CrewMonitoringWindow>();
_menu.Set(stationName, gridUid);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
switch (state)
{
case CrewMonitoringState st:
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
var securityDepartmentSensors = st.Sensors
.Where(sensor => sensor.JobDepartments.Contains("Security"))
.ToList();
_menu?.ShowSensors(securityDepartmentSensors, Owner, xform?.Coordinates);
break;
}
}
}

View file

@ -1,73 +0,0 @@
using Content.Shared.Medical.CrewMonitoring;
using Robust.Client.UserInterface;
using System.Linq;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.AccessOverriderComponent;
using Content.Shared.Implants.Components;
namespace Content.Client.Medical.CrewMonitoring.BSO;
public class BSOCrewMonitoringBoundUserInterface : BoundUserInterface
{
[ViewVariables]
protected CrewMonitoringWindow? _menu;
public BSOCrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_accessOverriderSystem = EntMan.System<SharedAccessOverriderSystem>();
}
protected readonly SharedAccessOverriderSystem _accessOverriderSystem = default!;
protected override void Open()
{
base.Open();
EntityUid? gridUid = null;
var stationName = string.Empty;
if (EntMan.TryGetComponent<TransformComponent>(Owner, out var xform))
{
gridUid = xform.GridUid;
if (EntMan.TryGetComponent<MetaDataComponent>(gridUid, out var metaData))
{
stationName = metaData.EntityName;
}
}
_menu = this.CreateWindow<CrewMonitoringWindow>();
_menu.Set(stationName, gridUid);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
switch (state)
{
case CrewMonitoringState st:
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
var commandDepartmentSensors = st.Sensors
.Where(sensor => sensor.JobDepartments.Contains("Command"))
.ToList();
//also ALWAYS include the trackers
//this is jank as there isnt a direct indication of a tracker in the suit sensor status
//so we need to check the component directly
foreach (var sensor in st.Sensors)
{
//get the client entity
var clientEntity = EntMan.GetEntity(sensor.SuitSensorUid);
if (EntMan.TryGetComponent<SubdermalImplantComponent>(clientEntity, out var suitSensor))
{
commandDepartmentSensors.Add(sensor);
}
}
//remove duplicates
commandDepartmentSensors = commandDepartmentSensors.Distinct().ToList();
_menu?.ShowSensors(commandDepartmentSensors, Owner, xform?.Coordinates);
break;
}
}
}

View file

@ -0,0 +1,46 @@
using Content.Shared.Medical.CrewMonitoring;
using Robust.Client.UserInterface;
namespace Content.Client._Sunrise.Medical.CrewMonitoring;
public class SunriseCrewMonitoringBoundUserInterface : BoundUserInterface
{
[ViewVariables]
protected SunriseCrewMonitoringWindow? _menu;
public SunriseCrewMonitoringBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
EntityUid? gridUid = null;
var stationName = string.Empty;
if (EntMan.TryGetComponent<TransformComponent>(Owner, out var xform))
{
gridUid = xform.GridUid;
if (EntMan.TryGetComponent<MetaDataComponent>(gridUid, out var metaData))
stationName = metaData.EntityName;
}
_menu = this.CreateWindow<SunriseCrewMonitoringWindow>();
_menu.SetBoundUserInterface(this);
_menu.Set(stationName, gridUid);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not CrewMonitoringState st)
return;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
_menu?.ShowSensors(st.Sensors, Owner, xform?.Coordinates);
_menu?.UpdateCorpseAlertToggle(st.CorpseAlertEnabled);
}
}

View file

@ -0,0 +1,58 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc crew-monitoring-ui-title}"
Resizable="False"
SetSize="1210 700"
MinSize="1210 700">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
<ui:CrewMonitoringNavMapControl Name="NavMap" HorizontalExpand="True" VerticalExpand="True" Margin="5 20"/>
<BoxContainer Orientation="Vertical" Margin="0 0 10 0">
<controls:StripeBack>
<PanelContainer>
<Label Name="StationName" Text="{Loc crew-monitoring-ui-no-station-label}" Align="Center" Margin="0 5 0 3"/>
</PanelContainer>
</controls:StripeBack>
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
PlaceHolder="{Loc crew-monitoring-ui-filter-line-placeholder}" />
<ScrollContainer Name="SensorScroller"
VerticalExpand="True"
SetWidth="520"
Margin="8, 8, 8, 8">
<BoxContainer Name="SensorsTable"
Orientation="Vertical"
HorizontalExpand="True"
Margin="0 0 10 0">
<!-- Table rows are filled by code -->
</BoxContainer>
<Label Name="NoServerLabel"
Text="{Loc crew-monitoring-ui-no-server-label}"
StyleClasses="LabelHeading"
FontColorOverride="Red"
HorizontalAlignment="Center"
Visible="false"/>
</ScrollContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 2 0 3">
<Label Text ="{Loc crew-monitoring-corpse-alert}" VerticalAlignment="Center" Margin="0 0 6 0"/>
<CheckBox Name="CorpseAlertToggle" Text="{Loc 'crew-monitoring-corpse-alert-off'}"
HorizontalAlignment="Center" Margin="0 0 0 0" SetWidth="260"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Footer -->
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc crew-monitoring-ui-flavor-left-label}" StyleClasses="WindowFooterText" />
<Label Text="{Loc crew-monitoring-ui-flavor-right-label}" StyleClasses="WindowFooterText"
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View file

@ -0,0 +1,504 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Client.Pinpointer.UI;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Medical.CrewMonitoring;
using Content.Shared.Medical.SuitSensor;
using Content.Shared.StatusIcon;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Content.Client._Sunrise.Medical.CrewMonitoring;
[GenerateTypedNameReferences]
public sealed partial class SunriseCrewMonitoringWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private readonly SharedTransformSystem _transformSystem;
private readonly SpriteSystem _spriteSystem;
private NetEntity? _trackedEntity;
private bool _tryToScrollToListFocus;
private Texture? _blipTexture;
private BoundUserInterface? _boundUserInterface;
private TimeSpan _lastStateUpdateTime;
private bool _showingEmptyState;
private int _emptyStateUpdateCount;
private bool _serverFound;
private const float ServerUpdateTimeoutSeconds = 7f;
public SunriseCrewMonitoringWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_transformSystem = _entManager.System<SharedTransformSystem>();
_spriteSystem = _entManager.System<SpriteSystem>();
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
CorpseAlertToggle.OnToggled += OnCorpseAlertTogglePressed;
UpdateCorpseAlertToggle(false);
}
public void Set(string stationName, EntityUid? mapUid)
{
_blipTexture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
if (_entManager.TryGetComponent<TransformComponent>(mapUid, out var xform))
NavMap.MapUid = xform.GridUid;
else
NavMap.Visible = false;
StationName.AddStyleClass("LabelBig");
StationName.Text = stationName;
NavMap.ForceNavMapUpdate();
}
public void SetBoundUserInterface(BoundUserInterface boundUserInterface)
{
_boundUserInterface = boundUserInterface;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_tryToScrollToListFocus)
TryToScrollToFocus();
if (_showingEmptyState)
UpdateNoServerLabelText();
}
public void ShowSensors(List<SuitSensorStatus> sensors, EntityUid monitor, EntityCoordinates? monitorCoords)
{
ClearOutDatedData();
_lastStateUpdateTime = _gameTiming.CurTime;
if (sensors.Count == 0)
{
NoServerLabel.Visible = true;
_showingEmptyState = true;
if (!_serverFound && ++_emptyStateUpdateCount >= 2)
_serverFound = true;
UpdateNoServerLabelText();
return;
}
NoServerLabel.Visible = false;
_showingEmptyState = false;
_serverFound = true;
_emptyStateUpdateCount = 0;
Dictionary<NetEntity, SuitSensorStatus> uniqueSensorsMap = new();
foreach (var sensor in sensors)
{
if (uniqueSensorsMap.TryGetValue(sensor.OwnerUid, out var existingSensor))
{
if (existingSensor.Coordinates != null && sensor.Coordinates == null)
continue;
if (existingSensor.DamagePercentage != null && sensor.DamagePercentage == null)
continue;
}
uniqueSensorsMap[sensor.OwnerUid] = sensor;
}
var uniqueSensors = uniqueSensorsMap.Values.ToList();
var orderedSensors = uniqueSensors.OrderBy(j => j.Job).ThenBy(n => n.Name);
var assignedSensors = new HashSet<SuitSensorStatus>();
var departments = uniqueSensors.SelectMany(d => d.JobDepartments).Distinct().OrderBy(n => n);
foreach (var department in departments)
{
var departmentSensors = orderedSensors.Where(d => d.JobDepartments.Contains(department));
if (departmentSensors == null || !departmentSensors.Any())
continue;
foreach (var sensor in departmentSensors)
assignedSensors.Add(sensor);
if (SensorsTable.ChildCount > 0)
{
var spacer = new Control()
{
SetHeight = 20,
};
SensorsTable.AddChild(spacer);
}
var deparmentLabel = new RichTextLabel()
{
Margin = new Thickness(10, 0),
HorizontalExpand = true,
};
deparmentLabel.SetMessage(department);
deparmentLabel.StyleClasses.Add("font-large");
SensorsTable.AddChild(deparmentLabel);
PopulateDepartmentList(departmentSensors);
}
var remainingSensors = orderedSensors.Except(assignedSensors);
if (remainingSensors.Any())
{
var spacer = new Control()
{
SetHeight = 20,
};
SensorsTable.AddChild(spacer);
var deparmentLabel = new RichTextLabel()
{
Margin = new Thickness(10, 0),
HorizontalExpand = true,
};
deparmentLabel.SetMessage(Loc.GetString("crew-monitoring-ui-no-department-label"));
SensorsTable.AddChild(deparmentLabel);
PopulateDepartmentList(remainingSensors);
}
if (monitorCoords != null && _blipTexture != null)
{
NavMap.TrackedEntities[_entManager.GetNetEntity(monitor)] = new NavMapBlip(monitorCoords.Value, _blipTexture, Color.Cyan, true, false);
}
}
private void UpdateNoServerLabelText()
{
var timedOut = (_gameTiming.CurTime - _lastStateUpdateTime) > TimeSpan.FromSeconds(ServerUpdateTimeoutSeconds);
if (timedOut)
{
_serverFound = false;
_emptyStateUpdateCount = 0;
}
NoServerLabel.Text = Loc.GetString(_serverFound && !timedOut
? "crew-monitoring-ui-no-sensors-label"
: "crew-monitoring-ui-no-server-label");
}
public void ClearSensors(bool showNoServerLabel)
{
ClearOutDatedData();
NoServerLabel.Visible = showNoServerLabel;
_showingEmptyState = showNoServerLabel;
if (showNoServerLabel)
{
_serverFound = false;
_emptyStateUpdateCount = 0;
_lastStateUpdateTime = TimeSpan.Zero;
UpdateNoServerLabelText();
}
}
private void PopulateDepartmentList(IEnumerable<SuitSensorStatus> departmentSensors)
{
foreach (var sensor in departmentSensors)
{
if (!string.IsNullOrEmpty(SearchLineEdit.Text)
&& !sensor.Name.Contains(SearchLineEdit.Text, StringComparison.CurrentCultureIgnoreCase)
&& !sensor.Job.Contains(SearchLineEdit.Text, StringComparison.CurrentCultureIgnoreCase))
continue;
var coordinates = _entManager.GetCoordinates(sensor.Coordinates);
NavMap.LocalizedNames.TryAdd(sensor.SuitSensorUid, sensor.Name + ", " + sensor.Job);
var sensorButton = new CrewMonitoringButton()
{
SuitSensorUid = sensor.SuitSensorUid,
Coordinates = coordinates,
Disabled = (coordinates == null),
HorizontalExpand = true,
};
if (sensor.SuitSensorUid == _trackedEntity)
sensorButton.AddStyleClass(StyleClass.Positive);
SensorsTable.AddChild(sensorButton);
var mainContainer = new BoxContainer()
{
Orientation = LayoutOrientation.Horizontal,
HorizontalExpand = true,
};
sensorButton.AddChild(mainContainer);
var statusContainer = new BoxContainer()
{
SizeFlagsStretchRatio = 1.25f,
Orientation = LayoutOrientation.Horizontal,
HorizontalExpand = true,
};
mainContainer.AddChild(statusContainer);
var suitCoordsIndicator = new TextureRect()
{
Texture = _blipTexture,
TextureScale = new Vector2(0.25f, 0.25f),
Modulate = coordinates != null ? Color.LimeGreen : Color.DarkRed,
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
};
statusContainer.AddChild(suitCoordsIndicator);
var specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "alive");
if (!sensor.IsAlive)
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "dead");
else if (sensor.DamagePercentage != null)
{
var index = MathF.Round(4f * sensor.DamagePercentage.Value);
if (index >= 5)
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "critical");
else
specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Alerts/human_crew_monitoring.rsi"), "health" + index);
}
var statusIcon = new AnimatedTextureRect
{
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
Margin = new Thickness(0, 1, 3, 0),
};
statusIcon.SetFromSpriteSpecifier(specifier);
statusIcon.DisplayRect.TextureScale = new Vector2(2f, 2f);
statusContainer.AddChild(statusIcon);
var nameLabel = new Label()
{
Text = sensor.Name,
HorizontalExpand = true,
ClipText = true,
};
statusContainer.AddChild(nameLabel);
var jobContainer = new BoxContainer()
{
Orientation = LayoutOrientation.Horizontal,
HorizontalExpand = true,
};
mainContainer.AddChild(jobContainer);
if (_prototypeManager.TryIndex<JobIconPrototype>(sensor.JobIcon, out var proto))
{
var jobIcon = new TextureRect()
{
TextureScale = new Vector2(2f, 2f),
VerticalAlignment = VAlignment.Center,
Texture = _spriteSystem.Frame0(proto.Icon),
Margin = new Thickness(5, 0, 5, 0),
};
jobContainer.AddChild(jobIcon);
}
var jobLabel = new Label()
{
Text = sensor.Job,
HorizontalExpand = true,
ClipText = true,
};
jobContainer.AddChild(jobLabel);
if (coordinates != null && NavMap.Visible && _blipTexture != null)
{
NavMap.TrackedEntities.TryAdd(sensor.SuitSensorUid,
new NavMapBlip
(CoordinatesToLocal(coordinates.Value),
_blipTexture,
(_trackedEntity == null || sensor.SuitSensorUid == _trackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
sensor.SuitSensorUid == _trackedEntity));
NavMap.Focus = _trackedEntity;
sensorButton.OnButtonUp += args =>
{
var prevTrackedEntity = _trackedEntity;
if (_trackedEntity == sensor.SuitSensorUid)
_trackedEntity = null;
else
{
_trackedEntity = sensor.SuitSensorUid;
NavMap.CenterToCoordinates(coordinates.Value);
}
NavMap.Focus = _trackedEntity;
UpdateSensorsTable(_trackedEntity, prevTrackedEntity);
};
}
}
}
private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
{
var prevTrackedEntity = _trackedEntity;
_trackedEntity = netEntity;
if (_trackedEntity == prevTrackedEntity)
prevTrackedEntity = null;
NavMap.Focus = _trackedEntity;
_tryToScrollToListFocus = true;
UpdateSensorsTable(_trackedEntity, prevTrackedEntity);
}
private void UpdateSensorsTable(NetEntity? currTrackedEntity, NetEntity? prevTrackedEntity)
{
foreach (var sensor in SensorsTable.Children)
{
if (sensor is not CrewMonitoringButton)
continue;
var castSensor = (CrewMonitoringButton)sensor;
if (castSensor.SuitSensorUid == prevTrackedEntity)
castSensor.RemoveStyleClass(StyleClass.Positive);
else if (castSensor.SuitSensorUid == currTrackedEntity)
castSensor.AddStyleClass(StyleClass.Positive);
if (castSensor?.Coordinates == null)
continue;
if (NavMap.TrackedEntities.TryGetValue(castSensor.SuitSensorUid, out var data))
{
data = new NavMapBlip
(CoordinatesToLocal(data.Coordinates),
data.Texture,
(currTrackedEntity == null || castSensor.SuitSensorUid == currTrackedEntity) ? Color.LimeGreen : Color.LimeGreen * Color.DimGray,
castSensor.SuitSensorUid == currTrackedEntity);
NavMap.TrackedEntities[castSensor.SuitSensorUid] = data;
}
}
}
private void TryToScrollToFocus()
{
if (!_tryToScrollToListFocus)
return;
if (TryGetNextScrollPosition(out float? nextScrollPosition))
{
SensorScroller.VScrollTarget = nextScrollPosition.Value;
if (MathHelper.CloseToPercent(SensorScroller.VScroll, SensorScroller.VScrollTarget))
{
_tryToScrollToListFocus = false;
return;
}
}
}
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
{
nextScrollPosition = 0;
foreach (var sensor in SensorsTable.Children)
{
if (sensor is CrewMonitoringButton &&
((CrewMonitoringButton)sensor).SuitSensorUid == _trackedEntity)
return true;
nextScrollPosition += sensor.Height;
}
nextScrollPosition = null;
return false;
}
private EntityCoordinates CoordinatesToLocal(EntityCoordinates refCoords)
{
if (NavMap.MapUid != null)
return _transformSystem.WithEntityId(refCoords, (EntityUid)NavMap.MapUid);
else
return refCoords;
}
private void ClearOutDatedData()
{
SensorsTable.RemoveAllChildren();
NavMap.TrackedCoordinates.Clear();
NavMap.TrackedEntities.Clear();
NavMap.LocalizedNames.Clear();
}
public void UpdateCorpseAlertToggle(bool enabled)
{
CorpseAlertToggle.Pressed = enabled;
CorpseAlertToggle.Text = _loc.GetString(enabled ? "crew-monitoring-corpse-alert-on" : "crew-monitoring-corpse-alert-off");
CorpseAlertToggle.Label.FontColorOverride = enabled ? Color.LimeGreen : Color.Red;
CorpseAlertToggle.Disabled = false;
}
private void OnCorpseAlertTogglePressed(BaseButton.ButtonToggledEventArgs args)
{
CorpseAlertToggle.Pressed = args.Pressed;
CorpseAlertToggle.Disabled = true;
_boundUserInterface?.SendMessage(new CrewMonitoringToggleCorpseAlertMessage());
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_boundUserInterface = null;
}
}
}
public sealed class CrewMonitoringButton : Button
{
public int IndexInTable;
public NetEntity SuitSensorUid;
public EntityCoordinates? Coordinates;
}

View file

@ -82,7 +82,10 @@ public sealed partial class CrewMonitoringConsoleSystem : EntitySystem // Sunris
// Update all sensors info
var allSensors = component.ConnectedSensors.Values.ToList();
// Sunrise - Start
ApplyFilter(uid, ref allSensors);
var corpseAlertEnabled = TryComp(uid, out CrewMonitoringCorpseAlertComponent? alert) && alert.DoCorpseAlert;
_uiSystem.SetUiState(uid, CrewMonitoringUIKey.Key, new CrewMonitoringState(allSensors, corpseAlertEnabled));
// Sunrise - End

View file

@ -32,8 +32,8 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
SubscribeLocalEvent<NinjaSuitComponent, ContainerIsInsertingAttemptEvent>(OnSuitInsertAttempt);
SubscribeLocalEvent<NinjaSuitComponent, RecallKatanaEvent>(OnRecallKatana);
SubscribeLocalEvent<NinjaSuitComponent, NinjaEmpEvent>(OnEmp);
SubscribeLocalEvent<NinjaSuitComponent, CreateSmokeGrenadeEvent>(OnCreateSmokeGrenade);
SubscribeLocalEvent<NinjaSuitComponent, CreateFlashbangGrenadeEvent>(OnCreateFlashbangGrenade);
SubscribeLocalEvent<NinjaSuitComponent, CreateSmokeGrenadeEvent>(OnCreateSmokeGrenade); // Sunrise-Add
SubscribeLocalEvent<NinjaSuitComponent, CreateFlashbangGrenadeEvent>(OnCreateFlashbangGrenade); // Sunrise-Add
}
protected override void NinjaEquipped(Entity<NinjaSuitComponent> ent, Entity<SpaceNinjaComponent> user)
@ -153,7 +153,7 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
_emp.EmpPulse(Transform(user).Coordinates, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration, user);
}
// Sunrise-start
private void OnCreateSmokeGrenade(Entity<NinjaSuitComponent> ent, ref CreateSmokeGrenadeEvent args)
{
var (uid, comp) = ent;
@ -169,9 +169,8 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
if (CheckDisabled(ent, user))
return;
// Create smoke grenade in hands or on ground
var grenade = Spawn("SmokeGrenade", _transform.GetMapCoordinates(user));
_hands.TryPickupAnyHand(user, grenade);
// Instant smoke effect around the user (10s by prototype)
Spawn("AdminInstantEffectSmoke10", _transform.GetMapCoordinates(user));
}
private void OnCreateFlashbangGrenade(Entity<NinjaSuitComponent> ent, ref CreateFlashbangGrenadeEvent args)
@ -189,8 +188,8 @@ public sealed class NinjaSuitSystem : SharedNinjaSuitSystem
if (CheckDisabled(ent, user))
return;
// Create flashbang grenade in hands or on ground
var grenade = Spawn("GrenadeFlashBang", _transform.GetMapCoordinates(user));
_hands.TryPickupAnyHand(user, grenade);
// Instant flash effect around the user
Spawn("AdminInstantEffectFlash", _transform.GetMapCoordinates(user));
}
// Sunrise-End
}

View file

@ -0,0 +1,84 @@
using Content.Shared.Implants.Components;
using Content.Shared.Medical.CrewMonitoring;
using Content.Shared.Medical.SuitSensor;
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
namespace Content.Server.Medical.CrewMonitoring;
public sealed partial class CrewMonitoringConsoleSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private void ApplyFilter(EntityUid uid, ref List<SuitSensorStatus> sensors)
{
if (!TryComp(uid, out CrewMonitoringFilterComponent? filter))
return;
var showOnlyWoundedOrDead = filter.OnlyShowWoundedOrDead;
var filterByDepartment = filter.AllowedDepartmentIds.Count > 0;
if (!showOnlyWoundedOrDead && !filterByDepartment)
return;
HashSet<string>? allowedDepartmentNames = null;
if (filterByDepartment)
allowedDepartmentNames = BuildAllowedDepartmentNameSet(filter.AllowedDepartmentIds);
var includeTrackers = filter.IncludeTrackers;
var filteredSensors = new List<SuitSensorStatus>(sensors.Count);
foreach (var sensor in sensors)
{
if (showOnlyWoundedOrDead && !IsWoundedOrDead(sensor))
continue;
if (allowedDepartmentNames != null)
{
if (!IsInAllowedDepartments(sensor, allowedDepartmentNames, includeTrackers))
continue;
}
filteredSensors.Add(sensor);
}
sensors = filteredSensors;
}
private HashSet<string> BuildAllowedDepartmentNameSet(List<string> departmentIds)
{
var allowedDepartments = new HashSet<string>();
foreach (var departmentId in departmentIds)
{
if (_prototypeManager.TryIndex<DepartmentPrototype>(departmentId, out var department))
allowedDepartments.Add(Loc.GetString(department.Name));
else
allowedDepartments.Add(departmentId);
}
return allowedDepartments;
}
private bool IsWoundedOrDead(SuitSensorStatus sensor)
{
if (!sensor.IsAlive)
return true;
return sensor.DamagePercentage is >= CriticalDamagePercentage;
}
private bool IsInAllowedDepartments(SuitSensorStatus sensor, HashSet<string> allowedDepartmentNames, bool includeTrackers)
{
foreach (var department in sensor.JobDepartments)
{
if (allowedDepartmentNames.Contains(department))
return true;
}
if (!includeTrackers)
return false;
var sensorEntity = GetEntity(sensor.SuitSensorUid);
return HasComp<SubdermalImplantComponent>(sensorEntity);
}
}

View file

@ -75,7 +75,7 @@ public sealed partial class NinjaSuitComponent : Component
/// </summary>
[DataField]
public TimeSpan EmpDuration = TimeSpan.FromSeconds(60);
// Sunrise-Start
/// <summary>
/// The action id for creating a smoke grenade
/// </summary>
@ -104,7 +104,8 @@ public sealed partial class NinjaSuitComponent : Component
/// Battery charge used to create a flashbang grenade.
/// </summary>
[DataField]
public float FlashbangGrenadeCharge = 120f;
public float FlashbangGrenadeCharge = 30;
// Sunrise-End
}
public sealed partial class RecallKatanaEvent : InstantActionEvent;

View file

@ -0,0 +1,22 @@
namespace Content.Shared.Medical.CrewMonitoring;
[RegisterComponent]
public sealed partial class CrewMonitoringFilterComponent : Component
{
/// <summary>
/// Разрешенные отделы. Если пустое все доступны
/// </summary>
[DataField]
public List<string> AllowedDepartmentIds = new();
/// <summary>
/// Будут ли отображаться по трекерам
/// </summary>
[DataField]
public bool IncludeTrackers;
/// <summary>
/// Показывать ли мертвых, в крите, в ужасном
/// </summary>
[DataField]
public bool OnlyShowWoundedOrDead;
}

View file

@ -25173,3 +25173,76 @@
id: 1635
time: '2026-01-26T19:44:54.0000000+00:00'
url: https://github.com/space-sunrise/sunrise-station/pull/3789
- author: KaiserMaus
changes:
- message: "\u041A\u043B\u0430\u043D \u043F\u0430\u0443\u043A\u0430 \u0434\u043E\
\u0440\u0430\u0431\u043E\u0442\u0430\u043B \u043A\u043E\u0441\u0442\u044E\u043C\
\ \u043D\u0438\u043D\u0434\u0437\u044F, \u0442\u0435\u043F\u0435\u0440\u044C\
\ \u0447\u0430\u0441\u0442\u044C \u043A\u043E\u0441\u0442\u044E\u043C\u0430\
\ \u0438\u043C\u0435\u0435\u0442 \u0442\u0435\u043F\u043B\u043E\u0432\u0438\u0437\
\u043E\u0440 \u0438 \u043C\u0433\u043D\u043E\u0432\u0435\u043D\u043D\u044B\u0439\
\ \u0432\u044B\u043F\u0443\u0441\u043A \u0432\u0441\u043F\u044B\u0448\u043A\u0438\
/\u0434\u044B\u043C\u0430 \u0430 \u0437\u0430\u0442\u0440\u0430\u0442\u044B\
\ \u043D\u0430 \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0444\
\u0443\u043D\u043A\u0446\u0438\u0438 \u0441\u0442\u0430\u043B\u0438 \u043C\u0435\
\u043D\u044C\u0448\u0435."
type: Tweak
- message: "NanoTrasen \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\
\ \u043D\u043E\u0432\u044B\u0435 \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\
\u044B \u0432 \u0431\u0440\u043E\u043D\u0435 \u0431\u043E\u0440\u0433\u043E\u0432\
, \u043E\u043D\u0430 \u0441\u0442\u0430\u043B\u0430 \u043D\u0430 5-10% \u043B\
\u0443\u0447\u0448\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0441\u0442\
\u043E\u044F\u0442\u044C \u043F\u0443\u043B\u044F\u043C."
type: Tweak
- message: "\u041D\u0438\u043D\u0434\u0437\u044F \u0438\u0437 \u041A\u043B\u0430\
\u043D\u0430 \u043F\u0430\u0443\u043A\u0430 \u0442\u0430\u043A\u0438 \u0434\u043E\
\u0437\u0432\u043E\u043D\u0438\u043B\u0441\u044F. \u0414\u043E\u0431\u0430\u0432\
\u043B\u0435\u043D\u043E \u0431\u043E\u043B\u044C\u0448\u0435 \u0432\u0435\u0441\
\u0435\u043B\u044C\u044F."
type: Tweak
id: 1636
time: '2026-01-26T19:51:04.0000000+00:00'
url: https://github.com/space-sunrise/sunrise-station/pull/3787
- author: Orvex07
changes:
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D \u043D\u043E\u0432\u044B\
\u0439 \u0440\u0443\u0447\u043D\u043E\u0439 \u043C\u043E\u043D\u0438\u0442\u043E\
\u0440\u0438\u043D\u0433 \u043F\u0430\u0440\u0430\u043C\u0435\u0434\u0438\u043A\
\u0430\u043C. \u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u044D\
\u043A\u0438\u043F\u0430\u0436 \u0432 \u0443\u0436\u0430\u0441\u043D\u043E\u043C\
, \u043A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u043C, \u043C\
\u0435\u0440\u0442\u0432\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\
\u0438\u0438"
type: Add
- message: "\u0418\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043C\u043E\u043D\
\u0438\u0442\u043E\u0440\u0438\u043D\u0433\u0430 \u043E\u043F\u043E\u0432\u0435\
\u0449\u0430\u0435\u0442 \u043A\u043E\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\
\u0435\u0440 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D \u043D\u043E\
\ \u043D\u0435\u0442 \u0434\u0430\u0442\u0447\u0438\u043A\u043E\u0432 \u044D\
\u043A\u0438\u043F\u0430\u0436\u0430 (\u0440\u0430\u043D\u0435\u0435 \u043F\u043E\
\u043A\u0430\u0437\u044B\u0432\u0430\u043B\u043E \u0447\u0442\u043E \u0441\u0435\
\u0440\u0432\u0435\u0440 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)"
type: Add
- message: "\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\
\ \u0440\u0443\u0447\u043D\u043E\u0439 \u043C\u043E\u043D\u0438\u0442\u043E\u0440\
\u0438\u043D\u0433 \u041E\u0421\u0429\u0430"
type: Fix
- message: "\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\
\ \u0440\u0443\u0447\u043D\u043E\u0439 \u043C\u043E\u043D\u0438\u0442\u043E\u0440\
\u0438\u043D\u0433 \u0411\u0440\u0438\u0433\u043C\u0435\u0434\u0438\u043A\u0430"
type: Fix
id: 1637
time: '2026-01-26T20:00:52.0000000+00:00'
url: https://github.com/space-sunrise/sunrise-station/pull/3788
- author: KaiserMaus
changes:
- message: "\u041F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u044B \u0438\
\ \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u044B \u0440\u0443\u043A\
\u043E\u0432\u043E\u0434\u0441\u0442\u0432\u0430 \u043F\u043E \u0440\u0430\u0441\
\u0430\u043C \u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u0441\
\u0432\u043E\u0438 \u0440\u0430\u0441\u0441\u044B \u0432 \u0441\u043F\u0438\u0441\
\u043E\u043A."
type: Tweak
id: 1638
time: '2026-01-26T20:27:51.0000000+00:00'
url: https://github.com/space-sunrise/sunrise-station/pull/3792

View file

@ -0,0 +1,5 @@
ent-HandheldEmergencyCrewMonitor = PulseGuard™ XR
.desc = A hand-held crew monitor displaying the status of suit sensors of injured crew.
ent-HandheldEmergencyCrewMonitorEmpty = { ent-HandheldEmergencyCrewMonitor }
.suffix = Empty
.desc = { ent-HandheldEmergencyCrewMonitor.desc }

View file

@ -1,6 +1,7 @@
crew-monitoring-corpse-alert = Alert signal:
crew-monitoring-corpse-alert-on = ON
crew-monitoring-corpse-alert-off = OFF
crew-monitoring-ui-no-sensors-label = Server found, no matching sensors
item-toggle-deactivate-alert = Deactivate alert
item-toggle-activate-alert = Activate alert

View file

@ -1,5 +1,5 @@
ent-HandheldBSOCrewMonitor = CommandFriend™ X-02
.desc = Не отслеживает уровень компетентности командного состава.
ent-HandheldBSOCrewMonitor = Команд-Друг™ X-02
.desc = Не отслеживает уровень компетентности командного состава. Показывает только командный состав.
ent-HandheldBSOCrewMonitorEmpty = { ent-HandheldBSOCrewMonitor }
.suffix = Пустой
.desc = { ent-HandheldBSOCrewMonitor.desc }

View file

@ -0,0 +1,5 @@
ent-HandheldEmergencyCrewMonitor = Пульс-Гард™ XR
.desc = Ручной монитор экипажа, отображающий состояние датчиков скафандра раненых членов экипажа.
ent-HandheldEmergencyCrewMonitorEmpty = { ent-HandheldEmergencyCrewMonitor }
.suffix = Пустой
.desc = { ent-HandheldEmergencyCrewMonitor.desc }

View file

@ -1,6 +1,8 @@
crew-monitoring-corpse-alert = Сигнал тревоги:
crew-monitoring-corpse-alert-on = ВКЛ
crew-monitoring-corpse-alert-off = ВЫКЛ
crew-monitoring-ui-no-server-label = Сервер не найден
crew-monitoring-ui-no-sensors-label = Сервер найден, подходящих датчиков нет
item-toggle-deactivate-alert = Деактивировать тревогу
item-toggle-activate-alert = Активировать тревогу

View file

@ -29,11 +29,12 @@ messenger-add-user-search = Поиск:
messenger-add-user-placeholder = Введите имя пользователя
messenger-add-user-cancel = Отмена
messenger-emoji-picker-title = Выбрать смайлики
messenger-connection-label = { $status ->
[connecting] { messenger-status-connecting }
[disconnected] { messenger-status-disconnected }
*[connected] { messenger-status-connected }
}
messenger-connection-label =
Статус подключения: { $status ->
[connecting] { messenger-status-connecting }
[disconnected] { messenger-status-disconnected }
*[connected] { messenger-status-connected }
}
messenger-system-user-added = добавил(а) { $userName } в группу
messenger-system-user-removed = удалил(а) { $userName } из группы
messenger-system-user-added-by = { $adderName } добавил(а) { $userName } в группу

View file

@ -1,8 +1,12 @@
terror-dragon = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно вышел на связь со странной рыбой в ближнем космосе.
terror-revenant = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно вышел на связь с потусторонней энергией в ближнем космосе.
terror-nukeops-infiltrator = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно вышел на связь с конкурентной корпорацией в ближнем космосе.
terror-lone-operative = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно отправил координаты диска аунтификации в ближнем космосе.
terror-nukeops-infiltrator = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно отправил координаты диска аунтификации в ближнем космосе.
terror-lone-operative = Внимание экипажу, похоже, кто-то на вашей станции установил неожиданный контакт с кроваво-красным мародёром в ближнем космосе.
terror-nukeops-operative = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно отправил объявление войны в ближнем космосе.
terror-pirate = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно оформил пару кредитов, заложив саму станцию.
terror-pirate-small = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно оформил пару микрозаймов, заложив саму станцию.
terror-abductor = Внимание экипажу, похоже, что кто-то с вашей станции неожиданно вышел на связь со странным сигналом в ближнем космосе.
terror-clown = Внимание экипажу, похоже, кто-то на вашей станции установил неожиданный контакт с бомбастическим трио хонкающих клоунов в ближнем космосе.
terror-ninja = Внимание экипажу, похоже, кто-то на вашей станции установил неожиданный контакт с кланом паука в ближнем космосе.
terror-rod = Внимание экипажу, похоже, кто-то на вашей станции установил неожиданный контакт с недвижимой силой в ближнем космосе.
terror-wizard = Внимание экипажу, похоже, кто-то на вашей станции установил неожиданный контакт с представителем Федерации магов в ближнем космосе.

View file

@ -720,7 +720,7 @@ entities:
rot: -1.5707963267948966 rad
pos: 2.5,-5.5
parent: 1
- proto: AntimaterialAmmoKit
- proto: ShotGunKitLarge
entities:
- uid: 839
components:
@ -3638,7 +3638,7 @@ entities:
- type: Transform
pos: -2.4942985,-13.37949
parent: 1
- proto: PlastitaniumWindowDiagonal
- proto: PlasmaReinforcedWindowDirectional
entities:
- uid: 520
components:
@ -4768,7 +4768,7 @@ entities:
rot: -1.5707963267948966 rad
pos: -1.5,-2.5
parent: 1
- proto: Thruster
- proto: ThrusterSyndicate
entities:
- uid: 659
components:
@ -5705,7 +5705,7 @@ entities:
- type: Transform
pos: 1.5215412,-12.936018
parent: 1
- proto: WeaponSIAR52Biocode
- proto: WeaponAJ100Biocode
entities:
- uid: 565
components:
@ -5773,7 +5773,7 @@ entities:
rot: 3.141592653589793 rad
pos: 0.5,-27.5
parent: 1
- proto: WindoorSecure
- proto: PlasmaWindoorSecureNukeopLocked
entities:
- uid: 831
components:

View file

@ -305,6 +305,14 @@
sprite: Clothing/Eyes/Glasses/ninjavisor.rsi
- type: FlashImmunity
# Sunrise-Start
- type: ToggleClothing
action: ActionToggleThermalVision
disableOnUnequip: true
targetSlot: eyes
- type: ComponentToggler
parent: true
components:
- type: ThermalVision
- type: ItemToggle
predictable: false
onUse: false
@ -316,8 +324,11 @@
soundFailToActivate:
path: /Audio/Machines/button.ogg
- type: NinjaSuitDraw
drawRate: 0.5
drawRate: 2
useRate: 1
- type: ToggleNinjaSuitDraw
- type: UseDelay
delay: 1
# Sunrise-End
- type: entity

View file

@ -219,7 +219,7 @@
#Space Ninja Helmet
- type: entity
parent: [ClothingHeadEVAHelmetBase, BaseHighlyIllegalContraband]
parent: [ClothingHeadEVAHelmetBase, BaseHighlyIllegalContraband, BaseNightVisionDevice]
id: ClothingHeadHelmetSpaceNinja
name: space ninja helmet
description: What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear.
@ -238,6 +238,21 @@
layers:
Hair: HEAD
Snout: HEAD
# Sunrise-Start
- type: ItemToggle
predictable: false
onUse: false
canActivateInhand: false
soundActivate:
path: /Audio/_Sunrise/Items/Goggles/activate.ogg
soundDeactivate:
path: /Audio/_Sunrise/Items/Goggles/deactivate.ogg
soundFailToActivate:
path: /Audio/Machines/button.ogg
- type: NinjaSuitDraw
drawRate: 0.1
- type: ToggleNinjaSuitDraw
# Sunrise-End
#Knight Helmet
- type: entity

View file

@ -524,6 +524,16 @@
sprite: Clothing/Uniforms/Jumpskirt/operative_s.rsi
- type: StaticPrice
price: 500
# Sunrise-Start
- type: Armor #Based on /tg/ but slightly compensated to fit the fact that armor stacks in SS14.
modifiers:
coefficients:
Slash: 0.92
Piercing: 0.98
Heat: 0.95
- type: StaminaResistance
damageCoefficient: 0.95
# Sunrise-End
- type: entity
parent: ClothingUniformSkirtBase

View file

@ -829,6 +829,16 @@
sprite: Clothing/Uniforms/Jumpsuit/operative.rsi
- type: StaticPrice
price: 500
# Sunrise-Start
- type: Armor
modifiers:
coefficients:
Slash: 0.92
Piercing: 0.98
Heat: 0.95
- type: StaminaResistance
damageCoefficient: 0.95
# Sunrise-End
- type: entity
parent: ClothingUniformBase

View file

@ -33,9 +33,22 @@
suffix: Flash
parent: AdminInstantEffectBase
components:
# Sunrise-Start
- type: TimerTrigger
delay: 0
keyOut: timer
# Sunrise-End
- type: FlashOnTrigger
# Sunrise-Start
keysIn:
- timer
# Sunrise-End
range: 7
- type: SpawnOnTrigger
# Sunrise-Start
keysIn:
- timer
# Sunrise-End
proto: GrenadeFlashEffect
- type: entity

View file

@ -3926,7 +3926,11 @@
id: MobDionaNymph
description: It's like a cat, only.... branch-ier.
components:
# Sunrise-Start
- type: VentCrawler # Sunrise-edit
- type: Item
size: Large
# Sunrise-End
- type: Sprite
drawdepth: Mobs
layers:
@ -4006,6 +4010,8 @@
- type: CollectiveMind
minds:
- Dioneas
- type: Carriable
- type: CanEscapeInventory
- type: entity
parent: MobDionaNymph

View file

@ -47,6 +47,10 @@
heatDamage:
types:
Heat: 2.5 # Per second, scales with temperature & other constants
# Sunrise-Start
- type: FlashModifier
modifier: 1.4
# Sunrise-End
# - type: Wagging TODO: Add back once we have animated tails again. Were removed due to the sprite rework, causing all of them to not fit anymore.
# action: ActionToggleWaggingVulpkanin
- type: TemperatureProtection

View file

@ -72,7 +72,8 @@
hadOutline: true
examineThreshold: 0.9 # Sunrise-Edit
- type: StealthOnMove
passiveVisibilityRate: -0.15 # Sunrise-Edit
passiveVisibilityRate: -1 # very useful for going around the station concealed, if you start jitterstrafing you get seen # Sunrise-Edit
movementVisibilityRate: 0.20
- type: entity
id: BigBox

View file

@ -1,59 +1,110 @@
- type: guideEntry
id: Species
name: guide-entry-species
text: "/ServerInfo/Guidebook/Mobs/Species.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Species.xml" # Sunrise-Edit
children:
- Arachnid
- Demon # Sunrise-Add
- Diona
- Dwarf
- Felinid # Sunrise-Add
- Human
- HumanoidXeno # Sunrise-Add
- Milira # Sunrise-Add
- Moth
- Predator # Sunrise-Add
- Reptilian
- Resomi # Sunrise-Add
- SlimePerson
- Swine # Sunrise-Add
- Tajaran # Sunrise-Add
- Vox
- Vulpkanin
- type: guideEntry
id: Arachnid
name: species-name-arachnid
text: "/ServerInfo/Guidebook/Mobs/Arachnid.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Arachnid.xml" # Sunrise-Edit
- type: guideEntry
id: Diona
name: species-name-diona
text: "/ServerInfo/Guidebook/Mobs/Diona.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Diona.xml" # Sunrise-Edit
- type: guideEntry
id: Dwarf
name: species-name-dwarf
text: "/ServerInfo/Guidebook/Mobs/Dwarf.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Dwarf.xml" # Sunrise-Edit
- type: guideEntry
id: Human
name: species-name-human
text: "/ServerInfo/Guidebook/Mobs/Human.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Human.xml" # Sunrise-Edit
- type: guideEntry
id: Moth
name: species-name-moth
text: "/ServerInfo/Guidebook/Mobs/Moth.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Moth.xml" # Sunrise-Edit
- type: guideEntry
id: Reptilian
name: species-name-reptilian
text: "/ServerInfo/Guidebook/Mobs/Reptilian.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Reptilian.xml" # Sunrise-Edit
- type: guideEntry
id: SlimePerson
name: species-name-slime
text: "/ServerInfo/Guidebook/Mobs/SlimePerson.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/SlimePerson.xml" # Sunrise-Edit
- type: guideEntry
id: Vox
name: species-name-vox
text: "/ServerInfo/Guidebook/Mobs/Vox.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Vox.xml" # Sunrise-Edit
- type: guideEntry
id: Vulpkanin
name: species-name-vulpkanin
text: "/ServerInfo/Guidebook/Mobs/Vulpkanin.xml"
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Vulpkanin.xml" # Sunrise-Edit
# Sunrise-Start
- type: guideEntry
id: Swine
name: species-name-swine
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Swine.xml"
- type: guideEntry
id: Tajaran
name: species-name-tajaran
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Tajaran.xml"
- type: guideEntry
id: Resomi
name: species-name-resomi
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Resomi.xml"
- type: guideEntry
id: Predator
name: species-name-predator
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Predator.xml"
- type: guideEntry
id: HumanoidXeno
name: species-name-xeno
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/HumanoidXeno.xml"
- type: guideEntry
id: Milira
name: species-name-milira
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Milira.xml"
- type: guideEntry
id: Demon
name: species-name-demon
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Demon.xml"
- type: guideEntry
id: Felinid
name: species-name-felinid
text: "/ServerInfo/Guidebook/_Sunrise/Mobs/Felinid.xml"
# Sunrise-End

View file

@ -14,16 +14,16 @@
jumpsuit: ClothingUniformJumpsuitNinja
back: ClothingBackpackSatchel
mask: ClothingMaskNinja
head: ClothingHeadHelmetSpaceNinjaBiocode # Sunrise-Edit
head: ClothingHeadHelmetSpaceNinja
eyes: ClothingEyesVisorNinja
gloves: ClothingHandsGlovesSpaceNinjaBiocode # Sunrise-Edit
outerClothing: ClothingOuterSuitSpaceNinjaBiocode # Sunrise-Edit
gloves: ClothingHandsGlovesSpaceNinja
outerClothing: ClothingOuterSuitSpaceNinja
shoes: ClothingShoesSpaceNinjaBiocode # Sunrise-Edit
id: AgentIDCard
ears: ClothingHeadsetNinja
pocket1: SpiderCharge
pocket2: HandHeldMassScanner
belt: EnergyKatanaBiocode # Sunrise-Edit
belt: EnergyKatana
inhand:
- JetpackBlackFilled
storage:

View file

@ -26,7 +26,7 @@
storage:
back:
- EmergencyRollerBedSpawnFolded
- HandheldCrewMonitor # Sunrise-Edit
- HandheldEmergencyCrewMonitor # Sunrise - edit
- type: chameleonOutfit
id: ParamedicChameleonOutfit

View file

@ -1,5 +1,5 @@
- type: entity
parent: ClothingUniformBase
parent: UnsensoredClothingUniformBase
id: ClothingUniformJumpsuitTactical
name: tactical turtleneck suit
description: A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp.
@ -8,3 +8,11 @@
sprite: _Sunrise/AssaultOperatives/tactical_suit.rsi
- type: Clothing
sprite: _Sunrise/AssaultOperatives/tactical_suit.rsi
- type: Armor
modifiers:
coefficients:
Slash: 0.92
Piercing: 0.98
Heat: 0.95
- type: StaminaResistance
damageCoefficient: 0.95

View file

@ -64,7 +64,12 @@
- type: damageModifierSet
id: Xenomorph
coefficients:
Heat: 1.15
Piercing: 1.05
Cold: 1.1
Heat: 1.2
Shock: 1.25
flatReductions:
Blunt: 4
- type: damageModifierSet
id: GorillaRampaging

View file

@ -572,9 +572,10 @@
hadOutline: true
examineThreshold: 0.9 # Sunrise-Edit
- type: StealthOnMove
passiveVisibilityRate: -0.15 # Sunrise-Edit
passiveVisibilityRate: -1
movementVisibilityRate: 0.3
- type: PowerCellDraw
drawRate: 1.8 # 200 seconds on the default cell
drawRate: 2 # 900 seconds on the default cell
- type: ToggleCellDraw
- type: PowerCellSlot
cellSlotId: cell_slot

View file

@ -5,6 +5,8 @@
components:
- type: BorgSwitchableType
selectedBorgType: security
- type: Loadout
prototypes: [ BorgArmorSiliconLoadoutMk2 ]
- type: entity
id: BorgChassisPeace

View file

@ -5,6 +5,8 @@
components:
- type: Item
size: Huge
shape:
- 0,0,4,3
- type: NestingMob
- type: MultiHandedItem
- type: CanEscapeInventory

View file

@ -39,8 +39,10 @@
attackRate: 1
damage:
types:
Blunt: 5
Slash: 10
Blunt: 3
Slash: 2
Caustic: 1
- type: LizardAccent
- type: Bloodstream
bloodReferenceSolution:
reagents:
@ -95,13 +97,14 @@
state: body-overlay-2
visible: false
- type: Hunger
baseDecayRate: 0.04
baseDecayRate: 0.08
- type: Thirst
baseDecayRate: 0.15
dehydrationDamage:
types:
Bloodloss: 0.5
Asphyxiation: 0.5
Caustic: 0.05
- type: Icon # It will not have an icon in the adminspawn menu without this. Body parts seem fine for whatever reason.
sprite: _Sunrise/Mobs/Species/HumanoidXeno/parts.rsi
state: full

View file

@ -27,6 +27,16 @@
- type: Icon
sprite: _Sunrise/Mobs/Species/Tajaran/parts.rsi
state: tajaran_m
- type: JumpAbility
action: ActionVulpkaninGravityJump
canCollide: true
jumpDistance: 3
jumpSound:
path: /Audio/Weapons/punchmiss.ogg
params:
pitch: 1.33
volume: -5
variation: 0.05
- type: MeleeWeapon
hidden: false
soundHit:
@ -37,7 +47,7 @@
damage:
types:
Blunt: 1
Slash: 2
Slash: 5
- type: Vocal
sounds:
Male: MaleTajaran
@ -47,8 +57,8 @@
currentTemperature: 310.15
specificHeat: 46
- type: TemperatureDamage
heatDamageThreshold: 400
coldDamageThreshold: 200
heatDamageThreshold: 350
coldDamageThreshold: 230
coldDamage:
types:
Cold : 0.2

View file

@ -1,35 +1,4 @@
# Ninja equipment with SpiderClan biocode
- type: entity
parent: ClothingOuterSuitSpaceNinja
id: ClothingOuterSuitSpaceNinjaBiocode
suffix: BIOCODE
components:
- type: Biocode
factions:
- SpiderClan
- type: FactionClothingBlocker
- type: entity
parent: ClothingHandsGlovesSpaceNinja
id: ClothingHandsGlovesSpaceNinjaBiocode
suffix: BIOCODE
components:
- type: Biocode
factions:
- SpiderClan
- type: FactionClothingBlocker
- type: entity
parent: ClothingHeadHelmetSpaceNinja
id: ClothingHeadHelmetSpaceNinjaBiocode
suffix: BIOCODE
components:
- type: Biocode
factions:
- SpiderClan
- type: FactionClothingBlocker
# Special ninja shoes - non-removable and no effects for non-ninja
- type: entity
parent: ClothingShoesSpaceNinja
@ -42,12 +11,3 @@
- type: FactionClothingBlocker
- type: Unremoveable
deleteOnDrop: false
- type: entity
parent: EnergyKatana
id: EnergyKatanaBiocode
suffix: BIOCODE
components:
- type: Biocode
factions:
- SpiderClan

View file

@ -8,15 +8,18 @@
tags:
- BrigmedicBeltEquip
- type: Sprite
sprite: _Starlight/Objects/Specific/Medical/handheld_brigmedic_crewmonitor.rsi
sprite: _Sunrise/Objects/Specific/Medical/handheld_brigmedic_crewmonitor.rsi
state: scanner
- type: ActivatableUI
key: enum.CrewMonitoringUIKey.Key
- type: UserInterface
interfaces:
enum.CrewMonitoringUIKey.Key:
type: BrigmedicCrewMonitoringBoundUserInterface
type: SunriseCrewMonitoringBoundUserInterface
- type: CrewMonitoringConsole
- type: CrewMonitoringFilter
allowedDepartmentIds:
- Security
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: CrewMonitor

View file

@ -12,8 +12,12 @@
- type: UserInterface
interfaces:
enum.CrewMonitoringUIKey.Key:
type: BSOCrewMonitoringBoundUserInterface
type: SunriseCrewMonitoringBoundUserInterface
- type: CrewMonitoringConsole
- type: CrewMonitoringFilter
allowedDepartmentIds:
- Command
includeTrackers: true
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: CrewMonitor

View file

@ -0,0 +1,39 @@
- type: entity
name: PulseGuard™ XR
categories: [ DoNotMap ]
parent: [ BaseHandheldComputer ]
id: HandheldEmergencyCrewMonitor
description: A hand-held crew monitor displaying the status of suit sensors of injured crew.
components:
- type: Item
heldPrefix: scanner
- type: Sprite
sprite: _Sunrise/Objects/Specific/Medical/handheld_emergency_crewmonitor.rsi
state: scanner
- type: ActivatableUI
key: enum.CrewMonitoringUIKey.Key
- type: UserInterface
interfaces:
enum.CrewMonitoringUIKey.Key:
type: SunriseCrewMonitoringBoundUserInterface
- type: CrewMonitoringConsole
- type: CrewMonitoringFilter
onlyShowWoundedOrDead: true
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: CrewMonitor
- type: WirelessNetworkConnection
range: 500
- type: StationLimitedNetwork
- type: StaticPrice
price: 500
- type: entity
id: HandheldEmergencyCrewMonitorEmpty
parent: HandheldEmergencyCrewMonitor
suffix: Empty
components:
- type: ItemSlots
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default

View file

@ -1,4 +1,4 @@
- type: entity
- type: entity # Sunrise-TODO: Добавить кастомизацию и возможность менять/Заменять и улучшать броню боргов
id: BorgArmorSilicon
name: armor plating
parent: ClothingOuterBase
@ -18,9 +18,9 @@
coefficients:
Blunt: 1
Slash: 0.7
Piercing: 0.7
Piercing: 0.6
Heat: 0.5
Shock: 2
Shock: 0.7
Caustic: 1
Structural: 0.2
- type: Unremoveable
@ -47,9 +47,9 @@
coefficients:
Blunt: 0.9
Slash: 0.6
Piercing: 0.6
Piercing: 0.5
Heat: 0.5
Shock: 2
Shock: 0.6
Caustic: 1
Structural: 0.2
- type: Unremoveable

View file

@ -7,11 +7,15 @@
# Sunrise-start
Pirate: 1
PirateSmall: 0.25
LoneOperative: 1
Instigator: 0.6
Infiltrator: 0.35
Nukeops: 0.05
LoneOperative: 0.25
Instigator: 0.25
Infiltrator: 0.5
Abductors: 0.5
Clown: 1
NinjaBackup: 0.75
Rod: 0.75
Wizard: 0.1
Nukeops: 0.1
# Sunrise-end
- type: ninjaHackingThreat
@ -59,4 +63,24 @@
id: Abductors
announcement: terror-abductor
rule: AbductorsSpawn
- type: ninjaHackingThreat
id: Clown
announcement: terror-clown
rule: UnknownShuttleHonki
- type: ninjaHackingThreat
id: NinjaBackup
announcement: terror-ninja
rule: NinjaSpawn
- type: ninjaHackingThreat
id: Rod
announcement: terror-rod
rule: ImmovableRodSpawn
- type: ninjaHackingThreat
id: Wizard
announcement: terror-wizard
rule: WizardSpawn
# Sunrise-end

View file

@ -0,0 +1,25 @@
<Document>
# Арахниды
<Box>
<GuideEntityEmbed Entity="MobArachnid" Caption=""/>
</Box>
У них есть два дополнительных кармана в инвентаре. Они могут есть сырое мясо без последствий, но некоторые продукты вроде шоколада и лука для них ядовиты.
Они задыхаются на 50% быстрее, а их синюю кровь нельзя восполнить из железа — Ведь их кровь основана на меди.
Их безоружные атаки наносят колющий урон вместо дробящего.
## Шелководство
<Box>
<GuideEntityEmbed Entity="MaterialWebSilk" Caption=""/>
<GuideEntityEmbed Entity="ClothingUniformJumpskirtWeb" Caption=""/>
<GuideEntityEmbed Entity="WebShield" Caption=""/>
<GuideEntityEmbed Entity="WallWeb" Caption=""/>
</Box>
Арахниды могут создавать паутину (шёлк) ценой насыщения. Они (и только они) могут плести из паутины различные предметы — от одежды и щитов до целых стен.
</Document>

View file

@ -0,0 +1,11 @@
<Document>
# Арканы
<Box>
<GuideEntityEmbed Entity="MobDemon" Caption=""/>
</Box>
Арканы наносят колющий урон безоружными атаками.
В остальном они близки к обычным гуманоидам.
</Document>

View file

@ -0,0 +1,44 @@
<Document>
# Дионы
<Box>
<GuideEntityEmbed Entity="MobDiona" Caption=""/>
</Box>
Они не могут носить обувь, но не замедляются в кудзу.
Голод и жажда наступают у них медленнее.
Их «кровь» — древесный сок, который нельзя восполнить из железа.
Будучи растениями, они отравляются гербицидом, а Robust Harvest лечит их (но при злоупотреблении это рискованно!).
Они получают [color=#1e90ff]на 30% меньше дробящего урона и на 20% меньше режущего[/color];
но получают [color=#ffa500]на 50% больше урона от жара, на 20% больше урона от электричества и
легко загораются при получении достаточного теплового урона из любого источника.[/color]
## Сделай как дерево и пусти корни
<Box>
<GuideEntityEmbed Entity="FloraTree" Caption=""/>
</Box>
При воздействии большого количества Robust Harvest диона начинает неконтролируемо расти и превращается в неподвижное дерево (сбрасывая всё снаряжение).
Срубив дерево, можно «вернуть» диону в подвижное состояние.
## Нимфы дион
<Box>
<GuideEntityEmbed Entity="MobDionaNymph" Caption=""/>
<GuideEntityEmbed Entity="MobDionaNymph" Caption=""/>
<GuideEntityEmbed Entity="MobDionaNymph" Caption=""/>
</Box>
После смерти диона может добровольно уничтожить своё тело и выпустить «внутренние органы» в виде трёх нимф,
при этом игрок получает контроль над мозговой нимфой.
Она может говорить, но у неё нет рук и инвентаря, и она мало на что способна. Из-за маленького размера нимфа
может помещаться в сумку или рюкзак, но самостоятельно туда забраться у неё не выйдет.
Их небольшой вид позволяет протиснуться в вентиляционные трубы.
<Box>
<GuideEntityEmbed Entity="GasVentScrubber" Caption=""/>
<GuideEntityEmbed Entity="ClothingBackpackSatchel" Caption=""/>
<GuideEntityEmbed Entity="GasVentPump" Caption=""/>
</Box>
Через 10 минут нимфа может восстановиться в полноценную диону. Это будет новое случайно сгенерированное тело с новым именем,
и, кроме их слов, почти не останется следов того, кем они были раньше.
</Document>

View file

@ -0,0 +1,14 @@
<Document>
# Дворфы
<Box>
<GuideEntityEmbed Entity="MobDwarf" Caption=""/>
</Box>
Дворфы во многом похожи на людей, но лучше переносят алкоголь, а спиртное их лечит.
<Box>
<GuideEntityEmbed Entity="DrinkBeerBottleFull" Caption=""/>
</Box>
Дворфам необходимо пить алкоголь для утоления жажды, обычная вода же наоборот сушит их организм.
</Document>

View file

@ -0,0 +1,21 @@
<Document>
# Фелиниды
<Box>
<GuideEntityEmbed Entity="MobFelinid" Caption=""/>
</Box>
Фелиниды обладают когтями, наносящими режущий урон в ближнем бою, и характерным «кошачьим» акцентом.
У них есть переключаемое ночное зрение.
Они могут вылизывать раны, что помогает остановить кровотечение и ускорить восстановление.
По защите они немного отличаются от стандартных гуманоидов:
они получают [color=#ffa500]на 10% больше дробящего, режущего и колющего урона[/color],
[color=#ffa500]на 10% больше урона от жара[/color],
но [color=#1e90ff]на 10% меньше урона от холода[/color].
<Box>
<GuideEntityEmbed Entity="ClothingBackpackDuffel" Caption=""/>
</Box>
Фелиниды достаточно компактны, чтобы при необходимости уместиться в сумке.
</Document>

View file

@ -0,0 +1,11 @@
<Document>
# Люди
<Box>
<GuideEntityEmbed Entity="MobHumanDummy" Caption=""/>
</Box>
В зависимости от того, кого спросить, люди либо ничем не примечательны, либо являются универсальным эталоном, с которым сравнивают всех остальных.
У них нет особых механик или заметных отличий.
</Document>

View file

@ -0,0 +1,23 @@
<Document>
# Ксеноморфы (гуманоидные)
<Box>
<GuideEntityEmbed Entity="MobHumanoidXeno" Caption=""/>
</Box>
Гуманоидные ксеноморфы устойчивы к перепадам давления и обладают кислотной кровью.
Их безоружные атаки значительно сильнее обычных.
<Box>
<GuideReagentEmbed Reagent="FluorosulfuricAcidHumanoidXeno"/>
</Box>
Они получают [color=#ffa500]на 5% больше колющего урона[/color],
[color=#ffa500]на 10% больше урона от холода[/color],
[color=#ffa500]на 20% больше урона от жара[/color],
[color=#ffa500]на 25% больше урона от электричества[/color].
Они быстрее испытывают голод и хотят чаще есть.
Удушье накапливается у них заметно медленнее, поэтому даже в критическом состоянии они могут долго ждать помощи.
</Document>

View file

@ -0,0 +1,22 @@
<Document>
# Милира
<Box>
<GuideEntityEmbed Entity="MobMilira" Caption=""/>
</Box>
Милира обладают крыльями (их можно раскрывать и складывать).
Они чувствительны к высоким температурам.
Они получают [color=#ffa500]на 15% больше дробящего и теплового урона[/color],
[color=#ffa500]на 10% больше режущего и колющего урона[/color],
но [color=#1e90ff]на 30% меньше урона от холода[/color].
<Box>
<GuideEntityEmbed Entity="ClothingOuterArmorMiliraLight" Caption=""/>
<GuideEntityEmbed Entity="ClothingOuterArmorMiliraHeavy" Caption=""/>
<GuideEntityEmbed Entity="ClothingOuterHardsuitMilira" Caption=""/>
</Box>
Обычная броня и скафандры блокируют милирам свободное раскрытие крыльев, но особая броня специально для них позволяет раскрывать крылья и летать.
</Document>

View file

@ -0,0 +1,21 @@
<Document>
# Нианы
<Box>
<GuideEntityEmbed Entity="MobMoth" Caption=""/>
</Box>
Они могут есть хлопок, ткани и одежду, но почти не способны есть то, что другие считают «едой». Им подходит несколько более низкий диапазон температур, чем людям.
Их насекомая кровь не может быть восполнена из железа, как обычная.
Крылья дают им лучшее ускорение при отсутствии гравитации на станции, но без оборудования в открытом космосе они всё равно не могут двигаться.
Они получают [color=#1e90ff]на 30% меньше урона от холода[/color], но [color=#ffa500]на 20% больше урона от жара и легче загораются[/color].
<Box>
<GuideEntityEmbed Entity="MobMothroach" Caption=""/>
<GuideEntityEmbed Entity="LightBulb" Caption=""/>
<GuideEntityEmbed Entity="MobMothroach" Caption=""/>
<GuideEntityEmbed Entity="LightBulb" Caption=""/>
<GuideEntityEmbed Entity="MobMothroach" Caption=""/>
</Box>
</Document>

View file

@ -0,0 +1,20 @@
<Document>
# Яутжа
<Box>
<GuideEntityEmbed Entity="MobPredator" Caption=""/>
</Box>
Яутжа имеют кислотную кровь.
<Box>
<GuideReagentEmbed Reagent="FluorosulfuricAcidPredator"/>
</Box>
Они получают [color=#1e90ff]на 20% меньше дробящего и колющего урона[/color],
[color=#1e90ff]на 15% меньше режущего урона[/color],
[color=#1e90ff]на 10% меньше урона от электричества[/color],
[color=#1e90ff]на 20% меньше урона от яда[/color],
но [color=#ffa500]на 15% больше урона от жара и холода[/color].
</Document>

View file

@ -0,0 +1,16 @@
<Document>
# Унатхи
<Box>
<GuideEntityEmbed Entity="MobReptilian" Caption=""/>
</Box>
Они могут есть ТОЛЬКО фрукты и мясо, но без вреда употребляют сырое мясо и пьют кровь.
Им подходит несколько более высокий диапазон температур, чем людям.
Они могут тянуть предметы хвостом, освобождая обе руки.
Их безоружные атаки когтями наносят режущий урон вместо дробящего.
Они получают [color=#ffa500]на 30% больше урона от холода.[/color]
</Document>

View file

@ -0,0 +1,17 @@
<Document>
# Резоми
<Box>
<GuideEntityEmbed Entity="MobResomi" Caption=""/>
</Box>
Резоми могут прыгать на короткие дистанции и имеют когти.
Они довольно хрупкие и хуже переносят высокие температуры.
У них есть переключаемое ночное зрение.
Они получают [color=#ffa500]на 25% больше урона от жара[/color],
но [color=#1e90ff]на 25% меньше урона от холода[/color].
Из-за небольшого роста резоми можно переносить в сумке или рюкзаке.
</Document>

View file

@ -0,0 +1,24 @@
<Document>
# Слаймолюди
<Box>
<GuideEntityEmbed Entity="MobSlimePerson" Caption=""/>
</Box>
Они дышат азотом вместо кислорода. Азот есть в воздухе станции, но его сложнее найти в сжатых баллонах. Они получают серьёзный урон, если их обливают или опрыскивают водой, но
(как и другие виды) могут пить воду безопасно, чтобы утолить жажду.
Они выдыхают закись азота и не подвержены её эффектам.
Их тело может перерабатывать 6 реагентов одновременно вместо 2.
У слаймолюдей есть [bold]внутренний инвентарь 2x3[/bold] в их слизистой мембране. Любой может увидеть, что там лежит, и достать это без вашего согласия,
так что будьте осторожны.
У слаймолюдей примерно [color=#1e90ff]вдвое[/color] выше регенерация по сравнению с другими гуманоидами, и они могут восстанавливаться от значительно больших ран — вплоть до [color=#1e90ff]втрое[/color] быстрее, чем остальные.
Их слизистая «кровь» не может восполняться из железа. Слизь — это источник
умеренно питательной пищи для других видов, хотя пить кровь коллег обычно не одобряется.
Они задыхаются на 80% медленнее, но получают на 9% больше урона от давления. Это делает их наиболее способными к выживанию в жёстком вакууме. На какое-то время.
Они получают [color=#1e90ff]на 40% меньше дробящего урона[/color], но [color=#ffa500]на 50% больше урона от холода, на 20% больше режущего и на 20% больше колющего урона[/color].
</Document>

View file

@ -0,0 +1,34 @@
<Document>
# Расы
Нанотрейзен использует множество разумных видов.
<Box>
<GuideEntityEmbed Entity="MobArachnid" Caption="Арахнид"/>
<GuideEntityEmbed Entity="MobDiona" Caption="Диона"/>
<GuideEntityEmbed Entity="MobDwarf" Caption="Дворф"/>
<GuideEntityEmbed Entity="MobHuman" Caption="Человек"/>
</Box>
<Box>
<GuideEntityEmbed Entity="MobMoth" Caption="Ниан"/>
<GuideEntityEmbed Entity="MobReptilian" Caption="Унатх"/>
<GuideEntityEmbed Entity="MobSlimePerson" Caption="Слаймолюд"/>
<GuideEntityEmbed Entity="MobVox" Caption="Вокс"/>
</Box>
<Box>
<GuideEntityEmbed Entity="MobVulpkanin" Caption="Вульпканин"/>
<GuideEntityEmbed Entity="MobFelinid" Caption="Фелинид"/>
<GuideEntityEmbed Entity="MobSwine" Caption="Троттин"/>
<GuideEntityEmbed Entity="MobTajaran" Caption="Таяран"/>
</Box>
<Box>
<GuideEntityEmbed Entity="MobPredator" Caption="Яутжа"/>
<GuideEntityEmbed Entity="MobHumanoidXeno" Caption="Ксеноморф"/>
<GuideEntityEmbed Entity="MobDemon" Caption="Аркана"/>
<GuideEntityEmbed Entity="MobResomi" Caption="Резоми"/>
</Box>
<Box>
<GuideEntityEmbed Entity="MobMilira" Caption="Милира"/>
</Box>
</Document>

View file

@ -0,0 +1,14 @@
<Document>
# Троттины
<Box>
<GuideEntityEmbed Entity="MobSwine" Caption=""/>
</Box>
Троттины быстро голодают и испытывают жажду.
Они медленнее других гуманоидов и обладают характерным акцентом.
Они получают [color=#1e90ff]на 20% меньше дробящего и режущего урона[/color],
а также [color=#1e90ff]на 20% меньше урона от холода[/color].
</Document>

View file

@ -0,0 +1,15 @@
<Document>
# Таяраны
<Box>
<GuideEntityEmbed Entity="MobTajaran" Caption=""/>
</Box>
Таяраны: кошачья раса с когтями и характерным акцентом.
Их безоружные атаки наносят режущий урон.
У них есть переключаемое ночное зрение.
Вспышки действуют на них сильнее обычного.
Они умеют прыгать на короткие дистанции, как вульпы.
</Document>

View file

@ -0,0 +1,28 @@
<Document>
# Воксы
<Box>
<GuideEntityEmbed Entity="MobVox" Caption=""/>
</Box>
[color=#ffa500]Внимание! Эта раса не рекомендуется новичкам из-за смертельной аллергии на кислород![/color]
Воксы дышат азотом, а [color=#ffa500]кислород для них токсичен.[/color]
К сожалению, на станциях полно кислорода,
поэтому воксы почти всегда должны использовать внутренний источник азота, чтобы избежать смертельного воздействия.
Воксы всегда появляются с рабочим азотным дыхательным оборудованием.
В их коробке выживания есть запасная дыхательная маска и аварийный азотный баллон.
Помимо обычной еды, воксы могут без последствий есть упаковки от снеков, кожуру банана, скорлупу яиц и сырое мясо. Также им нравится пить сварочное топливо.
Воксы [color=#1e90ff]медленно восстанавливаются от небольших уровней урона ядом[/color] самостоятельно,
если не превышать 20 единиц урона ядом.
Это позволяет им выдерживать дыхание станционным воздухом до тридцати секунд без длительного вреда,
чтобы быстро поесть, попить, принять таблетки и т. п.
Восстановление после 30 секунд воздействия кислорода занимает у них около двух минут.
Если их здоровье не улучшается в течение минуты после воздействия кислорода, им следует обратиться за медицинской помощью.
Воксы наносят режущий урон безоружными атаками.
</Document>

View file

@ -0,0 +1,18 @@
<Document>
# Вульпканины
<Box>
<GuideEntityEmbed Entity="MobVulpkanin" Caption=""/>
</Box>
Вульпканины из-за густого меха [color=#1e90ff]предпочитают более холодные температуры[/color] и [color=#ffa500]быстрее перегреваются.[/color]
Их ловкие (но неуклюжие) ноги позволяют им прыгать на короткие дистанции — аккуратнее, чтобы не врезаться!
Их диета позволяет безопасно есть сырое мясо, но теобромин для них ядовит.
Их необычная форма морды затрудняет питьё, поэтому иногда часть выпитой жидкости проливается на пол.
Они получают [color=#1e90ff]на 15% меньше урона от холода[/color], но [color=#ffa500]на 15% больше урона от жара.[/color]
Вспышки действуют на них сильнее обычного.
</Document>

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View file

@ -0,0 +1,33 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation https://github.com/tgstation/tgstation/commit/ab4abf318f293a701754656dd4e9261eb70f8824#diff-9ab5c8a5e47ab7cfaeadd859a23e32b05de1fe839e99ea767fd7e340b6385d67, in-hands modified by SeamLesss (github) from Objects/Misc/qm_clipboard.rsi/inhand-(left/right) respritet by seraphimttt (discord)",
"states": [
{
"name": "scanner",
"directions": 1,
"delays": [
[
0.4,
0.4
]
]
},
{
"name": "scanner-inhand-left",
"directions": 4
},
{
"name": "scanner-inhand-right",
"directions": 4
},
{
"name": "icon",
"directions": 1
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 626 B

View file

@ -920,6 +920,10 @@ VendingMachineMayson: null
DrinkLukin: null
DrinkFourteenLokoPlusCan: null
MagazineAsh12: null
ClothingHeadHelmetSpaceNinjaBiocode: null
ClothingHandsGlovesSpaceNinjaBiocode: null
ClothingOuterSuitSpaceNinjaBiocode: null
EnergyKatanaBiocode: null
# SL HITSCAN START
BoxMagazineRifle: BoxMagazineRifleSP
MagazineBoxLightRifle: MagazineBoxLightRifleSP