Body scanner and new health analyzer UI (#445)
* Body scanner and new health analyzer UI * fix --------- Co-authored-by: XDRmix <xdrmix@mail.ru>
@@ -1,33 +1,26 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
SetSize="250 100">
|
||||
<ScrollContainer
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RootContainer"
|
||||
Orientation="Vertical">
|
||||
<Label
|
||||
Name="NoPatientDataText"
|
||||
Text="{Loc health-analyzer-window-no-patient-data-text}" />
|
||||
<BoxContainer
|
||||
Name="PatientDataContainer"
|
||||
Orientation="Vertical"
|
||||
Margin="0 0 5 10">
|
||||
<Label
|
||||
Name="PatientName"/>
|
||||
<Label
|
||||
Name="Temperature"
|
||||
Margin="0 5 0 0"/>
|
||||
<Label
|
||||
Name="BloodLevel"
|
||||
Margin="0 5 0 0"/>
|
||||
<Label
|
||||
Name="patientDamageAmount"
|
||||
Margin="0 15 0 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer
|
||||
Name="GroupsContainer"
|
||||
Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
<!-- WD start -->
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Title="{Loc 'gravity-generator-window-title'}"
|
||||
MinSize="260 300">
|
||||
<BoxContainer Margin="4 0" Orientation="Vertical">
|
||||
<controls:StripeBack>
|
||||
<Label Name="EntityNameLabel" Text="N/A" StyleClasses="LabelBig" Align="Center"/>
|
||||
</controls:StripeBack>
|
||||
<GridContainer Columns="2">
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-current-alive-status-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="AliveStatusLabel" Text="{Loc 'health-analyzer-window-no-data'}" Margin="4 0 0 0"/>
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="TemperatureLabel" Text="{Loc 'health-analyzer-window-no-data'}" Margin="4 0 0 0"/>
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="BloodLevelLabel" Text="{Loc 'health-analyzer-window-no-data'}" Margin="4 0 0 0"/>
|
||||
<Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="TotalDamageLabel" Text="{Loc 'health-analyzer-window-no-data'}" Margin="4 0 0 0"/>
|
||||
</GridContainer>
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 0 0 10"/>
|
||||
<!-- Filled by code -->
|
||||
<GridContainer Name="DamageGroupsContainer" Columns="2"/>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
<!-- WD end -->
|
||||
|
||||
@@ -1,189 +1,67 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.White.Medical.BodyScanner;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.MedicalScanner;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.HealthAnalyzer.UI
|
||||
{
|
||||
// WD start
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HealthAnalyzerWindow : DefaultWindow
|
||||
public sealed partial class HealthAnalyzerWindow : FancyWindow
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly IPrototypeManager _prototypes;
|
||||
private readonly IResourceCache _cache;
|
||||
|
||||
private const int AnalyzerHeight = 430;
|
||||
private const int AnalyzerWidth = 300;
|
||||
|
||||
public HealthAnalyzerWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
var dependencies = IoCManager.Instance!;
|
||||
_entityManager = dependencies.Resolve<IEntityManager>();
|
||||
_spriteSystem = _entityManager.System<SpriteSystem>();
|
||||
_prototypes = dependencies.Resolve<IPrototypeManager>();
|
||||
_cache = dependencies.Resolve<IResourceCache>();
|
||||
}
|
||||
|
||||
public void Populate(HealthAnalyzerScannedUserMessage msg)
|
||||
{
|
||||
GroupsContainer.RemoveAllChildren();
|
||||
var entities = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
var target = _entityManager.GetEntity(msg.TargetEntity);
|
||||
|
||||
if (target == null
|
||||
|| !_entityManager.TryGetComponent<DamageableComponent>(target, out var damageable))
|
||||
if (msg.TargetEntity != null &&
|
||||
entities.TryGetComponent<DamageableComponent>(entities.GetEntity(msg.TargetEntity), out var damageable))
|
||||
{
|
||||
NoPatientDataText.Visible = true;
|
||||
return;
|
||||
}
|
||||
EntityNameLabel.Text = Identity.Name(entities.GetEntity(msg.TargetEntity.Value), entities);
|
||||
TemperatureLabel.Text = float.IsNaN(msg.Temperature) ? Loc.GetString("health-analyzer-window-no-data") : $"{msg.Temperature - 273f:F1} \u00B0C";
|
||||
BloodLevelLabel.Text = float.IsNaN(msg.BloodLevel) ? Loc.GetString("health-analyzer-window-no-data") : $"{msg.BloodLevel * 100:F1} %";
|
||||
TotalDamageLabel.Text = damageable.TotalDamage.ToString();
|
||||
|
||||
NoPatientDataText.Visible = false;
|
||||
entities.TryGetComponent<MobStateComponent>(entities.GetEntity(msg.TargetEntity), out var mobStateComponent);
|
||||
|
||||
string entityName = Loc.GetString("health-analyzer-window-entity-unknown-text");
|
||||
if (_entityManager.HasComponent<MetaDataComponent>(target.Value))
|
||||
{
|
||||
entityName = Identity.Name(target.Value, _entityManager);
|
||||
}
|
||||
|
||||
PatientName.Text = Loc.GetString(
|
||||
"health-analyzer-window-entity-health-text",
|
||||
("entityName", entityName)
|
||||
);
|
||||
|
||||
Temperature.Text = Loc.GetString("health-analyzer-window-entity-temperature-text",
|
||||
("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - 273f:F1} °C")
|
||||
);
|
||||
|
||||
BloodLevel.Text = Loc.GetString("health-analyzer-window-entity-blood-level-text",
|
||||
("bloodLevel", float.IsNaN(msg.BloodLevel) ? "N/A" : $"{msg.BloodLevel * 100:F1} %")
|
||||
);
|
||||
|
||||
patientDamageAmount.Text = Loc.GetString(
|
||||
"health-analyzer-window-entity-damage-total-text",
|
||||
("amount", damageable.TotalDamage)
|
||||
);
|
||||
|
||||
var damageSortedGroups =
|
||||
damageable.DamagePerGroup.OrderBy(damage => damage.Value)
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
|
||||
|
||||
DrawDiagnosticGroups(damageSortedGroups, damagePerType);
|
||||
|
||||
SetHeight = AnalyzerHeight;
|
||||
SetWidth = AnalyzerWidth;
|
||||
}
|
||||
|
||||
private void DrawDiagnosticGroups(
|
||||
Dictionary<string, FixedPoint2> groups, IReadOnlyDictionary<string, FixedPoint2> damageDict)
|
||||
{
|
||||
HashSet<string> shownTypes = new();
|
||||
|
||||
// Show the total damage and type breakdown for each damage group.
|
||||
foreach (var (damageGroupId, damageAmount) in groups.Reverse())
|
||||
{
|
||||
if (damageAmount == 0)
|
||||
continue;
|
||||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
var groupContainer = new BoxContainer
|
||||
AliveStatusLabel.Text = mobStateComponent?.CurrentState switch
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 15),
|
||||
Align = BoxContainer.AlignMode.Begin,
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
Shared.Mobs.MobState.Alive => Loc.GetString("health-analyzer-window-entity-current-alive-status-alive-text"),
|
||||
Shared.Mobs.MobState.Critical => Loc.GetString("health-analyzer-window-entity-current-alive-status-critical-text"),
|
||||
Shared.Mobs.MobState.Dead => Loc.GetString("health-analyzer-window-entity-current-alive-status-dead-text"),
|
||||
_ => Loc.GetString("health-analyzer-window-no-data"),
|
||||
};
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId, damageAmount.Int()));
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerGroup = damageable.DamagePerGroup;
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
|
||||
|
||||
GroupsContainer.AddChild(groupContainer);
|
||||
DamageGroupsContainer.RemoveAllChildren();
|
||||
|
||||
// Show the damage for each type in that group.
|
||||
var group = _prototypes.Index<DamageGroupPrototype>(damageGroupId);
|
||||
|
||||
foreach (var type in group.DamageTypes)
|
||||
// Show the total damage and type breakdown for each damage group.
|
||||
foreach (var (damageGroupId, damageAmount) in damagePerGroup)
|
||||
{
|
||||
if (damageDict.TryGetValue(type, out var typeAmount) && typeAmount > 0)
|
||||
{
|
||||
// If damage types are allowed to belong to more than one damage group,
|
||||
// they may appear twice here. Mark them as duplicate.
|
||||
if (shownTypes.Contains(type))
|
||||
continue;
|
||||
var damageGroupTitle = Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId, ("amount", damageAmount));
|
||||
|
||||
shownTypes.Add(type);
|
||||
|
||||
var damageString = Loc.GetString(
|
||||
"health-analyzer-window-damage-type-text",
|
||||
("damageType", Loc.GetString("health-analyzer-window-damage-type-" + type)),
|
||||
("amount", typeAmount)
|
||||
);
|
||||
|
||||
groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, "- ")));
|
||||
}
|
||||
DamageGroupsContainer.AddChild(new GroupDamageCardComponent(damageGroupTitle, damageGroupId, damagePerType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Texture GetTexture(string texture)
|
||||
{
|
||||
var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
|
||||
var rsiSprite = new SpriteSpecifier.Rsi(rsiPath, texture);
|
||||
|
||||
var rsi = _cache.GetResource<RSIResource>(rsiSprite.RsiPath).RSI;
|
||||
if (!rsi.TryGetState(rsiSprite.RsiState, out var state))
|
||||
{
|
||||
rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown");
|
||||
}
|
||||
|
||||
return _spriteSystem.Frame0(rsiSprite);
|
||||
}
|
||||
|
||||
private static Label CreateDiagnosticItemLabel(string text)
|
||||
{
|
||||
return new Label
|
||||
{
|
||||
Margin = new Thickness(2, 2),
|
||||
Text = text,
|
||||
};
|
||||
}
|
||||
|
||||
private BoxContainer CreateDiagnosticGroupTitle(string text, string id, int damageAmount)
|
||||
{
|
||||
var rootContainer = new BoxContainer
|
||||
{
|
||||
VerticalAlignment = VAlignment.Bottom,
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal
|
||||
};
|
||||
|
||||
rootContainer.AddChild(new TextureRect
|
||||
{
|
||||
Margin = new Thickness(0, 3),
|
||||
SetSize = new Vector2(30, 30),
|
||||
Texture = GetTexture(id.ToLower())
|
||||
});
|
||||
|
||||
rootContainer.AddChild(CreateDiagnosticItemLabel(text));
|
||||
|
||||
return rootContainer;
|
||||
}
|
||||
}
|
||||
// WD end
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Content.Shared.White.Medical.BodyScanner;
|
||||
|
||||
namespace Content.Client.White.Medical.BodyScanner
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class BodyScannerConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private BodyScannerConsoleWindow? _window;
|
||||
|
||||
public BodyScannerConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = new BodyScannerConsoleWindow();
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
|
||||
_window.OnScanButtonPressed += () => StartScanning();
|
||||
_window.OnPrintButtonPressed += () => StartPrinting();
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case BodyScannerConsoleBoundUserInterfaceState msg:
|
||||
_window?.UpdateUserInterface(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartScanning()
|
||||
{
|
||||
SendMessage(new BodyScannerStartScanningMessage());
|
||||
}
|
||||
|
||||
public void StartPrinting()
|
||||
{
|
||||
SendMessage(new BodyScannerStartPrintingMessage());
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnClose -= Close;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc 'body-scanner-console-window-title'}"
|
||||
MinSize="830 670">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Name="MainContainer"
|
||||
Margin="4 0"
|
||||
Orientation="Vertical"
|
||||
Visible="False">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<!-- Entity name -->
|
||||
<controls:StripeBack>
|
||||
<Label Name="EntityNameLabel" Text="{Loc 'body-scanner-console-window-no-data'}" StyleClasses="LabelBig" Align="Center"/>
|
||||
</controls:StripeBack>
|
||||
<!-- Main box -->
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<!-- First column -->
|
||||
<BoxContainer Orientation="Vertical" MinSize="300 490">
|
||||
<GridContainer Columns="2">
|
||||
<Label Text="{Loc 'body-scanner-console-window-current-alive-status-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="AliveStatusLabel" Text="{Loc 'body-scanner-console-window-no-data'}" Margin="4 0 0 0"/>
|
||||
<Label Text="{Loc 'body-scanner-console-window-temperature-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="TemperatureLabel" Text="{Loc 'body-scanner-console-window-no-data'}" Margin="4 0 0 0"/>
|
||||
<Label Text="{Loc 'body-scanner-console-window-entity-blood-level-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="BloodLevelLabel" Text="{Loc 'body-scanner-console-window-no-data'}" Margin="4 0 0 0"/>
|
||||
<Label Text="{Loc 'body-scanner-console-window-entity-damage-total-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="TotalDamageLabel" Text="{Loc 'body-scanner-console-window-no-data'}" Margin="4 0 0 0"/>
|
||||
</GridContainer>
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 5"/>
|
||||
<GridContainer Name="DamageGroupsContainer" Columns="2"/>
|
||||
</BoxContainer>
|
||||
<!-- Second column -->
|
||||
<customControls:VSeparator StyleClasses="LowDivider" Margin="5 0"/>
|
||||
<BoxContainer Orientation="Vertical" MinSize="255 485">
|
||||
<GridContainer Columns="2">
|
||||
<!-- Temperature -->
|
||||
<BoxContainer Orientation="Vertical" Margin="4 0 4 4">
|
||||
<Label Text="{Loc 'body-scanner-console-window-temperature-group-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="CurrentTemperature"/>
|
||||
<Label Name="HeatDamageThreshold"/>
|
||||
<Label Name="ColdDamageThreshold"/>
|
||||
</BoxContainer>
|
||||
<!-- Saturation -->
|
||||
<BoxContainer Orientation="Vertical" Margin="4 0 4 4">
|
||||
<Label Text="{Loc 'body-scanner-console-window-saturation-group-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="CurrentSaturation"/>
|
||||
<Label Name="MinimumSaturation"/>
|
||||
<Label Name="MaximumSaturation"/>
|
||||
</BoxContainer>
|
||||
<!-- Thirst -->
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<Label Text="{Loc 'body-scanner-console-window-thirst-group-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="CurrentThirst"/>
|
||||
<Label Name="CurrentThirstStatus"/>
|
||||
<Label Name="CurrentThirstThreshold"/>
|
||||
</BoxContainer>
|
||||
<!-- Hunger -->
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<Label Text="{Loc 'body-scanner-console-window-hunger-group-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="CurrentHunger"/>
|
||||
<Label Name="CurrentHungerStatus"/>
|
||||
<Label Name="CurrentHungerThreshold"/>
|
||||
</BoxContainer>
|
||||
</GridContainer>
|
||||
<!-- Blood solution-->
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<Label Text="{Loc 'body-scanner-console-window-blood-solutions-group-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="BloodSolutionVolume"/>
|
||||
<PanelContainer VerticalExpand="True" MinSize="0 100">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer HorizontalExpand="True" MinSize="0 100">
|
||||
<BoxContainer Name="BloodSolutionElements" Orientation="Vertical" Margin="4" HorizontalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
<!-- Chemical solution-->
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<Label Text="{Loc 'body-scanner-console-window-chemical-solutions-group-text'}" StyleClasses="StatusFieldTitle"/>
|
||||
<Label Name="ChemicalSolutionVolume"/>
|
||||
<PanelContainer VerticalExpand="True" MinSize="0 100">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer HorizontalExpand="True" MinSize="0 100">
|
||||
<BoxContainer Name="ChemicalSolutionElements" Orientation="Vertical" Margin="4" HorizontalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Third column -->
|
||||
<customControls:VSeparator StyleClasses="LowDivider" Margin="5 0"/>
|
||||
<BoxContainer Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<ProgressBar Name="HealthBar" HorizontalAlignment="Center" VerticalAlignment="Center" MinValue="0" MaxValue="1" MinSize="200 20" Margin="10"/>
|
||||
<PanelContainer Margin="10 0 0 0" StyleClasses="Inset" VerticalAlignment="Center" >
|
||||
<SpriteView Name="EntityView" OverrideDirection="South" Scale="8 8" />
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Main panel bottom row -->
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 5"/>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Mind" Margin="5 0 0 0"/>
|
||||
<customControls:VSeparator StyleClasses="LowDivider" Margin="10 0"/>
|
||||
<Label Name="DNA"/>
|
||||
<customControls:VSeparator StyleClasses="LowDivider" Margin="10 0"/>
|
||||
<Label Name="Fingerprint"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Bottom row -->
|
||||
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 5"/>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="PrintButton" TextAlign="Center" MinSize="150 25" Margin="0 0 10 5" Text="{Loc 'body-scanner-console-window-print-button'}"/>
|
||||
<Button Name="ScanButton" TextAlign="Center" MinSize="150 25" Margin="0 0 10 5" Text="{Loc 'body-scanner-console-window-scan-button'}"/>
|
||||
<Label Name="StatusText"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="NoDataContainer"
|
||||
VerticalExpand="True"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="4 0"
|
||||
Orientation="Vertical">
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TexturePath="/Textures/White/Interface/BodyScanner/body_scanner_logo.svg.160dpi.png"
|
||||
Margin="10"/>
|
||||
<ProgressBar Name="NoDataScanProgressBar"
|
||||
HorizontalAlignment="Center"
|
||||
MinValue="0" MaxValue="1"
|
||||
MinSize="400 75"
|
||||
Margin="10"/>
|
||||
<Button Name="NoDataScanButton"
|
||||
HorizontalAlignment="Center"
|
||||
TextAlign="Center"
|
||||
MinSize="250 50"
|
||||
Margin="10"
|
||||
Text="{Loc 'body-scanner-console-window-scan-button'}"/>
|
||||
<Label Name="NoDataStatusLabel"
|
||||
Text=" "
|
||||
HorizontalAlignment="Center"
|
||||
StyleClasses="LabelBig"
|
||||
Margin="10"/>
|
||||
</BoxContainer>
|
||||
<!-- Footer -->
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Bottom">
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||
<Label Text="{Loc 'body-scanner-console-window-flavor-bottom-left'}" StyleClasses="WindowFooterText" />
|
||||
<Label Text="{Loc 'body-scanner-console-window-flavor-bottom-right'}" 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>
|
||||
@@ -0,0 +1,169 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.White.Medical.BodyScanner;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Client.Graphics;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
|
||||
namespace Content.Client.White.Medical.BodyScanner
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BodyScannerConsoleWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public event Action? OnScanButtonPressed;
|
||||
public event Action? OnPrintButtonPressed;
|
||||
|
||||
public BodyScannerConsoleWindow()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
ScanButton.OnPressed += _ => OnScanButtonPressed?.Invoke();
|
||||
NoDataScanButton.OnPressed += _ => OnScanButtonPressed?.Invoke();
|
||||
PrintButton.OnPressed += _ => OnPrintButtonPressed?.Invoke();
|
||||
}
|
||||
|
||||
public void UpdateUserInterface(BodyScannerConsoleBoundUserInterfaceState state)
|
||||
{
|
||||
MainContainer.Visible = state.TargetEntityUid != null && !state.Scanning;
|
||||
NoDataContainer.Visible = !MainContainer.Visible;
|
||||
NoDataStatusLabel.Text = state.Scanning ? Loc.GetString("body-scanner-console-window-status-scanning") : " ";
|
||||
|
||||
ScanButton.Disabled = !state.CanScan;
|
||||
PrintButton.Disabled = !state.CanPrint;
|
||||
NoDataScanButton.Disabled = !state.CanScan;
|
||||
|
||||
NoDataScanProgressBar.Value = 1 - (float) (state.ScanTimeRemaining / state.ScanTotalTime);
|
||||
|
||||
if (state.Scanning)
|
||||
return;
|
||||
|
||||
EntityNameLabel.Text = state.EntityName;
|
||||
|
||||
// First column
|
||||
TemperatureLabel.Text = $"{state.CurrentTemperature - 273f:F1} \u00B0C";
|
||||
BloodLevelLabel.Text = $"{state.BloodSolution.FillFraction * 100:F1} %";
|
||||
TotalDamageLabel.Text = state.TotalDamage.ToString();
|
||||
|
||||
AliveStatusLabel.Text = state.CurrentState switch
|
||||
{
|
||||
Shared.Mobs.MobState.Alive => Loc.GetString("body-scanner-console-window-current-alive-status-alive-text"),
|
||||
Shared.Mobs.MobState.Critical => Loc.GetString("body-scanner-console-window-current-alive-status-critical-text"),
|
||||
Shared.Mobs.MobState.Dead => Loc.GetString("body-scanner-console-window-current-alive-status-dead-text"),
|
||||
_ => Loc.GetString("body-scanner-console-window-no-data")
|
||||
};
|
||||
|
||||
HashSet<string> shownTypes = new();
|
||||
|
||||
var protos = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerGroup = state.DamagePerGroup;
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerType = state.DamageDict;
|
||||
|
||||
DamageGroupsContainer.RemoveAllChildren();
|
||||
|
||||
// Show the total damage and type breakdown for each damage group.
|
||||
foreach (var (damageGroupId, damageAmount) in damagePerGroup)
|
||||
{
|
||||
var damageGroupTitle = Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId, ("amount", damageAmount));
|
||||
|
||||
DamageGroupsContainer.AddChild(new GroupDamageCardComponent(damageGroupTitle, damageGroupId, damagePerType));
|
||||
}
|
||||
|
||||
// Second column.
|
||||
CurrentTemperature.Text = Loc.GetString("body-scanner-console-window-temperature-current-temperature-text",
|
||||
("amount", $"{state.CurrentTemperature - 273:f1}"));
|
||||
HeatDamageThreshold.Text = Loc.GetString("body-scanner-console-window-temperature-heat-damage-threshold-temperature-text",
|
||||
("amount", $"{state.HeatDamageThreshold - 273:f1}"));
|
||||
ColdDamageThreshold.Text = Loc.GetString("body-scanner-console-window-temperature-cold-damage-threshold-temperature-text",
|
||||
("amount", $"{state.ColdDamageThreshold - 273:f1}"));
|
||||
|
||||
CurrentSaturation.Text = Loc.GetString("body-scanner-console-window-saturation-current-saturation-text",
|
||||
("amount", $"{state.Saturation:f1}"));
|
||||
MinimumSaturation.Text = Loc.GetString("body-scanner-console-window-saturation-maximum-saturation-text",
|
||||
("amount", $"{state.MinSaturation:f1}"));
|
||||
MaximumSaturation.Text = Loc.GetString("body-scanner-console-window-saturation-minimum-saturation-text",
|
||||
("amount", $"{state.MaxSaturation:f1}"));
|
||||
|
||||
CurrentThirst.Text = Loc.GetString("body-scanner-console-window-thirst-current-thirst-text",
|
||||
("amount", $"{state.CurrentThirst:f1}"));
|
||||
CurrentThirstStatus.Text = Loc.GetString("body-scanner-console-window-thirst-current-thirst-status-text",
|
||||
("status", Loc.GetString("body-scanner-console-window-hunger-current-hunger-status-" + state.CurrentThirstThreshold)));
|
||||
|
||||
CurrentHunger.Text = Loc.GetString("body-scanner-console-window-hunger-current-hunger-text",
|
||||
("amount", $"{state.CurrentHunger:f1}"));
|
||||
CurrentHungerStatus.Text = Loc.GetString("body-scanner-console-window-hunger-current-hunger-status-text",
|
||||
("status", Loc.GetString("body-scanner-console-window-thirst-current-thirst-status-" + state.CurrentHungerThreshold)));
|
||||
|
||||
BloodSolutionVolume.Text = Loc.GetString("body-scanner-console-window-blood-solutions-volume-group-text",
|
||||
("amount", $"{state.BloodSolution.Volume.Float():f1}"),
|
||||
("maxAmount", $"{state.BloodSolution.MaxVolume.Float():f1}"),
|
||||
("temperature", $"{state.BloodSolution.Temperature - 271:f1}"));
|
||||
|
||||
BloodSolutionElements.RemoveAllChildren();
|
||||
state.BloodSolution.Contents.ForEach(x =>
|
||||
{
|
||||
_prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto);
|
||||
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
|
||||
BloodSolutionElements.AddChild(new Label() { Text = $"{name}: {x.Quantity}", FontColorOverride = Color.LightGray });
|
||||
});
|
||||
|
||||
ChemicalSolutionVolume.Text = Loc.GetString("body-scanner-console-window-chemical-solutions-volume-group-text",
|
||||
("amount", $"{state.ChemicalSolution.Volume.Float():f1}"),
|
||||
("maxAmount", $"{state.ChemicalSolution.MaxVolume.Float():f1}"),
|
||||
("temperature", $"{state.ChemicalSolution.Temperature - 271:f1}"));
|
||||
|
||||
ChemicalSolutionElements.RemoveAllChildren();
|
||||
state.ChemicalSolution.Contents.ForEach(x =>
|
||||
{
|
||||
_prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto);
|
||||
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
|
||||
ChemicalSolutionElements.AddChild(new Label() { Text = $"{name}: {x.Quantity}", FontColorOverride = Color.LightGray });
|
||||
});
|
||||
|
||||
// Third column.
|
||||
HealthBar.Value = 1 - (state.TotalDamage / state.DeadThreshold).Float();
|
||||
|
||||
HealthBar.ForegroundStyleBoxOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = HealthBar.Value >= 0.5f ?
|
||||
GetColorLerp(Color.Yellow, Color.Green, (HealthBar.Value - 0.5f) * 2) :
|
||||
GetColorLerp(Color.Red, Color.Yellow, HealthBar.Value * 2)
|
||||
};
|
||||
|
||||
if (state.TargetEntityUid != null)
|
||||
EntityView.SetEntity(state.TargetEntityUid.Value);
|
||||
|
||||
// Bottom row.
|
||||
Mind.Text = Loc.GetString("body-scanner-console-window-mind-text",
|
||||
("value", state.HasMind ? Loc.GetString("body-scanner-console-window-mind-present-text") :
|
||||
Loc.GetString("body-scanner-console-window-mind-absent-text")));
|
||||
DNA.Text = Loc.GetString("body-scanner-console-window-dna-text",
|
||||
("value", $"{state.DNA}"));
|
||||
Fingerprint.Text = Loc.GetString("body-scanner-console-window-fingerprint-text",
|
||||
("value", $"{state.Fingerprint}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smooth transition from one color to another depending on the percentage.
|
||||
/// </summary>
|
||||
/// <param name="startColor"></param>
|
||||
/// <param name="endColor"></param>
|
||||
/// <param name="percentage"></param>
|
||||
/// <returns></returns>
|
||||
private Color GetColorLerp(Color startColor, Color endColor, float percentage)
|
||||
{
|
||||
var r = MathHelper.Lerp(startColor.R, endColor.R, percentage);
|
||||
var g = MathHelper.Lerp(startColor.G, endColor.G, percentage);
|
||||
var b = MathHelper.Lerp(startColor.B, endColor.B, percentage);
|
||||
var a = MathHelper.Lerp(startColor.A, endColor.A, percentage);
|
||||
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer MinSize="120 100" Margin="1">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BorderColor="#777777" BorderThickness="1"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="DamageGroupTitle" Align="Center" Margin="5" StyleClasses="StatusFieldTitle"/>
|
||||
<customControls:HSeparator Margin="5 0"/>
|
||||
<BoxContainer Name="DamageLabelsContainer" Orientation="Vertical" Margin="5"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,102 @@
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.White.Medical.BodyScanner
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GroupDamageCardComponent : Control
|
||||
{
|
||||
public GroupDamageCardComponent(string title, string damageGroupID, IReadOnlyDictionary<string, FixedPoint2> damagePerType)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
DamageGroupTitle.Text = title;
|
||||
|
||||
HashSet<string> shownTypes = new();
|
||||
var protos = IoCManager.Resolve<IPrototypeManager>();
|
||||
var group = protos.Index<DamageGroupPrototype>(damageGroupID);
|
||||
|
||||
// Show the damage for each type in that group.
|
||||
foreach (var type in group.DamageTypes)
|
||||
{
|
||||
if (damagePerType.TryGetValue(type, out var typeAmount))
|
||||
{
|
||||
// If damage types are allowed to belong to more than one damage group, they may appear twice here. Mark them as duplicate.
|
||||
if (!shownTypes.Contains(type))
|
||||
{
|
||||
shownTypes.Add(type);
|
||||
|
||||
Label damagePerTypeLabel = new()
|
||||
{
|
||||
Text = Loc.GetString("health-analyzer-window-damage-type-" + type, ("amount", typeAmount)),
|
||||
};
|
||||
|
||||
SetColorLabel(damagePerTypeLabel, typeAmount.Float());
|
||||
|
||||
DamageLabelsContainer.AddChild(damagePerTypeLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the color of a label depending on the damage.
|
||||
/// </summary>
|
||||
/// <param name="label"></param>
|
||||
/// <param name="damage"></param>
|
||||
private void SetColorLabel(Label label, float damage)
|
||||
{
|
||||
var startColor = Color.White;
|
||||
var critColor = Color.Yellow;
|
||||
var endColor = Color.Red;
|
||||
|
||||
var startDamage = 0f;
|
||||
var critDamage = 30f;
|
||||
var endDamage = 100f;
|
||||
|
||||
if (damage <= startDamage)
|
||||
{
|
||||
label.FontColorOverride = startColor;
|
||||
}
|
||||
else if (damage >= endDamage)
|
||||
{
|
||||
label.FontColorOverride = endColor;
|
||||
}
|
||||
else if (damage >= startDamage && damage <= critDamage)
|
||||
{
|
||||
// We need a number from 0 to 100.
|
||||
damage *= 100f / (critDamage - startDamage);
|
||||
label.FontColorOverride = GetColorLerp(startColor, critColor, damage);
|
||||
}
|
||||
else if (damage >= critDamage && damage <= endDamage)
|
||||
{
|
||||
// We need a number from 0 to 100.
|
||||
damage *= 100f / (endDamage - critDamage);
|
||||
label.FontColorOverride = GetColorLerp(critColor, endColor, damage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smooth transition from one color to another depending on the percentage.
|
||||
/// </summary>
|
||||
/// <param name="startColor"></param>
|
||||
/// <param name="endColor"></param>
|
||||
/// <param name="percentage"></param>
|
||||
/// <returns></returns>
|
||||
private Color GetColorLerp(Color startColor, Color endColor, float percentage)
|
||||
{
|
||||
var t = percentage / 100f;
|
||||
var r = MathHelper.Lerp(startColor.R, endColor.R, t);
|
||||
var g = MathHelper.Lerp(startColor.G, endColor.G, t);
|
||||
var b = MathHelper.Lerp(startColor.B, endColor.B, t);
|
||||
var a = MathHelper.Lerp(startColor.A, endColor.A, t);
|
||||
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.White.Medical.BodyScanner;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
@@ -9,7 +10,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
[RegisterComponent, Access(typeof(BloodstreamSystem), (typeof(ChemistrySystem)))]
|
||||
[RegisterComponent, Access(typeof(BloodstreamSystem), typeof(ChemistrySystem), typeof(BodyScannerConsoleSystem))] // WD EDIT
|
||||
public sealed partial class BloodstreamComponent : Component
|
||||
{
|
||||
public static string DefaultChemicalsSolutionName = "chemicals";
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
|
||||
namespace Content.Server.White.Medical.BodyScanner
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class ActiveBodyScannerConsoleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// When did the scanning start?
|
||||
/// </summary>
|
||||
[DataField("startTime", customTypeSerializer: typeof(TimespanSerializer))]
|
||||
public TimeSpan StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// What is being scanned?
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid PatientUid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Content.Server.Medical.Components;
|
||||
|
||||
namespace Content.Server.White.Medical.BodyScanner
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class BodyInScannerBodyScannerConsoleComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public MedicalScannerComponent? MedicalCannerComponent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Content.Shared.White.Medical.BodyScanner;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.White.Medical.BodyScanner
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class BodyScannerConsoleComponent : Component
|
||||
{
|
||||
public const string ScannerPort = "MedicalScannerReceiver";
|
||||
|
||||
/// <summary>
|
||||
/// Genetic scanner. Can be null if not linked.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? GeneticScanner;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance between body scanner console and one if its machines.
|
||||
/// </summary>
|
||||
[DataField("maxDistance")]
|
||||
public float MaxDistance = 4f;
|
||||
|
||||
public bool GeneticScannerInRange = true;
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes to scan.
|
||||
/// </summary>
|
||||
[ViewVariables, DataField("scanDuration", customTypeSerializer: typeof(TimespanSerializer))]
|
||||
public TimeSpan ScanDuration = TimeSpan.FromSeconds(3);
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play on scan finished.
|
||||
/// </summary>
|
||||
[DataField("scanFinishedSound")]
|
||||
public SoundSpecifier ScanFinishedSound = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play when printing.
|
||||
/// </summary>
|
||||
[DataField("printSound")]
|
||||
public SoundSpecifier PrintSound = new SoundPathSpecifier("/Audio/Machines/diagnoser_printing.ogg");
|
||||
|
||||
[ViewVariables]
|
||||
public BodyScannerConsoleBoundUserInterfaceState? LastScannedState;
|
||||
|
||||
/// <summary>
|
||||
/// The entity spawned by a report.
|
||||
/// </summary>
|
||||
[DataField("reportEntityId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ReportEntityId = "Paper";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Medical.Components;
|
||||
using Content.Shared.DeviceLinking.Events;
|
||||
using Content.Shared.White.Medical.BodyScanner;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Forensics;
|
||||
using Content.Server.Paper;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Damage.Components;
|
||||
using System.Text;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.White.Medical.BodyScanner
|
||||
{
|
||||
public sealed class BodyScannerConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly PaperSystem _paper = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActiveBodyScannerConsoleComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
|
||||
SubscribeLocalEvent<BodyScannerConsoleComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
SubscribeLocalEvent<BodyScannerConsoleComponent, NewLinkEvent>(OnNewLink);
|
||||
SubscribeLocalEvent<BodyScannerConsoleComponent, PortDisconnectedEvent>(OnPortDisconnected);
|
||||
|
||||
SubscribeLocalEvent<BodyScannerConsoleComponent, BodyScannerStartScanningMessage>(OnStartScanning);
|
||||
SubscribeLocalEvent<BodyScannerConsoleComponent, BodyScannerStartPrintingMessage>(OnStartPrinting);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var queryActiveBodyScanner = EntityQueryEnumerator<ActiveBodyScannerConsoleComponent, BodyScannerConsoleComponent>();
|
||||
while (queryActiveBodyScanner.MoveNext(out var uid, out var active, out var scan))
|
||||
{
|
||||
UpdateUserInterface(scan.Owner, scan, active);
|
||||
|
||||
if (_timing.CurTime - active.StartTime < scan.ScanDuration)
|
||||
continue;
|
||||
|
||||
FinishScan(uid, scan, active);
|
||||
}
|
||||
|
||||
var queryBodyInScanner = EntityQueryEnumerator<BodyInScannerBodyScannerConsoleComponent, BodyScannerConsoleComponent>();
|
||||
while (queryBodyInScanner.MoveNext(out var uid, out var bodyInScanner, out var scan))
|
||||
{
|
||||
if (bodyInScanner.MedicalCannerComponent?.BodyContainer.ContainedEntity == null)
|
||||
{
|
||||
RemComp<BodyInScannerBodyScannerConsoleComponent>(uid);
|
||||
UpdateUserInterface(scan.Owner, scan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUserInterface(
|
||||
EntityUid uid,
|
||||
BodyScannerConsoleComponent? scanComponent = null,
|
||||
ActiveBodyScannerConsoleComponent? activeScanComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref scanComponent))
|
||||
return;
|
||||
|
||||
if (!_uiSystem.TryGetUi(uid, BodyScannerConsoleUIKey.Key, out var bui))
|
||||
return;
|
||||
|
||||
var state = GetUserInterfaceState(scanComponent, activeScanComponent);
|
||||
scanComponent.LastScannedState = state;
|
||||
|
||||
_uiSystem.SetUiState(bui, state);
|
||||
}
|
||||
|
||||
private BodyScannerConsoleBoundUserInterfaceState GetUserInterfaceState(
|
||||
BodyScannerConsoleComponent consoleComponent,
|
||||
ActiveBodyScannerConsoleComponent? activeScanComponent = null)
|
||||
{
|
||||
BodyScannerConsoleBoundUserInterfaceState state = new();
|
||||
|
||||
if (consoleComponent?.GeneticScanner != null && TryComp<MedicalScannerComponent>(consoleComponent.GeneticScanner, out var scanner))
|
||||
{
|
||||
state.ScannerConnected = consoleComponent.GeneticScanner != null;
|
||||
state.GeneticScannerInRange = consoleComponent.GeneticScannerInRange;
|
||||
|
||||
state.CanScan = true;
|
||||
|
||||
if (state.ScannerConnected && state.GeneticScannerInRange && activeScanComponent != null)
|
||||
{
|
||||
state.CanScan = false;
|
||||
state.CanPrint = false;
|
||||
|
||||
state.Scanning = true;
|
||||
state.ScanTotalTime = consoleComponent.ScanDuration;
|
||||
state.ScanTimeRemaining = consoleComponent.ScanDuration - (_timing.CurTime - activeScanComponent.StartTime);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
var scanBody = scanner.BodyContainer.ContainedEntity;
|
||||
|
||||
if (scanBody != null)
|
||||
{
|
||||
state.CanPrint = true;
|
||||
|
||||
state.TargetEntityUid = GetNetEntity(scanBody);
|
||||
state.EntityName = MetaData(scanBody.Value).EntityName;
|
||||
|
||||
if (TryComp<BloodstreamComponent>(scanBody, out var bloodstream))
|
||||
{
|
||||
state.BleedAmount = bloodstream.BleedAmount;
|
||||
state.BloodMaxVolume = bloodstream.BloodMaxVolume;
|
||||
state.BloodReagent = bloodstream.BloodReagent;
|
||||
state.BloodSolution.MaxVolume = bloodstream.BloodMaxVolume;
|
||||
state.ChemicalMaxVolume = bloodstream.ChemicalMaxVolume;
|
||||
state.ChemicalSolution.MaxVolume = bloodstream.ChemicalMaxVolume;
|
||||
if (bloodstream.BloodSolution != null)
|
||||
state.BloodSolution = bloodstream.BloodSolution.Value.Comp.Solution.Clone();
|
||||
if (bloodstream.ChemicalSolution != null)
|
||||
state.ChemicalSolution = bloodstream.ChemicalSolution.Value.Comp.Solution.Clone();
|
||||
}
|
||||
|
||||
if (TryComp<DnaComponent>(scanBody, out var dna))
|
||||
{
|
||||
state.DNA = dna.DNA;
|
||||
}
|
||||
|
||||
if (TryComp<FingerprintComponent>(scanBody, out var fingerprint))
|
||||
{
|
||||
state.Fingerprint = fingerprint.Fingerprint ?? "";
|
||||
}
|
||||
|
||||
if (TryComp<MindContainerComponent>(scanBody, out var mind))
|
||||
{
|
||||
state.HasMind = mind.HasMind;
|
||||
}
|
||||
|
||||
if (TryComp<RespiratorComponent>(scanBody, out var respirator))
|
||||
{
|
||||
state.MaxSaturation = respirator.MaxSaturation;
|
||||
state.MinSaturation = respirator.MinSaturation;
|
||||
state.Saturation = respirator.Saturation;
|
||||
}
|
||||
|
||||
if (TryComp<TemperatureComponent>(scanBody, out var temp))
|
||||
{
|
||||
state.HeatDamageThreshold = temp.HeatDamageThreshold;
|
||||
state.ColdDamageThreshold = temp.ColdDamageThreshold;
|
||||
state.CurrentTemperature = temp.CurrentTemperature;
|
||||
}
|
||||
|
||||
if (TryComp<ThirstComponent>(scanBody, out var thirst))
|
||||
{
|
||||
state.CurrentThirst = thirst.CurrentThirst;
|
||||
state.CurrentThirstThreshold = (byte) thirst.CurrentThirstThreshold;
|
||||
}
|
||||
|
||||
if (TryComp<DamageableComponent>(scanBody, out var damageable))
|
||||
{
|
||||
state.TotalDamage = damageable.TotalDamage;
|
||||
state.DamageDict = new(damageable.Damage.DamageDict);
|
||||
state.DamagePerGroup = new(damageable.DamagePerGroup);
|
||||
}
|
||||
|
||||
if (TryComp<HungerComponent>(scanBody, out var hunger))
|
||||
{
|
||||
state.CurrentHunger = hunger.CurrentHunger;
|
||||
state.CurrentHungerThreshold = (byte) hunger.CurrentThreshold;
|
||||
}
|
||||
|
||||
if (TryComp<MobStateComponent>(scanBody, out var mobState))
|
||||
{
|
||||
state.CurrentState = mobState.CurrentState;
|
||||
}
|
||||
|
||||
if (TryComp<MobThresholdsComponent>(scanBody, out var mobThresholds))
|
||||
{
|
||||
foreach (var pair in mobThresholds.Thresholds)
|
||||
{
|
||||
if (pair.Value == Shared.Mobs.MobState.Dead)
|
||||
{
|
||||
state.DeadThreshold = pair.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<StaminaComponent>(scanBody, out var stamina))
|
||||
{
|
||||
state.StaminaCritThreshold = stamina.CritThreshold;
|
||||
state.StaminaDamage = stamina.StaminaDamage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public void FinishScan(EntityUid uid, BodyScannerConsoleComponent? component = null, ActiveBodyScannerConsoleComponent? active = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, ref active))
|
||||
return;
|
||||
|
||||
_audio.PlayPvs(component.ScanFinishedSound, uid);
|
||||
|
||||
RemComp<ActiveBodyScannerConsoleComponent>(uid);
|
||||
|
||||
if (component?.GeneticScanner != null && TryComp<MedicalScannerComponent>(component.GeneticScanner, out var scanner))
|
||||
{
|
||||
EnsureComp<BodyInScannerBodyScannerConsoleComponent>(uid).MedicalCannerComponent = scanner;
|
||||
}
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
public void OnMapInit(EntityUid uid, BodyScannerConsoleComponent component, MapInitEvent args)
|
||||
{
|
||||
if (!_uiSystem.TryGetUi(uid, BodyScannerConsoleUIKey.Key, out var bui))
|
||||
return;
|
||||
|
||||
UpdateUserInterface(uid, component);
|
||||
}
|
||||
|
||||
private void OnNewLink(EntityUid uid, BodyScannerConsoleComponent component, NewLinkEvent args)
|
||||
{
|
||||
if (TryComp<MedicalScannerComponent>(args.Sink, out var scanner) && args.SinkPort == BodyScannerConsoleComponent.ScannerPort)
|
||||
{
|
||||
component.GeneticScanner = args.Sink;
|
||||
scanner.ConnectedConsole = uid;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPortDisconnected(EntityUid uid, BodyScannerConsoleComponent component, PortDisconnectedEvent args)
|
||||
{
|
||||
if (args.Port != BodyScannerConsoleComponent.ScannerPort)
|
||||
return;
|
||||
|
||||
if (TryComp<MedicalScannerComponent>(component.GeneticScanner, out var scanner))
|
||||
scanner.ConnectedConsole = null;
|
||||
|
||||
component.GeneticScanner = null;
|
||||
|
||||
FinishScan(uid, component);
|
||||
}
|
||||
|
||||
private void OnStartScanning(EntityUid uid, BodyScannerConsoleComponent component, BodyScannerStartScanningMessage msg)
|
||||
{
|
||||
if (component.GeneticScanner == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<MedicalScannerComponent>(component.GeneticScanner, out var scanner))
|
||||
return;
|
||||
|
||||
if (scanner.BodyContainer.ContainedEntity == null)
|
||||
return;
|
||||
|
||||
var activeComp = EnsureComp<ActiveBodyScannerConsoleComponent>(uid);
|
||||
activeComp.StartTime = _timing.CurTime;
|
||||
}
|
||||
|
||||
private void OnStartPrinting(EntityUid uid, BodyScannerConsoleComponent component, BodyScannerStartPrintingMessage args)
|
||||
{
|
||||
if (!_uiSystem.TryGetUi(uid, BodyScannerConsoleUIKey.Key, out var bui))
|
||||
return;
|
||||
|
||||
var state = component.LastScannedState;
|
||||
if (state == null)
|
||||
return;
|
||||
|
||||
state.CanPrint = false;
|
||||
|
||||
_uiSystem.SetUiState(bui, state);
|
||||
|
||||
var report = Spawn(component.ReportEntityId, Transform(uid).Coordinates);
|
||||
_metaSystem.SetEntityName(report,
|
||||
Loc.GetString("body-scanner-console-report-title", ("name", state.EntityName)));
|
||||
|
||||
StringBuilder text = new();
|
||||
text.AppendLine(state.EntityName);
|
||||
text.AppendLine();
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-report-temperature",
|
||||
("amount", $"{state.CurrentTemperature - 273f:F1}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-report-blood-level",
|
||||
("amount", $"{state.BloodSolution.FillFraction * 100:F1}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-report-total-damage",
|
||||
("amount", state.TotalDamage.ToString())));
|
||||
text.AppendLine();
|
||||
|
||||
HashSet<string> shownTypes = new();
|
||||
|
||||
var protos = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerGroup = state.DamagePerGroup;
|
||||
IReadOnlyDictionary<string, FixedPoint2> damagePerType = state.DamageDict;
|
||||
|
||||
foreach (var (damageGroupId, damageAmount) in damagePerGroup)
|
||||
{
|
||||
text.AppendLine(Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId,
|
||||
("amount", damageAmount)));
|
||||
|
||||
var group = protos.Index<DamageGroupPrototype>(damageGroupId);
|
||||
|
||||
foreach (var type in group.DamageTypes)
|
||||
{
|
||||
if (damagePerType.TryGetValue(type, out var typeAmount))
|
||||
{
|
||||
// If damage types are allowed to belong to more than one damage group, they may appear twice here. Mark them as duplicate.
|
||||
if (!shownTypes.Contains(type))
|
||||
{
|
||||
shownTypes.Add(type);
|
||||
|
||||
text.Append(" - ");
|
||||
text.AppendLine(Loc.GetString("health-analyzer-window-damage-type-" + type,
|
||||
("amount", typeAmount)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text.AppendLine();
|
||||
}
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-temperature-group-text"));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-temperature-current-temperature-text",
|
||||
("amount", $"{state.CurrentTemperature - 273:f1}")));
|
||||
text.AppendLine(Loc.GetString(
|
||||
"body-scanner-console-window-temperature-heat-damage-threshold-temperature-text",
|
||||
("amount", $"{state.HeatDamageThreshold - 273:f1}")));
|
||||
text.AppendLine(Loc.GetString(
|
||||
"body-scanner-console-window-temperature-cold-damage-threshold-temperature-text",
|
||||
("amount", $"{state.ColdDamageThreshold - 273:f1}")));
|
||||
text.AppendLine();
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-saturation-group-text"));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-saturation-current-saturation-text",
|
||||
("amount", $"{state.Saturation:f1}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-saturation-maximum-saturation-text",
|
||||
("amount", $"{state.MinSaturation:f1}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-saturation-minimum-saturation-text",
|
||||
("amount", $"{state.MaxSaturation:f1}")));
|
||||
text.AppendLine();
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-thirst-group-text"));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-thirst-current-thirst-text",
|
||||
("amount", $"{state.CurrentThirst:f1}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-thirst-current-thirst-status-text",
|
||||
("status",
|
||||
Loc.GetString("body-scanner-console-window-hunger-current-hunger-status-" +
|
||||
state.CurrentThirstThreshold))));
|
||||
text.AppendLine();
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-hunger-group-text"));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-hunger-current-hunger-text",
|
||||
("amount", $"{state.CurrentHunger:f1}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-hunger-current-hunger-status-text",
|
||||
("status",
|
||||
Loc.GetString("body-scanner-console-window-thirst-current-thirst-status-" +
|
||||
state.CurrentHungerThreshold))));
|
||||
text.AppendLine();
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-blood-solutions-group-text"));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-blood-solutions-volume-group-text",
|
||||
("amount", $"{state.BloodSolution.Volume.Float():f1}"),
|
||||
("maxAmount", $"{state.BloodSolution.MaxVolume.Float():f1}"),
|
||||
("temperature", $"{state.BloodSolution.Temperature - 271:f1}")));
|
||||
state.BloodSolution.Contents.ForEach(x =>
|
||||
{
|
||||
text.Append(" - ");
|
||||
text.AppendLine($"{x.Reagent.Prototype}: {x.Quantity}");
|
||||
});
|
||||
text.AppendLine();
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-chemical-solutions-group-text"));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-chemical-solutions-volume-group-text",
|
||||
("amount", $"{state.ChemicalSolution.Volume.Float():f1}"),
|
||||
("maxAmount", $"{state.ChemicalSolution.MaxVolume.Float():f1}"),
|
||||
("temperature", $"{state.ChemicalSolution.Temperature - 271:f1}")));
|
||||
state.ChemicalSolution.Contents.ForEach(x =>
|
||||
{
|
||||
text.Append(" - ");
|
||||
text.AppendLine($"{x.Reagent.Prototype}: {x.Quantity}");
|
||||
});
|
||||
text.AppendLine();
|
||||
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-dna-text",
|
||||
("value", state.DNA)));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-fingerprint-text",
|
||||
("value", $"{state.Fingerprint}")));
|
||||
text.AppendLine(Loc.GetString("body-scanner-console-window-mind-text",
|
||||
("value", $"{state.HasMind}")));
|
||||
|
||||
_audio.PlayPvs(component.PrintSound, uid);
|
||||
_popup.PopupEntity(Loc.GetString("body-scanner-console-print-popup"), uid);
|
||||
_paper.SetContent(report, text.ToString());
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, ActiveBodyScannerConsoleComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
if (args.Powered)
|
||||
return;
|
||||
|
||||
RemComp<ActiveBodyScannerConsoleComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Tiles;
|
||||
using Content.Shared.Wieldable;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.White.Medical.BodyScanner
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum BodyScannerConsoleUIKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BodyScannerConsoleBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public bool ScannerConnected;
|
||||
public bool GeneticScannerInRange;
|
||||
public bool CanScan;
|
||||
public bool CanPrint;
|
||||
|
||||
public bool Scanning;
|
||||
public TimeSpan ScanTimeRemaining;
|
||||
public TimeSpan ScanTotalTime;
|
||||
|
||||
public NetEntity? TargetEntityUid;
|
||||
public string EntityName;
|
||||
|
||||
public float BleedAmount;
|
||||
public FixedPoint2 BloodMaxVolume;
|
||||
public string BloodReagent;
|
||||
public Solution BloodSolution;
|
||||
public FixedPoint2 ChemicalMaxVolume;
|
||||
public Solution ChemicalSolution;
|
||||
|
||||
public string DNA;
|
||||
public string Fingerprint;
|
||||
|
||||
public bool HasMind;
|
||||
|
||||
public float MaxSaturation;
|
||||
public float MinSaturation;
|
||||
public float Saturation;
|
||||
|
||||
public float HeatDamageThreshold;
|
||||
public float ColdDamageThreshold;
|
||||
public float CurrentTemperature;
|
||||
|
||||
public float CurrentThirst;
|
||||
public byte CurrentThirstThreshold;
|
||||
|
||||
public FixedPoint2 TotalDamage;
|
||||
public Dictionary<string, FixedPoint2> DamageDict;
|
||||
public Dictionary<string, FixedPoint2> DamagePerGroup;
|
||||
public FixedPoint2 DeadThreshold;
|
||||
|
||||
public float CurrentHunger;
|
||||
public byte CurrentHungerThreshold;
|
||||
|
||||
public MobState CurrentState;
|
||||
|
||||
public float StaminaCritThreshold;
|
||||
public float StaminaDamage;
|
||||
|
||||
public BodyScannerConsoleBoundUserInterfaceState()
|
||||
{
|
||||
CanScan = true;
|
||||
EntityName = "N/A";
|
||||
BloodMaxVolume = FixedPoint2.Zero;
|
||||
BloodReagent = "N/A";
|
||||
ChemicalMaxVolume = FixedPoint2.Zero;
|
||||
BloodSolution = new();
|
||||
ChemicalSolution = new();
|
||||
DNA = "";
|
||||
Fingerprint = "";
|
||||
TotalDamage = FixedPoint2.Zero;
|
||||
DamageDict = new();
|
||||
DamagePerGroup = new();
|
||||
DeadThreshold = FixedPoint2.Zero;
|
||||
CurrentState = MobState.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BodyScannerStartScanningMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class BodyScannerStartPrintingMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
health-analyzer-window-no-patient-data-text = No patient data.
|
||||
health-analyzer-window-entity-unknown-text = unknown
|
||||
health-analyzer-window-entity-health-text = {$entityName}'s health:
|
||||
health-analyzer-window-entity-temperature-text = Temperature: {$temperature}
|
||||
health-analyzer-window-entity-blood-level-text = Blood Level: {$bloodLevel}
|
||||
health-analyzer-window-entity-damage-total-text = Total Damage: {$amount}
|
||||
health-analyzer-window-no-patient-data-text = No patient
|
||||
health-analyzer-window-no-data = N/A
|
||||
health-analyzer-window-entity-current-alive-status-text = Status
|
||||
health-analyzer-window-entity-current-alive-status-alive-text = Alive
|
||||
health-analyzer-window-entity-current-alive-status-critical-text = Critical
|
||||
health-analyzer-window-entity-current-alive-status-dead-text = Dead
|
||||
health-analyzer-window-entity-temperature-text = Temperature
|
||||
health-analyzer-window-entity-blood-level-text = Blood Level
|
||||
health-analyzer-window-entity-damage-total-text = Total Damage
|
||||
health-analyzer-window-damage-group-text = {$damageGroup}: {$amount}
|
||||
health-analyzer-window-damage-type-text = {$damageType}: {$amount}
|
||||
health-analyzer-window-damage-type-duplicate-text = {$damageType}: {$amount} (duplicate)
|
||||
@@ -15,6 +18,7 @@ health-analyzer-window-damage-type-Piercing = Piercing
|
||||
|
||||
health-analyzer-window-damage-group-Burn = Burn
|
||||
health-analyzer-window-damage-type-Heat = Heat
|
||||
health-analyzer-window-damage-type-Laser = Laser
|
||||
health-analyzer-window-damage-type-Shock = Shock
|
||||
health-analyzer-window-damage-type-Cold = Cold
|
||||
health-analyzer-window-damage-type-Caustic = Caustic
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
## EnterVerb
|
||||
|
||||
body-scanner-console-window-no-data = N/A
|
||||
|
||||
body-scanner-console-window-title = Body scanner console
|
||||
body-scanner-console-window-scan-button = Scan
|
||||
body-scanner-console-window-print-button = Print
|
||||
|
||||
body-scanner-console-window-status-scanning = Scanning in progress
|
||||
|
||||
body-scanner-console-window-current-alive-status-text = Status
|
||||
body-scanner-console-window-current-alive-status-alive-text = Alive
|
||||
body-scanner-console-window-current-alive-status-critical-text = Critical
|
||||
body-scanner-console-window-current-alive-status-dead-text = Dead
|
||||
body-scanner-console-window-temperature-text = Temperature
|
||||
body-scanner-console-window-entity-blood-level-text = Blood Level
|
||||
body-scanner-console-window-entity-damage-total-text = Total Damage
|
||||
body-scanner-console-window-damage-group-text = {$damageGroup}: {$amount}
|
||||
body-scanner-console-window-damage-type-text = {$damageType}: {$amount}
|
||||
body-scanner-console-window-damage-type-duplicate-text = {$damageType}: {$amount} (duplicate)
|
||||
|
||||
body-scanner-console-window-damage-group-Brute = Brute: {$amount}
|
||||
body-scanner-console-window-damage-type-Blunt = Blunt: {$amount}
|
||||
body-scanner-console-window-damage-type-Slash = Slash: {$amount}
|
||||
body-scanner-console-window-damage-type-Piercing = Piercing: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Burn = Burn: {$amount}
|
||||
body-scanner-console-window-damage-type-Heat = Heat: {$amount}
|
||||
body-scanner-console-window-damage-type-Laser = Laser: {$amount}
|
||||
body-scanner-console-window-damage-type-Shock = Shock: {$amount}
|
||||
body-scanner-console-window-damage-type-Cold = Cold: {$amount}
|
||||
body-scanner-console-window-damage-type-Caustic = Caustic: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Airloss = Airloss: {$amount}
|
||||
body-scanner-console-window-damage-type-Asphyxiation = Asphyxiation: {$amount}
|
||||
body-scanner-console-window-damage-type-Bloodloss = Bloodloss: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Toxin = Toxin: {$amount}
|
||||
body-scanner-console-window-damage-type-Poison = Poison: {$amount}
|
||||
body-scanner-console-window-damage-type-Radiation = Radiation: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Genetic = Genetic: {$amount}
|
||||
body-scanner-console-window-damage-type-Cellular = Cellular: {$amount}
|
||||
|
||||
body-scanner-console-window-temperature-group-text = Temperature
|
||||
body-scanner-console-window-temperature-current-temperature-text = Current: {$amount} °C
|
||||
body-scanner-console-window-temperature-heat-damage-threshold-temperature-text = Maximum: {$amount} °C
|
||||
body-scanner-console-window-temperature-cold-damage-threshold-temperature-text = Minimum: {$amount} °C
|
||||
|
||||
body-scanner-console-window-saturation-group-text = Saturation
|
||||
body-scanner-console-window-saturation-current-saturation-text = Current: {$amount}
|
||||
body-scanner-console-window-saturation-maximum-saturation-text = Maximum: {$amount}
|
||||
body-scanner-console-window-saturation-minimum-saturation-text = Minimum: {$amount}
|
||||
|
||||
body-scanner-console-window-thirst-group-text = Thirst
|
||||
body-scanner-console-window-thirst-current-thirst-text = Current: {$amount}
|
||||
body-scanner-console-window-thirst-current-thirst-status-text = Status: {$status}
|
||||
body-scanner-console-window-thirst-current-thirst-status-8 = Overhydrated
|
||||
body-scanner-console-window-thirst-current-thirst-status-4 = Okey
|
||||
body-scanner-console-window-thirst-current-thirst-status-2 = Thirsty
|
||||
body-scanner-console-window-thirst-current-thirst-status-1 = Parched
|
||||
body-scanner-console-window-thirst-current-thirst-status-0 = Dead
|
||||
|
||||
body-scanner-console-window-hunger-group-text = Hunger
|
||||
body-scanner-console-window-hunger-current-hunger-text = Current: {$amount}
|
||||
body-scanner-console-window-hunger-current-hunger-status-text = Status: {$status}
|
||||
body-scanner-console-window-hunger-current-hunger-status-8 = Overfeed
|
||||
body-scanner-console-window-hunger-current-hunger-status-4 = Okey
|
||||
body-scanner-console-window-hunger-current-hunger-status-2 = Peckish
|
||||
body-scanner-console-window-hunger-current-hunger-status-1 = Starving
|
||||
body-scanner-console-window-hunger-current-hunger-status-0 = Dead
|
||||
|
||||
body-scanner-console-window-blood-solutions-group-text = Blood solutions
|
||||
body-scanner-console-window-blood-solutions-volume-group-text = Volume: {$amount}/{$maxAmount} ({$temperature} °C)
|
||||
|
||||
body-scanner-console-window-chemical-solutions-group-text = Chemical solutions
|
||||
body-scanner-console-window-chemical-solutions-volume-group-text = Volume: {$amount}/{$maxAmount} ({$temperature} °C)
|
||||
|
||||
body-scanner-console-window-dna-text = DNA: {$value}
|
||||
body-scanner-console-window-fingerprint-text = Fingerprint: {$value}
|
||||
body-scanner-console-window-mind-text = Mind: {$value}
|
||||
|
||||
body-scanner-console-window-connection-status-text-connected = Ready for work
|
||||
body-scanner-console-window-connection-status-text-no-connection = No connection
|
||||
body-scanner-console-window-connection-status-text-not-in-range = Not in range
|
||||
|
||||
body-scanner-console-window-flavor-bottom-left = Medical technologies sourced from local unscrupulous researchers.
|
||||
body-scanner-console-window-flavor-bottom-right = v1.3
|
||||
|
||||
body-scanner-console-print-popup = The console printed out a report.
|
||||
body-scanner-console-report-title = Health Report: Patient - {$name}
|
||||
body-scanner-console-report-temperature = Temperature: {$amount} °C
|
||||
body-scanner-console-report-blood-level = Blood Level: {$amount} %
|
||||
body-scanner-console-report-total-damage = Total Damage: {$amount}
|
||||
@@ -1,26 +1,35 @@
|
||||
health-analyzer-window-no-patient-data-text = Нет данных о пациенте.
|
||||
health-analyzer-window-entity-health-text = Состояние { $entityName }:
|
||||
health-analyzer-window-entity-temperature-text = Температура: { $temperature }
|
||||
health-analyzer-window-entity-blood-level-text = Уровень крови: { $bloodLevel }
|
||||
health-analyzer-window-entity-damage-total-text = Общие повреждения: { $amount }
|
||||
health-analyzer-window-damage-group-text = { $damageGroup }: { $amount }
|
||||
health-analyzer-window-damage-type-text = { $damageType }: { $amount }
|
||||
health-analyzer-window-damage-type-duplicate-text = { $damageType }: { $amount } (повтор)
|
||||
health-analyzer-window-damage-group-Brute = Механические
|
||||
health-analyzer-window-damage-type-Blunt = Удары
|
||||
health-analyzer-window-damage-type-Slash = Разрезы
|
||||
health-analyzer-window-damage-type-Piercing = Уколы
|
||||
health-analyzer-window-damage-group-Burn = Ожоги
|
||||
health-analyzer-window-damage-type-Heat = Термические
|
||||
health-analyzer-window-damage-type-Shock = Электрические
|
||||
health-analyzer-window-damage-type-Cold = Обморожение
|
||||
health-analyzer-window-damage-group-Airloss = Нехватка воздуха
|
||||
health-analyzer-window-damage-type-Asphyxiation = Удушение
|
||||
health-analyzer-window-damage-type-Bloodloss = Кровопотеря
|
||||
health-analyzer-window-damage-group-Toxin = Токсины
|
||||
health-analyzer-window-damage-type-Poison = Яды
|
||||
health-analyzer-window-damage-type-Radiation = Радиация
|
||||
health-analyzer-window-damage-group-Genetic = Генетические
|
||||
health-analyzer-window-damage-type-Cellular = Клеточные
|
||||
health-analyzer-window-damage-group-Caustic = Кислотные
|
||||
health-analyzer-window-damage-type-Caustic = Кислотные
|
||||
health-analyzer-window-no-patient-data-text = Пациент отсутствует
|
||||
health-analyzer-window-no-data = Н/Д
|
||||
health-analyzer-window-entity-current-alive-status-text = Статус
|
||||
health-analyzer-window-entity-current-alive-status-alive-text = Жив
|
||||
health-analyzer-window-entity-current-alive-status-critical-text = Критическое
|
||||
health-analyzer-window-entity-current-alive-status-dead-text = Мёртв
|
||||
health-analyzer-window-entity-temperature-text = Температура
|
||||
health-analyzer-window-entity-blood-level-text = Уровень крови
|
||||
health-analyzer-window-entity-damage-total-text = Общие повреждения
|
||||
health-analyzer-window-damage-group-text = {$damageGroup}: {$amount}
|
||||
health-analyzer-window-damage-type-text = {$damageType}: {$amount}
|
||||
health-analyzer-window-damage-type-duplicate-text = {$damageType}: {$amount} (дубликат)
|
||||
|
||||
health-analyzer-window-damage-group-Brute = Механические: {$amount}
|
||||
health-analyzer-window-damage-type-Blunt = Удары: {$amount}
|
||||
health-analyzer-window-damage-type-Slash = Разрезы: {$amount}
|
||||
health-analyzer-window-damage-type-Piercing = Уколы: {$amount}
|
||||
|
||||
health-analyzer-window-damage-group-Burn = Ожоги: {$amount}
|
||||
health-analyzer-window-damage-type-Heat = Термические: {$amount}
|
||||
health-analyzer-window-damage-type-Shock = Электрические: {$amount}
|
||||
health-analyzer-window-damage-type-Laser = Лазерный: {$amount}
|
||||
health-analyzer-window-damage-type-Cold = Обморожение: {$amount}
|
||||
health-analyzer-window-damage-type-Caustic = Кислотные: {$amount}
|
||||
|
||||
health-analyzer-window-damage-group-Airloss = Нехватка воздуха: {$amount}
|
||||
health-analyzer-window-damage-type-Asphyxiation = Удушение: {$amount}
|
||||
health-analyzer-window-damage-type-Bloodloss = Кровопотеря: {$amount}
|
||||
|
||||
health-analyzer-window-damage-group-Toxin = Токсины: {$amount}
|
||||
health-analyzer-window-damage-type-Poison = Яды: {$amount}
|
||||
health-analyzer-window-damage-type-Radiation = Радиация: {$amount}
|
||||
|
||||
health-analyzer-window-damage-group-Genetic = Генетические: {$amount}
|
||||
health-analyzer-window-damage-type-Cellular = Клеточные: {$amount}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
## EnterVerb
|
||||
|
||||
body-scanner-console-window-no-data = Н/Д
|
||||
|
||||
body-scanner-console-window-title = Консоль сканера тела
|
||||
body-scanner-console-window-scan-button = Сканировать
|
||||
body-scanner-console-window-print-button = Печать
|
||||
|
||||
body-scanner-console-window-status-scanning = Идет сканирование
|
||||
|
||||
body-scanner-console-window-current-alive-status-text = Статус
|
||||
body-scanner-console-window-current-alive-status-alive-text = Жив
|
||||
body-scanner-console-window-current-alive-status-critical-text = Критический
|
||||
body-scanner-console-window-current-alive-status-dead-text = Мёртв
|
||||
body-scanner-console-window-temperature-text = Температура
|
||||
body-scanner-console-window-entity-blood-level-text = Уровень крови
|
||||
body-scanner-console-window-entity-damage-total-text = Общие повреждения
|
||||
body-scanner-console-window-damage-group-text = {$damageGroup}: {$amount}
|
||||
body-scanner-console-window-damage-type-text = {$damageType}: {$amount}
|
||||
body-scanner-console-window-damage-type-duplicate-text = {$damageType}: {$amount} (дубликат)
|
||||
|
||||
body-scanner-console-window-damage-group-Brute = Механические: {$amount}
|
||||
body-scanner-console-window-damage-type-Blunt = Удары: {$amount}
|
||||
body-scanner-console-window-damage-type-Slash = Разрезы: {$amount}
|
||||
body-scanner-console-window-damage-type-Piercing = Уколы: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Burn = Ожоги: {$amount}
|
||||
body-scanner-console-window-damage-type-Heat = Термические: {$amount}
|
||||
body-scanner-console-window-damage-type-Laser = Лазерный: {$amount}
|
||||
body-scanner-console-window-damage-type-Shock = Электрические: {$amount}
|
||||
body-scanner-console-window-damage-type-Cold = Обморожение: {$amount}
|
||||
body-scanner-console-window-damage-type-Caustic = Кислотные: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Airloss = Нехватка воздуха: {$amount}
|
||||
body-scanner-console-window-damage-type-Asphyxiation = Удушение: {$amount}
|
||||
body-scanner-console-window-damage-type-Bloodloss = Кровопотеря: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Toxin = Токсины: {$amount}
|
||||
body-scanner-console-window-damage-type-Poison = Яды: {$amount}
|
||||
body-scanner-console-window-damage-type-Radiation = Радиация: {$amount}
|
||||
|
||||
body-scanner-console-window-damage-group-Genetic = Генетические: {$amount}
|
||||
body-scanner-console-window-damage-type-Cellular = Клеточные: {$amount}
|
||||
|
||||
body-scanner-console-window-temperature-group-text = Температура
|
||||
body-scanner-console-window-temperature-current-temperature-text = Текущая: {$amount} °C
|
||||
body-scanner-console-window-temperature-heat-damage-threshold-temperature-text = Максимум: {$amount} °C
|
||||
body-scanner-console-window-temperature-cold-damage-threshold-temperature-text = Минимум: {$amount} °C
|
||||
|
||||
body-scanner-console-window-saturation-group-text = Насыщенность
|
||||
body-scanner-console-window-saturation-current-saturation-text = Текущая: {$amount}
|
||||
body-scanner-console-window-saturation-maximum-saturation-text = Максимум: {$amount}
|
||||
body-scanner-console-window-saturation-minimum-saturation-text = Минимум: {$amount}
|
||||
|
||||
body-scanner-console-window-thirst-group-text = Жажда
|
||||
body-scanner-console-window-thirst-current-thirst-text = Текущая: {$amount}
|
||||
body-scanner-console-window-thirst-current-thirst-status-text = Статус: {$status}
|
||||
body-scanner-console-window-thirst-current-thirst-status-8 = Переувлажнение
|
||||
body-scanner-console-window-thirst-current-thirst-status-4 = Хорошо
|
||||
body-scanner-console-window-thirst-current-thirst-status-2 = Жажда
|
||||
body-scanner-console-window-thirst-current-thirst-status-1 = Пересохший
|
||||
body-scanner-console-window-thirst-current-thirst-status-0 = Мёртв
|
||||
|
||||
body-scanner-console-window-hunger-group-text = Голод
|
||||
body-scanner-console-window-hunger-current-hunger-text = Текущий: {$amount}
|
||||
body-scanner-console-window-hunger-current-hunger-status-text = Статус: {$status}
|
||||
body-scanner-console-window-hunger-current-hunger-status-8 = Перекармливание
|
||||
body-scanner-console-window-hunger-current-hunger-status-4 = Хорошо
|
||||
body-scanner-console-window-hunger-current-hunger-status-2 = Проголодавшийся
|
||||
body-scanner-console-window-hunger-current-hunger-status-1 = Голодающий
|
||||
body-scanner-console-window-hunger-current-hunger-status-0 = Мёртв
|
||||
|
||||
body-scanner-console-window-blood-solutions-group-text = Состав крови
|
||||
body-scanner-console-window-blood-solutions-volume-group-text = Объём: {$amount}/{$maxAmount} ({$temperature} °C)
|
||||
|
||||
body-scanner-console-window-chemical-solutions-group-text = Состав элементов в крови
|
||||
body-scanner-console-window-chemical-solutions-volume-group-text = Объём: {$amount}/{$maxAmount} ({$temperature} °C)
|
||||
|
||||
body-scanner-console-window-dna-text = ДНК: {$value}
|
||||
body-scanner-console-window-fingerprint-text = Отпечатки: {$value}
|
||||
body-scanner-console-window-mind-text = Душа: {$value}
|
||||
body-scanner-console-window-mind-present-text = Присутствует
|
||||
body-scanner-console-window-mind-absent-text = Отсутствует
|
||||
|
||||
body-scanner-console-window-connection-status-text-connected = Готова к работе
|
||||
body-scanner-console-window-connection-status-text-no-connection = Нет соединения
|
||||
body-scanner-console-window-connection-status-text-not-in-range = Не в радиусе
|
||||
|
||||
body-scanner-console-window-flavor-bottom-left = Медицинские технологии, полученные от местных недобросовестных исследователей.
|
||||
body-scanner-console-window-flavor-bottom-right = v1.3
|
||||
|
||||
body-scanner-console-print-popup = Консоль распечатала отчет.
|
||||
body-scanner-console-report-title = Отчет о состоянии здоровья: Пациент - {$name}
|
||||
body-scanner-console-report-temperature = Температура: {$amount} °C
|
||||
body-scanner-console-report-blood-level = Уровень крови: {$amount} %
|
||||
body-scanner-console-report-total-damage = Общие повреждения: {$amount}
|
||||
@@ -155,8 +155,14 @@
|
||||
name: Раздатчик
|
||||
description: Передатчик сигнала ХимМастера
|
||||
|
||||
# WD
|
||||
- type: sourcePort
|
||||
id: LightStatus
|
||||
name: Статус светильника
|
||||
description: Этот порт вызывается всякий раз, когда меняется статус светильника
|
||||
defaultLinks: [ Toggle ]
|
||||
|
||||
- type: sourcePort
|
||||
id: BodyScannerSender
|
||||
name: Сканер тела
|
||||
description: Передатчик сканера тела
|
||||
|
||||
@@ -539,12 +539,18 @@
|
||||
IdCardConsole-privilegedId: !type:ContainerSlot
|
||||
IdCardConsole-targetId: !type:ContainerSlot
|
||||
|
||||
# WD start
|
||||
- type: entity
|
||||
parent: BaseComputer
|
||||
id: computerBodyScanner
|
||||
name: body scanner computer
|
||||
description: A body scanner.
|
||||
description: Used to scan patients in body scanner.
|
||||
components:
|
||||
- type: BodyScannerConsole
|
||||
reportEntityId: PaperBodyScannerReport
|
||||
- type: DeviceList
|
||||
- type: DeviceNetwork
|
||||
deviceNetId: Wired
|
||||
- type: ApcPowerReceiver
|
||||
powerLoad: 500
|
||||
- type: Computer
|
||||
@@ -553,6 +559,20 @@
|
||||
radius: 1.5
|
||||
energy: 1.6
|
||||
color: "#1f8c28"
|
||||
- type: DeviceLinkSource
|
||||
range: 4
|
||||
ports:
|
||||
- BodyScannerSender
|
||||
- type: ActivatableUI
|
||||
key: enum.BodyScannerConsoleUIKey.Key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.BodyScannerConsoleUIKey.Key
|
||||
type: BodyScannerConsoleBoundUserInterface
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: StrongMetallic
|
||||
# WD end
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputer
|
||||
|
||||
26
Resources/Prototypes/White/Entities/Objects/Misc/paper.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
- type: entity
|
||||
name: body scanner printout
|
||||
parent: Paper
|
||||
id: PaperBodyScannerReport
|
||||
description: 'The readout of a body scanner'
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
layers:
|
||||
- state: paper_dotmatrix
|
||||
- state: paper_dotmatrix_words
|
||||
map: ["enum.PaperVisualLayers.Writing"]
|
||||
visible: false
|
||||
- state: paper_stamp-generic
|
||||
map: ["enum.PaperVisualLayers.Stamp"]
|
||||
visible: false
|
||||
- type: PaperVisuals
|
||||
headerImagePath: "/Textures/White/Interface/BodyScanner/paper_heading_body_scanner.svg.96dpi.png"
|
||||
headerMargin: 0.0, 0.0, 0.0, 16.0
|
||||
backgroundImagePath: "/Textures/White/Interface/BodyScanner/paper_background_dotmatrix.svg.96dpi.png"
|
||||
backgroundImageTile: true
|
||||
backgroundPatchMargin: 37.0, 0.0, 37.0, 0.0
|
||||
contentImagePath: "/Textures/White/Interface/BodyScanner/paper_content_dotmatrix_blue.svg.96dpi.png"
|
||||
contentImageNumLines: 2
|
||||
contentMargin: 16.0, 16.0, 16.0, 0.0
|
||||
maxWritableArea: 400.0, 0.0
|
||||
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="211"
|
||||
height="60"
|
||||
viewBox="0 0 211 60"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="body_scanner_logo.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
inkscape:export-filename="body_scanner_logo.svg.160dpi.png"
|
||||
inkscape:export-xdpi="160"
|
||||
inkscape:export-ydpi="160"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath1608">
|
||||
<g
|
||||
id="g1630"
|
||||
transform="scale(0.96824607,1.0327953)">
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1610"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="1.1368795e-05" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1612"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="6.207139" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1614"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="12.414267" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1616"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="18.621393" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1618"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="24.828522" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1620"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="31.035648" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1622"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="37.242775" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1624"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="43.449905" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1626"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="55.864159" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1628"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="49.657032" />
|
||||
</g>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-object-midpoints="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-page="true"
|
||||
inkscape:zoom="4.2223172"
|
||||
inkscape:cx="137.48375"
|
||||
inkscape:cy="0.11841839"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showguides="true" />
|
||||
<g
|
||||
aria-label="NTX"
|
||||
transform="scale(1.0327953,0.96824607)"
|
||||
id="text1677"
|
||||
clip-path="url(#clipPath1608)"
|
||||
style="font-size:87.0333px;line-height:1.25;display:inline;fill:#2c3b8c;stroke-width:2.17583">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:74.7181px;line-height:1.25;font-family:'Californian FB';-inkscape-font-specification:'Californian FB, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.06434"
|
||||
x="-7.0813799"
|
||||
y="54.390907"
|
||||
id="text2"
|
||||
transform="scale(0.95869604,1.0430835)"><tspan
|
||||
id="tspan2"
|
||||
x="-7.0813799"
|
||||
y="54.390907"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:74.7181px;font-family:Algerian;-inkscape-font-specification:'Algerian, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;opacity:1;fill:#008000;stroke-width:4.06434"
|
||||
sodipodi:role="line">ВД</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
aria-label="Labs"
|
||||
id="text2013"
|
||||
style="font-size:41.0143px;line-height:1.25;fill:#6969e1;fill-opacity:1;stroke-width:1.02536"
|
||||
transform="translate(0,-3.3157149)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30.2197px;line-height:1.25;font-family:Candara;-inkscape-font-specification:'Candara, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;display:inline;fill:#6969e1;fill-opacity:1;stroke:none;stroke-width:0.755493"
|
||||
x="72.789795"
|
||||
y="50.594276"
|
||||
id="text3"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3"
|
||||
x="72.789795"
|
||||
y="50.594276"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30.2197px;font-family:Candara;-inkscape-font-specification:'Candara, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#6969e1;fill-opacity:1;stroke-width:0.755493">Медицина </tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
@@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="76"
|
||||
height="32"
|
||||
viewBox="0 0 76 32"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="paper_background_dotmatrix.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
inkscape:export-filename="paper_background_dotmatrix.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8">
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect2519"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect2519"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<filter
|
||||
id="mask-powermask-path-effect1163_inverse"
|
||||
inkscape:label="filtermask-powermask-path-effect1163"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
height="100"
|
||||
width="100"
|
||||
x="-50"
|
||||
y="-50">
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect1163_primitive1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect1163_primitive2"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||
in="fbSourceGraphic" />
|
||||
</filter>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask2602">
|
||||
<path
|
||||
id="mask-powermask-path-effect2634_box"
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
d="M -1,-0.96563349 H 77 V 33 H -1 Z" />
|
||||
<g
|
||||
id="g2632"
|
||||
style="">
|
||||
<g
|
||||
id="g2612">
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.0923076;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2604"
|
||||
cx="16"
|
||||
cy="0"
|
||||
r="12" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.0923076;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2606"
|
||||
cx="60"
|
||||
cy="0"
|
||||
r="12" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.0923076;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2608"
|
||||
cx="16"
|
||||
cy="32"
|
||||
r="12" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.0923076;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2610"
|
||||
cx="60"
|
||||
cy="32"
|
||||
r="12" />
|
||||
</g>
|
||||
<g
|
||||
id="g2630"
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-opacity:1">
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2614"
|
||||
cx="34"
|
||||
cy="4"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2616"
|
||||
cx="42"
|
||||
cy="4"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2618"
|
||||
cx="34"
|
||||
cy="28"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2620"
|
||||
cx="42"
|
||||
cy="28"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2622"
|
||||
cx="34"
|
||||
cy="20"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2624"
|
||||
cx="42"
|
||||
cy="20"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2626"
|
||||
cx="34"
|
||||
cy="12"
|
||||
r="2" />
|
||||
<circle
|
||||
style="fill:#000000;fill-opacity:0.797456;stroke:none;stroke-width:0.133333;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="circle2628"
|
||||
cx="42"
|
||||
cy="12"
|
||||
r="2" />
|
||||
</g>
|
||||
</g>
|
||||
</mask>
|
||||
<filter
|
||||
id="mask-powermask-path-effect2634_inverse"
|
||||
inkscape:label="filtermask-powermask-path-effect2634"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
height="100"
|
||||
width="100"
|
||||
x="-50"
|
||||
y="-50">
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect2634_primitive1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect2634_primitive2"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||
in="fbSourceGraphic" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1034"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="8.4422326"
|
||||
inkscape:cx="37.667761"
|
||||
inkscape:cy="19.130011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="44"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid233"
|
||||
originx="0"
|
||||
originy="0" />
|
||||
</sodipodi:namedview>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="rect1200"
|
||||
width="76"
|
||||
height="31.965633"
|
||||
x="0"
|
||||
y="0.034366511"
|
||||
mask="url(#mask2602)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 702 B |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="12"
|
||||
height="32"
|
||||
viewBox="0 0 12 32"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="paper_content_dotmatrix_blue.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
inkscape:export-filename="paper_content_dotmatrix_blue.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-object-midpoints="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-page="true"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="0.28125"
|
||||
inkscape:cy="6.265625"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<rect
|
||||
style="mix-blend-mode:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.49485;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="rect983"
|
||||
width="12"
|
||||
height="16"
|
||||
x="0"
|
||||
y="0" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:3.47338;stroke-linejoin:round"
|
||||
id="rect231"
|
||||
width="12"
|
||||
height="16"
|
||||
x="0"
|
||||
y="0.21811378" />
|
||||
<rect
|
||||
style="fill:#b6b6fb;fill-opacity:0.30067104;stroke-width:3.13086;stroke-linejoin:round"
|
||||
id="rect339"
|
||||
width="12"
|
||||
height="12.999999"
|
||||
x="0"
|
||||
y="19" />
|
||||
<rect
|
||||
style="fill:#d0d0fb;fill-opacity:0.30000001;stroke-width:1.50402;stroke-linejoin:round"
|
||||
id="rect1539"
|
||||
width="12"
|
||||
height="3"
|
||||
x="0"
|
||||
y="0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 176 B |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="211"
|
||||
height="60"
|
||||
viewBox="0 0 211 60"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="paper_heading_body_scanner.svg"
|
||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||
inkscape:export-filename="paper_heading_body_scanner.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath1608">
|
||||
<g
|
||||
id="g1630"
|
||||
transform="scale(0.96824607,1.0327953)">
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1610"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="1.1368795e-05" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1612"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="6.207139" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1614"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="12.414267" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1616"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="18.621393" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1618"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="24.828522" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1620"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="31.035648" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1622"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="37.242775" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1624"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="43.449905" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1626"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="55.864159" />
|
||||
<rect
|
||||
style="fill:#00a700;fill-opacity:1;stroke-width:0.0886665;stroke-linejoin:round"
|
||||
id="rect1628"
|
||||
width="145.43811"
|
||||
height="3.8830388"
|
||||
x="0"
|
||||
y="49.657032" />
|
||||
</g>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-object-midpoints="false"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-page="true"
|
||||
inkscape:zoom="4.2223172"
|
||||
inkscape:cx="137.48375"
|
||||
inkscape:cy="0.11841839"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showguides="true" />
|
||||
<g
|
||||
aria-label="NTX"
|
||||
transform="scale(1.0327953,0.96824607)"
|
||||
id="text1677"
|
||||
clip-path="url(#clipPath1608)"
|
||||
style="font-size:87.0333px;line-height:1.25;display:inline;fill:#2c3b8c;stroke-width:2.17583">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:74.7181px;line-height:1.25;font-family:'Californian FB';-inkscape-font-specification:'Californian FB, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.06434"
|
||||
x="-7.0813799"
|
||||
y="54.390907"
|
||||
id="text2"
|
||||
transform="scale(0.95869604,1.0430835)"><tspan
|
||||
id="tspan2"
|
||||
x="-7.0813799"
|
||||
y="54.390907"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:74.7181px;font-family:Algerian;-inkscape-font-specification:'Algerian, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;opacity:1;fill:#008000;stroke-width:4.06434"
|
||||
sodipodi:role="line">ВД</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
aria-label="Labs"
|
||||
id="text2013"
|
||||
style="font-size:41.0143px;line-height:1.25;fill:#6969e1;fill-opacity:1;stroke-width:1.02536"
|
||||
transform="translate(0,-3.3157149)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30.2197px;line-height:1.25;font-family:Candara;-inkscape-font-specification:'Candara, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;display:inline;fill:#6969e1;fill-opacity:1;stroke:none;stroke-width:0.755493"
|
||||
x="72.789795"
|
||||
y="50.594276"
|
||||
id="text3"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3"
|
||||
x="72.789795"
|
||||
y="50.594276"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30.2197px;font-family:Candara;-inkscape-font-specification:'Candara, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#6969e1;fill-opacity:1;stroke-width:0.755493">Медицина </tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||