mirror of
https://github.com/wega-team/ss14-wega.git
synced 2026-02-14 19:30:01 +01:00
@@ -4,6 +4,9 @@ using Content.Client.Stylesheets;
|
||||
using Content.Shared.Charges.Components;
|
||||
using Content.Shared.Charges.Systems;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Hands;
|
||||
using Robust.Client.GameObjects; // Corvax-Wega-Add
|
||||
using Robust.Client.Graphics; // Corvax-Wega-Add
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -14,12 +17,33 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
{
|
||||
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!; // Corvax-Wega-Add
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!; // Corvax-Wega-Add
|
||||
|
||||
private CrayonPreviewOverlay? _previewOverlay; // Corvax-Wega-Add
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Subs.ItemStatus<CrayonComponent>(ent => new StatusControl(ent, _charges, _entityManager));
|
||||
SubscribeLocalEvent<CrayonComponent, HandSelectedEvent>(OnCrayonSelected);
|
||||
SubscribeLocalEvent<CrayonComponent, HandDeselectedEvent>(OnCrayonDeselected);
|
||||
}
|
||||
|
||||
private void OnCrayonSelected(EntityUid uid, CrayonComponent component, HandSelectedEvent args)
|
||||
{
|
||||
_previewOverlay ??= new CrayonPreviewOverlay(_sprite);
|
||||
_overlay.AddOverlay(_previewOverlay);
|
||||
}
|
||||
|
||||
private void OnCrayonDeselected(EntityUid uid, CrayonComponent component, HandDeselectedEvent args)
|
||||
{
|
||||
if (_previewOverlay != null)
|
||||
{
|
||||
_overlay.RemoveOverlay(_previewOverlay);
|
||||
_previewOverlay = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class StatusControl : Control
|
||||
@@ -29,11 +53,11 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
private readonly RichTextLabel _label;
|
||||
private readonly int _capacity;
|
||||
|
||||
public StatusControl(Entity<CrayonComponent> crayon, SharedChargesSystem charges, EntityManager entityManage)
|
||||
public StatusControl(Entity<CrayonComponent> crayon, SharedChargesSystem charges, EntityManager entityManager)
|
||||
{
|
||||
_crayon = crayon;
|
||||
_charges = charges;
|
||||
_capacity = entityManage.GetComponent<LimitedChargesComponent>(_crayon.Owner).MaxCharges;
|
||||
_capacity = entityManager.GetComponent<LimitedChargesComponent>(_crayon.Owner).MaxCharges;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
|
||||
AddChild(_label);
|
||||
}
|
||||
@@ -43,8 +67,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
base.FrameUpdate(args);
|
||||
|
||||
_label.SetMarkup(Robust.Shared.Localization.Loc.GetString("crayon-drawing-label",
|
||||
("color",_crayon.Comp.Color),
|
||||
("state",_crayon.Comp.SelectedState),
|
||||
("color", _crayon.Comp.Color),
|
||||
("state", _crayon.Comp.SelectedState),
|
||||
("charges", _charges.GetCurrentCharges(_crayon.Owner)),
|
||||
("capacity", _capacity)));
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.OpenBackpack);
|
||||
human.AddFunction(ContentKeyFunctions.OpenBelt);
|
||||
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
||||
human.AddFunction(ContentKeyFunctions.MouseWheelUp); // Corvax-Wega-Add
|
||||
human.AddFunction(ContentKeyFunctions.MouseWheelDown); // Corvax-Wega-Add
|
||||
human.AddFunction(ContentKeyFunctions.RotateObjectClockwise);
|
||||
human.AddFunction(ContentKeyFunctions.RotateObjectCounterclockwise);
|
||||
human.AddFunction(ContentKeyFunctions.FlipObject);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Input; // Corvax-Wega-Add
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Input; // Corvax-Wega-Add
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -124,6 +126,33 @@ namespace Content.Client.Viewport
|
||||
RectClipContent = true;
|
||||
}
|
||||
|
||||
// Corvax-Wega-Add-start
|
||||
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||
{
|
||||
base.MouseWheel(args);
|
||||
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var key = args.Delta.Y > 0 ? ContentKeyFunctions.MouseWheelUp : ContentKeyFunctions.MouseWheelDown;
|
||||
|
||||
SendKeyEvent(key, BoundKeyState.Down, args);
|
||||
SendKeyEvent(key, BoundKeyState.Up, args);
|
||||
}
|
||||
|
||||
private void SendKeyEvent(BoundKeyFunction key, BoundKeyState state, GUIMouseWheelEventArgs args)
|
||||
{
|
||||
var keyEvent = new BoundKeyEventArgs(
|
||||
key,
|
||||
state,
|
||||
args.GlobalPixelPosition,
|
||||
false
|
||||
);
|
||||
|
||||
_inputManager.ViewportKeyEvent(this, keyEvent);
|
||||
}
|
||||
// Corvax-Wega-Add-end
|
||||
|
||||
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindDown(args);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.Botany.PlantAnalyzer;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._Wega.Botany;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class PlantAnalyzerBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private PlantAnalyzerWindow? _window;
|
||||
|
||||
public PlantAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = this.CreateWindow<PlantAnalyzerWindow>();
|
||||
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
||||
_window.Print.OnPressed += _ => Print();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
if (message is not PlantAnalyzerScannedUserMessage cast)
|
||||
return;
|
||||
|
||||
_window.Populate(cast);
|
||||
}
|
||||
|
||||
private void Print()
|
||||
{
|
||||
SendMessage(new PlantAnalyzerPrintMessage());
|
||||
if (_window != null)
|
||||
_window.Print.Disabled = true;
|
||||
}
|
||||
}
|
||||
7
Content.Client/_Wega/Botany/PlantAnalyzerSystem.cs
Normal file
7
Content.Client/_Wega/Botany/PlantAnalyzerSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Botany.Systems;
|
||||
|
||||
namespace Content.Client.Botany.Systems;
|
||||
|
||||
public sealed class PlantAnalyzerSystem : SharedPlantAnalyzerSystem
|
||||
{
|
||||
}
|
||||
164
Content.Client/_Wega/Botany/PlantAnalyzerWindow.xaml
Normal file
164
Content.Client/_Wega/Botany/PlantAnalyzerWindow.xaml
Normal file
@@ -0,0 +1,164 @@
|
||||
<controls:FancyWindow
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
MinWidth="340"
|
||||
MaxWidth="400"
|
||||
SetSize="340 600">
|
||||
<ScrollContainer
|
||||
Margin="5"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="False">
|
||||
<BoxContainer
|
||||
Orientation="Vertical"
|
||||
VerticalExpand="True">
|
||||
|
||||
<!-- Header Section -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 0 0 8">
|
||||
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" SetSize="64 64" />
|
||||
<TextureRect Name="NoDataIcon" SetSize="64 64"
|
||||
TexturePath="/Textures/Interface/VerbIcons/information.svg.192dpi.png" Stretch="KeepAspectCentered"/>
|
||||
<BoxContainer Margin="8 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
|
||||
<Label Name="ScanModeLabel" StyleClasses="LabelSubText" />
|
||||
<RichTextLabel Name="SeedLabel" SetWidth="200" Margin="0 2 0 0" />
|
||||
<Label Name="ContainerLabel" StyleClasses="LabelSubText" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Plant Status Tags -->
|
||||
<BoxContainer Name="PlantDataTags" Orientation="Horizontal" HorizontalExpand="True"
|
||||
HorizontalAlignment="Center" Margin="0 4 0 8" Visible="False">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Alive" Visible="False" Text="{Loc 'plant-analyzer-component-alive'}"
|
||||
StyleClasses="LabelSuccess" Margin="0 0 4 0" />
|
||||
<Label Name="Dead" Visible="False" Text="{Loc 'plant-analyzer-component-dead'}"
|
||||
StyleClasses="LabelDanger" Margin="0 0 4 0" />
|
||||
<Label Name="Unviable" Visible="False" Text="{Loc 'plant-analyzer-component-unviable'}"
|
||||
StyleClasses="LabelWarning" Margin="0 0 4 0" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Kudzu" Visible="False" Text="{Loc 'plant-analyzer-component-kudzu'}"
|
||||
StyleClasses="LabelCaution" Margin="0 0 4 0" />
|
||||
<Label Name="Mutating" Visible="False" Text="{Loc 'plant-analyzer-component-mutating'}"
|
||||
StyleClasses="LabelAccent" Margin="0 0 4 0" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Plant Health Stats -->
|
||||
<GridContainer Name="PlantDataGrid" Columns="4" HSeparationOverride="8" Visible="False">
|
||||
<BoxContainer Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<Label Text="{Loc 'plant-analyzer-component-health'}" StyleClasses="LabelSubText" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Health" MinWidth="40" />
|
||||
<Label Text="/" StyleClasses="LabelSubText" />
|
||||
<Label Name="Endurance" MinWidth="40" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<Label Text="{Loc 'plant-analyzer-component-age'}" StyleClasses="LabelSubText" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="Age" MinWidth="40" />
|
||||
<Label Text="/" StyleClasses="LabelSubText" />
|
||||
<Label Name="Lifespan" MinWidth="40" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<Label Text="{Loc 'plant-analyzer-component-maturation'}" StyleClasses="LabelSubText" />
|
||||
<Label Name="Maturation" />
|
||||
</BoxContainer>
|
||||
</GridContainer>
|
||||
|
||||
<PanelContainer Name="PlantDataDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Tray Conditions -->
|
||||
<BoxContainer Name="ContainerGrid" Orientation="Vertical" Visible="False" Margin="0 4">
|
||||
<Label Text="{Loc 'plant-analyzer-tray-conditions'}" StyleClasses="LabelHeading" />
|
||||
|
||||
<!-- Water -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 2">
|
||||
<Label MinWidth="20" />
|
||||
<Label Text="{Loc 'plant-analyzer-component-water'}" StyleClasses="LabelSubText" HorizontalExpand="True" />
|
||||
<Label Name="WaterLevelLabel" FontColorOverride="DeepSkyBlue" MinWidth="40" HorizontalAlignment="Right" />
|
||||
<Label Text="/ 100" StyleClasses="LabelSubText" MinWidth="35" />
|
||||
<Label Name="WaterConsumptionLabel" Text="→" StyleClasses="LabelSubText" MinWidth="15" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Nutrition -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 2">
|
||||
<Label MinWidth="20" />
|
||||
<Label Text="{Loc 'plant-analyzer-component-nutrition'}" StyleClasses="LabelSubText" HorizontalExpand="True" />
|
||||
<Label Name="NutritionLevelLabel" FontColorOverride="Orange" MinWidth="40" HorizontalAlignment="Right" />
|
||||
<Label Text="/ 100" StyleClasses="LabelSubText" MinWidth="35" />
|
||||
<Label Name="NutritionConsumptionLabel" Text="→" StyleClasses="LabelSubText" MinWidth="15" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Toxins -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 2">
|
||||
<Label MinWidth="20" />
|
||||
<Label Text="{Loc 'plant-analyzer-component-toxins'}" StyleClasses="LabelSubText" HorizontalExpand="True" />
|
||||
<Label Name="ToxinsLabel" FontColorOverride="YellowGreen" MinWidth="40" HorizontalAlignment="Right" />
|
||||
<Label Text="/ 100" StyleClasses="LabelSubText" MinWidth="35" />
|
||||
<Label Name="ToxinsResistanceLabel" Text="←" StyleClasses="LabelSubText" MinWidth="15" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Pests -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 2">
|
||||
<Label MinWidth="20" />
|
||||
<Label Text="{Loc 'plant-analyzer-component-pests'}" StyleClasses="LabelSubText" HorizontalExpand="True" />
|
||||
<Label Name="PestLevelLabel" FontColorOverride="HotPink" MinWidth="40" HorizontalAlignment="Right" />
|
||||
<Label Text="/ 10" StyleClasses="LabelSubText" MinWidth="35" />
|
||||
<Label Name="PestResistanceLabel" Text="←" StyleClasses="LabelSubText" MinWidth="15" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Weeds -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 2">
|
||||
<Label MinWidth="20" />
|
||||
<Label Text="{Loc 'plant-analyzer-component-weeds'}" StyleClasses="LabelSubText" HorizontalExpand="True" />
|
||||
<Label Name="WeedLevelLabel" FontColorOverride="Red" MinWidth="40" HorizontalAlignment="Right" />
|
||||
<Label Text="/ 10" StyleClasses="LabelSubText" MinWidth="35" />
|
||||
<Label Name="WeedResistanceLabel" Text="←" StyleClasses="LabelSubText" MinWidth="15" HorizontalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="ContainerDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Soil Chemicals -->
|
||||
<BoxContainer Name="ChemicalsInWaterBox" Orientation="Vertical" Visible="False" Margin="0 4">
|
||||
<Label Text="{Loc 'plant-analyzer-soil-chemicals'}" StyleClasses="LabelHeading" />
|
||||
<RichTextLabel Name="ChemicalsInWaterLabel" SetWidth="300" Margin="4 2" />
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="ChemicalsInWaterDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Ideal Environment -->
|
||||
<BoxContainer Name="EnvironmentBox" Orientation="Vertical" Visible="False" Margin="0 4">
|
||||
<Label Text="{Loc 'plant-analyzer-ideal-environment'}" StyleClasses="LabelHeading" />
|
||||
<RichTextLabel Name="EnvironmentLabel" SetWidth="300" Margin="4 2" />
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="EnvironmentDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Plant Output -->
|
||||
<BoxContainer Name="ProduceBox" Orientation="Vertical" Visible="False" Margin="0 4">
|
||||
<Label Text="{Loc 'plant-analyzer-plant-output'}" StyleClasses="LabelHeading" />
|
||||
<RichTextLabel Name="ProduceLabel" SetWidth="300" Margin="4 2" />
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer Name="ProduceDivider" Visible="False" StyleClasses="LowDivider" />
|
||||
|
||||
<!-- Print Button -->
|
||||
<Button Name="Print"
|
||||
Access="Public"
|
||||
TextAlign="Center"
|
||||
HorizontalExpand="True"
|
||||
Disabled="True"
|
||||
Margin="0 8 0 0"
|
||||
ToggleMode="False"
|
||||
Text="{Loc 'plant-analyzer-print-report'}" />
|
||||
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</controls:FancyWindow>
|
||||
264
Content.Client/_Wega/Botany/PlantAnalyzerWindow.xaml.cs
Normal file
264
Content.Client/_Wega/Botany/PlantAnalyzerWindow.xaml.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Botany.Systems;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Botany.PlantAnalyzer;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._Wega.Botany;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class PlantAnalyzerWindow : FancyWindow
|
||||
{
|
||||
private readonly EntityManager _entityManager;
|
||||
private readonly IGameTiming _gameTiming;
|
||||
private readonly PlantAnalyzerSystem _plant;
|
||||
|
||||
public PlantAnalyzerWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
var dependencies = IoCManager.Instance!;
|
||||
_entityManager = dependencies.Resolve<EntityManager>();
|
||||
_gameTiming = dependencies.Resolve<IGameTiming>();
|
||||
_plant = _entityManager.System<PlantAnalyzerSystem>();
|
||||
}
|
||||
|
||||
public void Populate(PlantAnalyzerScannedUserMessage msg)
|
||||
{
|
||||
Print.Disabled = !msg.ScanMode.GetValueOrDefault(false)
|
||||
|| msg.PrintReadyAt.GetValueOrDefault(TimeSpan.MaxValue) > _gameTiming.CurTime
|
||||
|| msg.PlantData is null;
|
||||
|
||||
var target = _entityManager.GetEntity(msg.TargetEntity);
|
||||
|
||||
// Header Section
|
||||
SpriteView.SetEntity(target);
|
||||
SpriteView.Visible = msg.ScanMode.HasValue && msg.ScanMode.Value;
|
||||
NoDataIcon.Visible = !SpriteView.Visible;
|
||||
|
||||
ScanModeLabel.Text = msg.ScanMode.HasValue
|
||||
? msg.ScanMode.Value
|
||||
? Loc.GetString("plant-analyzer-scan-active")
|
||||
: Loc.GetString("plant-analyzer-scan-inactive")
|
||||
: Loc.GetString("plant-analyzer-no-data");
|
||||
|
||||
ScanModeLabel.Modulate = msg.ScanMode.HasValue && msg.ScanMode.Value
|
||||
? Color.FromHex("#4CAF50")
|
||||
: Color.FromHex("#F44336");
|
||||
|
||||
SeedLabel.Text = msg.PlantData == null
|
||||
? Loc.GetString("plant-analyzer-no-plant")
|
||||
: Loc.GetString(msg.PlantData.SeedDisplayName);
|
||||
|
||||
ContainerLabel.Text = target != null && _entityManager.HasComponent<MetaDataComponent>(target.Value)
|
||||
? Identity.Name(target.Value, _entityManager)
|
||||
: Loc.GetString("plant-analyzer-unknown-container");
|
||||
|
||||
// Plant Status Tags
|
||||
if (msg.PlantData is not null)
|
||||
{
|
||||
Dead.Visible = msg.PlantData.Dead;
|
||||
Alive.Visible = !Dead.Visible;
|
||||
Unviable.Visible = !msg.PlantData.Viable;
|
||||
Kudzu.Visible = msg.PlantData.Kudzu;
|
||||
Mutating.Visible = msg.PlantData.Mutating;
|
||||
|
||||
Alive.Modulate = Color.Green;
|
||||
Dead.Modulate = Color.Red;
|
||||
Unviable.Modulate = Color.Red;
|
||||
Mutating.Modulate = Color.Violet;
|
||||
Kudzu.Modulate = Color.Red;
|
||||
|
||||
PlantDataTags.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlantDataTags.Visible = false;
|
||||
}
|
||||
|
||||
// Plant Health Stats
|
||||
if (msg.PlantData is not null)
|
||||
{
|
||||
Health.Text = msg.PlantData.Health.ToString("0.0");
|
||||
Endurance.Text = msg.PlantData.Endurance.ToString("0.0");
|
||||
Age.Text = msg.PlantData.Age.ToString("0.0");
|
||||
Lifespan.Text = msg.PlantData.Lifespan.ToString("0.0");
|
||||
Maturation.Text = msg.PlantData.Maturation.ToString("0.0");
|
||||
|
||||
PlantDataGrid.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlantDataGrid.Visible = false;
|
||||
}
|
||||
PlantDataDivider.Visible = PlantDataGrid.Visible;
|
||||
|
||||
// Tray Conditions
|
||||
if (msg.TrayData is not null)
|
||||
{
|
||||
WaterLevelLabel.Text = msg.TrayData.WaterLevel.ToString("0.0");
|
||||
NutritionLevelLabel.Text = msg.TrayData.NutritionLevel.ToString("0.0");
|
||||
ToxinsLabel.Text = msg.TrayData.Toxins.ToString("0.0");
|
||||
PestLevelLabel.Text = msg.TrayData.PestLevel.ToString("0.1");
|
||||
WeedLevelLabel.Text = msg.TrayData.WeedLevel.ToString("0.1");
|
||||
|
||||
// Set consumption/resistance indicators
|
||||
if (msg.TolerancesData is not null)
|
||||
{
|
||||
WaterConsumptionLabel.Text = $"→{msg.TolerancesData.WaterConsumption:0.0}";
|
||||
NutritionConsumptionLabel.Text = $"→{msg.TolerancesData.NutrientConsumption:0.0}";
|
||||
ToxinsResistanceLabel.Text = $"←{msg.TolerancesData.ToxinsTolerance:0.0}";
|
||||
PestResistanceLabel.Text = $"←{msg.TolerancesData.PestTolerance:0.1}";
|
||||
WeedResistanceLabel.Text = $"←{msg.TolerancesData.WeedTolerance:0.1}";
|
||||
}
|
||||
else
|
||||
{
|
||||
WaterConsumptionLabel.Text = "→?";
|
||||
NutritionConsumptionLabel.Text = "→?";
|
||||
ToxinsResistanceLabel.Text = "←?";
|
||||
PestResistanceLabel.Text = "←?";
|
||||
WeedResistanceLabel.Text = "←?";
|
||||
}
|
||||
|
||||
ContainerGrid.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ContainerGrid.Visible = false;
|
||||
}
|
||||
ContainerDivider.Visible = ContainerGrid.Visible;
|
||||
|
||||
// Soil Chemicals
|
||||
if (msg.TrayData?.Chemicals != null && msg.TrayData.Chemicals.Any())
|
||||
{
|
||||
var chemicals = _plant.ChemicalsToLocalizedStrings(msg.TrayData.Chemicals);
|
||||
ChemicalsInWaterLabel.Text = chemicals;
|
||||
ChemicalsInWaterBox.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ChemicalsInWaterBox.Visible = false;
|
||||
}
|
||||
ChemicalsInWaterDivider.Visible = ChemicalsInWaterBox.Visible;
|
||||
|
||||
// Ideal Environment
|
||||
if (msg.TolerancesData is not null)
|
||||
{
|
||||
var environmentText = GenerateEnvironmentText(msg.TolerancesData);
|
||||
EnvironmentLabel.SetMessage(environmentText);
|
||||
EnvironmentBox.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnvironmentBox.Visible = false;
|
||||
}
|
||||
EnvironmentDivider.Visible = EnvironmentBox.Visible;
|
||||
|
||||
// Plant Output
|
||||
if (msg.ProduceData is not null)
|
||||
{
|
||||
var outputText = GenerateOutputText(msg.ProduceData);
|
||||
ProduceLabel.SetMessage(outputText);
|
||||
ProduceBox.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProduceBox.Visible = false;
|
||||
}
|
||||
ProduceDivider.Visible = ProduceBox.Visible;
|
||||
}
|
||||
|
||||
private FormattedMessage GenerateEnvironmentText(PlantAnalyzerTolerancesData data)
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
|
||||
// Temperature
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-temperature")}: ");
|
||||
msg.PushColor(Color.LightSkyBlue);
|
||||
msg.AddText($"{data.IdealHeat:0.0}°C ±{data.HeatTolerance:0.0}°C\n");
|
||||
msg.Pop();
|
||||
|
||||
// Pressure
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-pressure")}: ");
|
||||
msg.PushColor(Color.LightGreen);
|
||||
msg.AddText($"{data.IdealPressure:0.0} kPa ±{data.PressureTolerance:0.0} kPa\n");
|
||||
msg.Pop();
|
||||
|
||||
// Light
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-light")}: ");
|
||||
msg.PushColor(Color.Gold);
|
||||
msg.AddText($"{data.IdealLight:0.0} L ±{data.LightTolerance:0.0} L\n");
|
||||
msg.Pop();
|
||||
|
||||
// Gases
|
||||
if (data.ConsumeGasses.Any())
|
||||
{
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-required-gases")}: ");
|
||||
msg.PushColor(Color.Plum);
|
||||
msg.AddText(_plant.GasesToLocalizedStrings(data.ConsumeGasses));
|
||||
msg.Pop();
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
private FormattedMessage GenerateOutputText(PlantAnalyzerProduceData data)
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
|
||||
// Yield
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-yield")}: ");
|
||||
msg.PushColor(data.Yield > 0 ? Color.LightGreen : Color.LightGray);
|
||||
msg.AddText($"{data.Yield}\n");
|
||||
msg.Pop();
|
||||
|
||||
// Potency
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-potency")}: ");
|
||||
msg.PushColor(Color.Gold);
|
||||
msg.AddText($"{Loc.GetString(data.Potency)}\n");
|
||||
msg.Pop();
|
||||
|
||||
// Produce
|
||||
if (data.Produce.Any())
|
||||
{
|
||||
var (_, plural, _) = _plant.ProduceToLocalizedStrings(data.Produce);
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-produces")}: ");
|
||||
msg.PushColor(Color.LightBlue);
|
||||
msg.AddText($"{plural}\n");
|
||||
msg.Pop();
|
||||
}
|
||||
|
||||
// Chemicals
|
||||
if (data.Chemicals.Any())
|
||||
{
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-chemicals")}: ");
|
||||
msg.PushColor(Color.MediumPurple);
|
||||
msg.AddText(_plant.ChemicalsToLocalizedStrings(data.Chemicals) + "\n");
|
||||
msg.Pop();
|
||||
}
|
||||
|
||||
// Gases
|
||||
if (data.ExudeGasses.Any())
|
||||
{
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-exudes")}: ");
|
||||
msg.PushColor(Color.LightCoral);
|
||||
msg.AddText(_plant.GasesToLocalizedStrings(data.ExudeGasses) + "\n");
|
||||
msg.Pop();
|
||||
}
|
||||
|
||||
// Seed info
|
||||
msg.AddText($"{Loc.GetString("plant-analyzer-seeds")}: ");
|
||||
msg.PushColor(data.Seedless ? Color.Orange : Color.LightGreen);
|
||||
msg.AddText(data.Seedless ?
|
||||
Loc.GetString("plant-analyzer-seedless") :
|
||||
Loc.GetString("plant-analyzer-produces-seeds"));
|
||||
msg.Pop();
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
74
Content.Client/_Wega/Crayon/CrayonPreviewOverlay.cs
Normal file
74
Content.Client/_Wega/Crayon/CrayonPreviewOverlay.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Crayon;
|
||||
|
||||
public sealed class CrayonPreviewOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IInputManager _input = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
private readonly SpriteSystem _sprite;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
|
||||
|
||||
public CrayonPreviewOverlay(SpriteSystem sprite)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sprite = sprite;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
if (_player.LocalEntity is not { } player)
|
||||
return;
|
||||
|
||||
var hand = _entMan.System<SharedHandsSystem>();
|
||||
var active = hand.GetActiveItem(player);
|
||||
|
||||
if (!_entMan.TryGetComponent(active, out CrayonComponent? crayon))
|
||||
return;
|
||||
|
||||
if (!_proto.TryIndex<DecalPrototype>(crayon.SelectedState, out var decalProto))
|
||||
return;
|
||||
|
||||
var texture = _sprite.Frame0(decalProto.Sprite);
|
||||
|
||||
var mousePos = _input.MouseScreenPosition;
|
||||
var worldPos = _eye.ScreenToMap(mousePos);
|
||||
|
||||
if (worldPos.MapId != args.MapId)
|
||||
return;
|
||||
|
||||
var transform = _entMan.System<TransformSystem>();
|
||||
var playerPos = transform.GetMapCoordinates(player);
|
||||
var distance = (worldPos.Position - playerPos.Position).Length();
|
||||
|
||||
var alpha = 0.7f;
|
||||
if (distance > 2f)
|
||||
alpha = 0.3f;
|
||||
else if (distance > 1.5f)
|
||||
alpha = 0.5f;
|
||||
|
||||
var color = crayon.Color.WithAlpha(alpha);
|
||||
var position = worldPos.Position - new Vector2(0.5f, 0.5f);
|
||||
|
||||
var grid = transform.GetGrid(player);
|
||||
Angle rot = grid != null ? transform.GetWorldRotation(grid.Value) : 0;
|
||||
|
||||
worldHandle.DrawTexture(texture, position, rot + crayon.Angle, color);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public sealed class ListenUpSystem : SharedListenUpSkillSystem
|
||||
|
||||
private void SwithOverlay(Entity<ListenUpComponent> ent, bool active)
|
||||
{
|
||||
Overlay overlay = ListenUp(ent.Comp.radius, ent.Comp.Sprite);
|
||||
Overlay overlay = ListenUp(ent.Comp.Radius, ent.Comp.Sprite);
|
||||
UpdateOverlay(active, overlay);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,17 +149,7 @@ public sealed partial class BotanySystem : EntitySystem
|
||||
|
||||
public IEnumerable<EntityUid> GenerateProduct(SeedData proto, EntityCoordinates position, int yieldMod = 1)
|
||||
{
|
||||
var totalYield = 0;
|
||||
if (proto.Yield > -1)
|
||||
{
|
||||
if (yieldMod < 0)
|
||||
totalYield = proto.Yield;
|
||||
else
|
||||
totalYield = proto.Yield * yieldMod;
|
||||
|
||||
totalYield = Math.Max(1, totalYield);
|
||||
}
|
||||
|
||||
var totalYield = CalculateTotalYield(proto.Yield, yieldMod); // Corvax-Wega-Edit
|
||||
var products = new List<EntityUid>();
|
||||
|
||||
if (totalYield > 1 || proto.HarvestRepeat != HarvestType.NoRepeat)
|
||||
@@ -197,5 +187,22 @@ public sealed partial class BotanySystem : EntitySystem
|
||||
return !proto.Ligneous || proto.Ligneous && held != null && HasComp<SharpComponent>(held);
|
||||
}
|
||||
|
||||
// Corvax-Wega-Add-start
|
||||
public int CalculateTotalYield(int yield, int yieldMod)
|
||||
{
|
||||
var totalYield = 0;
|
||||
if (yield > -1)
|
||||
{
|
||||
if (yieldMod < 0)
|
||||
totalYield = yield;
|
||||
else
|
||||
totalYield = yield * yieldMod;
|
||||
|
||||
totalYield = Math.Max(1, totalYield);
|
||||
}
|
||||
return totalYield;
|
||||
}
|
||||
// Corvax-Wega-Add-end
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Charges.Systems;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
@@ -26,6 +27,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!; // Corvax-Wega-Add
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -37,6 +40,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
SubscribeLocalEvent<CrayonComponent, UseInHandEvent>(OnCrayonUse);
|
||||
SubscribeLocalEvent<CrayonComponent, AfterInteractEvent>(OnCrayonAfterInteract, after: [typeof(IngestionSystem)]);
|
||||
SubscribeLocalEvent<CrayonComponent, DroppedEvent>(OnCrayonDropped);
|
||||
|
||||
SubscribeNetworkEvent<CrayonRotateEvent>(OnCrayonRotate); // Corvax-Wega-Add
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<CrayonComponent> ent, ref MapInitEvent args)
|
||||
@@ -71,15 +76,23 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_decals.TryAddDecal(component.SelectedState, args.ClickLocation.Offset(new Vector2(-0.5f, -0.5f)), out _, component.Color, cleanable: true))
|
||||
// Corvax-Wega-Edit-start
|
||||
var grid = _transform.GetGrid(args.User);
|
||||
Angle rot = grid != null ? _transform.GetWorldRotation(grid.Value) : 0;
|
||||
|
||||
if (!_decals.TryAddDecal(component.SelectedState, args.ClickLocation.Offset(new Vector2(-0.5f, -0.5f)),
|
||||
out _, component.Color, rot + component.Angle, cleanable: true))
|
||||
return;
|
||||
// Corvax-Wega-Edit-end
|
||||
|
||||
if (component.UseSound != null)
|
||||
_audio.PlayPvs(component.UseSound, uid, AudioParams.Default.WithVariation(0.125f));
|
||||
|
||||
_charges.TryUseCharge(uid);
|
||||
|
||||
_adminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}");
|
||||
_adminLogger.Add(LogType.CrayonDraw, LogImpact.Low,
|
||||
$"{ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}");
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
if (component.DeleteEmpty && _charges.IsEmpty(uid))
|
||||
@@ -90,7 +103,6 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
|
||||
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
|
||||
{
|
||||
// Open crayon window if neccessary.
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
@@ -105,7 +117,6 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
|
||||
private void OnCrayonBoundUI(EntityUid uid, CrayonComponent component, CrayonSelectMessage args)
|
||||
{
|
||||
// Check if the selected state is valid
|
||||
if (!_prototypeManager.TryIndex<DecalPrototype>(args.State, out var prototype) || !prototype.Tags.Contains("crayon"))
|
||||
return;
|
||||
|
||||
@@ -134,4 +145,19 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
_popup.PopupEntity(Loc.GetString("crayon-interact-used-up-text", ("owner", uid)), user, user);
|
||||
QueueDel(uid);
|
||||
}
|
||||
|
||||
// Corvax-Wega-Add-start
|
||||
private void OnCrayonRotate(CrayonRotateEvent args, EntitySessionEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.SenderSession.AttachedEntity is not { } player)
|
||||
return;
|
||||
|
||||
var active = _hands.GetActiveItem(player);
|
||||
if (!TryComp<CrayonComponent>(active, out var crayon))
|
||||
return;
|
||||
|
||||
crayon.Angle += args.Angle;
|
||||
Dirty(active.Value, crayon);
|
||||
}
|
||||
// Corvax-Wega-Add-end
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
if (args.SenderSession.AttachedEntity != instrument.InstrumentPlayer)
|
||||
return;
|
||||
|
||||
if (master != null)
|
||||
if (master != null && !HasComp<PrivateListeningComponent>(uid)) // Corvax-Wega-Edit
|
||||
{
|
||||
if (!HasComp<ActiveInstrumentComponent>(master))
|
||||
return;
|
||||
@@ -283,6 +283,7 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
|
||||
var list = new ValueList<(NetEntity, string)>();
|
||||
var instrumentQuery = GetEntityQuery<InstrumentComponent>();
|
||||
var privateQuery = GetEntityQuery<PrivateListeningComponent>(); // Corvax-Wega-Add
|
||||
|
||||
if (!TryComp(uid, out InstrumentComponent? originInstrument)
|
||||
|| originInstrument.InstrumentPlayer is not {} originPlayer)
|
||||
@@ -299,6 +300,9 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
if (!instrumentQuery.TryGetComponent(entity, out var instrument) || instrument.Master != null)
|
||||
continue;
|
||||
|
||||
if (privateQuery.HasComponent(entity)) // Corvax-Wega-Add
|
||||
continue; // Corvax-Wega-Add
|
||||
|
||||
// We want to use the instrument player's name.
|
||||
if (instrument.InstrumentPlayer is not {} playerUid)
|
||||
continue;
|
||||
@@ -409,7 +413,18 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
|
||||
if (send || !instrument.RespectMidiLimits)
|
||||
{
|
||||
RaiseNetworkEvent(msg);
|
||||
// Corvax-Wega-Edit-start
|
||||
var listener = GetInstrumentListener(uid, instrument);
|
||||
if (listener != null)
|
||||
{
|
||||
var filer = Filter.Empty().AddPlayer(args.SenderSession);
|
||||
RaiseNetworkEvent(msg, filer);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseNetworkEvent(msg);
|
||||
}
|
||||
// Corvax-Wega-Edit-end
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -66,6 +67,7 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem
|
||||
SubscribeLocalEvent<BloodCultRuleComponent, ComponentShutdown>(OnRuleShutdown);
|
||||
SubscribeLocalEvent<BloodCultistComponent, BloodCultObjectiveActionEvent>(OnCheckObjective);
|
||||
SubscribeLocalEvent<BloodCultistComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<BloodCultistComponent, ShotAttemptedEvent>(OnShotAttempted); // Corvax-Wega-Testing
|
||||
SubscribeLocalEvent<BloodCultConstructComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<BloodCultObjectComponent, ComponentShutdown>(OnComponentShutdown);
|
||||
SubscribeLocalEvent<BloodCultObjectComponent, CryostorageEnterEvent>(OnCryostorageEnter);
|
||||
@@ -141,6 +143,15 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem
|
||||
}
|
||||
}
|
||||
|
||||
// Corvax-Wega-Testing-start
|
||||
// Да я пометил тегами чтобы банально не забыть про это и чо?
|
||||
private void OnShotAttempted(Entity<BloodCultistComponent> ent, ref ShotAttemptedEvent args)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("gun-disabled"), ent, ent);
|
||||
args.Cancel();
|
||||
}
|
||||
// Corvax-Wega-Testing-end
|
||||
|
||||
#region Stages Update
|
||||
private void OnRuleShutdown(EntityUid uid, BloodCultRuleComponent component, ComponentShutdown args)
|
||||
{
|
||||
|
||||
350
Content.Server/_Wega/Botany/PlantAnalyzerSystem.cs
Normal file
350
Content.Server/_Wega/Botany/PlantAnalyzerSystem.cs
Normal file
@@ -0,0 +1,350 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Botany.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Botany.Components;
|
||||
using Content.Shared.Botany.PlantAnalyzer;
|
||||
using Content.Shared.Botany.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Labels.EntitySystems;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
|
||||
namespace Content.Server.Botany.Systems;
|
||||
|
||||
public sealed class PlantAnalyzerSystem : SharedPlantAnalyzerSystem
|
||||
{
|
||||
[Dependency] private readonly BotanySystem _botany = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly PaperSystem _paperSystem = default!;
|
||||
[Dependency] private readonly LabelSystem _labelSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly PowerCellSystem _cell = default!;
|
||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PlantAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<PlantAnalyzerComponent, PlantAnalyzerDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<PlantAnalyzerComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<PlantAnalyzerComponent, PlantAnalyzerPrintMessage>(OnPrint);
|
||||
SubscribeLocalEvent<PlantAnalyzerComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
||||
SubscribeLocalEvent<PlantAnalyzerComponent, DroppedEvent>(OnDropped);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var analyzerQuery = EntityQueryEnumerator<PlantAnalyzerComponent, TransformComponent>();
|
||||
while (analyzerQuery.MoveNext(out var uid, out var component, out var transform))
|
||||
{
|
||||
if (component.NextUpdate > _gameTiming.CurTime)
|
||||
continue;
|
||||
|
||||
if (component.ScannedEntity is not { } target)
|
||||
continue;
|
||||
|
||||
if (Deleted(target))
|
||||
{
|
||||
StopAnalyzingEntity((uid, component));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_cell.HasDrawCharge(uid))
|
||||
{
|
||||
StopAnalyzingEntity((uid, component));
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetCoordinates = Transform(target).Coordinates;
|
||||
if (!_transform.InRange(targetCoordinates, transform.Coordinates, component.MaxScanRange))
|
||||
{
|
||||
StopAnalyzingEntity((uid, component));
|
||||
continue;
|
||||
}
|
||||
|
||||
component.NextUpdate = _gameTiming.CurTime + component.UpdateInterval;
|
||||
UpdateScannedUser(uid, target, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAfterInteract(Entity<PlantAnalyzerComponent> entity, ref AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || !args.CanReach || !HasComp<PlantHolderComponent>(args.Target) || !_cell.HasDrawCharge(entity, user: args.User))
|
||||
return;
|
||||
|
||||
StartScan(entity, args.User, args.Target.Value);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnUseInHand(Entity<PlantAnalyzerComponent> entity, ref UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!_cell.HasDrawCharge(entity, user: args.User))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("plant-analyzer-no-power"), entity, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
OpenUserInterface(args.User, entity.Owner);
|
||||
|
||||
if (entity.Comp.ScannedEntity != null)
|
||||
{
|
||||
UpdateScannedUser(entity.Owner, entity.Comp.ScannedEntity.Value, true);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnDoAfter(Entity<PlantAnalyzerComponent> entity, ref PlantAnalyzerDoAfterEvent args)
|
||||
{
|
||||
var target = args.Target;
|
||||
if (args.Cancelled || args.Handled || target == null || !_cell.HasDrawCharge(entity, user: args.User))
|
||||
return;
|
||||
|
||||
if (!entity.Comp.Silent)
|
||||
_audioSystem.PlayPvs(entity.Comp.ScanningEndSound, entity);
|
||||
|
||||
OpenUserInterface(args.User, entity.Owner);
|
||||
BeginAnalyzingEntity(entity, target.Value);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnInsertedIntoContainer(Entity<PlantAnalyzerComponent> analyzer, ref EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (analyzer.Comp.ScannedEntity is { } _)
|
||||
_toggle.TryDeactivate(analyzer.Owner);
|
||||
}
|
||||
|
||||
private void OnDropped(Entity<PlantAnalyzerComponent> analyzer, ref DroppedEvent args)
|
||||
{
|
||||
if (analyzer.Comp.ScannedEntity is { } _)
|
||||
_toggle.TryDeactivate(analyzer.Owner);
|
||||
}
|
||||
|
||||
private void StartScan(Entity<PlantAnalyzerComponent> analyzer, EntityUid user, EntityUid target)
|
||||
{
|
||||
_audioSystem.PlayPvs(analyzer.Comp.ScanningBeginSound, analyzer);
|
||||
|
||||
var doAfterArgs = new DoAfterArgs(EntityManager, user, analyzer.Comp.ScanDelay, new PlantAnalyzerDoAfterEvent(), analyzer, target, analyzer)
|
||||
{
|
||||
NeedHand = true,
|
||||
BreakOnMove = true,
|
||||
};
|
||||
|
||||
if (!_doAfterSystem.TryStartDoAfter(doAfterArgs))
|
||||
return;
|
||||
|
||||
if (target == user || analyzer.Comp.Silent)
|
||||
return;
|
||||
|
||||
var msg = Loc.GetString("plant-analyzer-popup-scan-target", ("target", target));
|
||||
_popupSystem.PopupEntity(msg, user, user);
|
||||
}
|
||||
|
||||
private void OpenUserInterface(EntityUid user, EntityUid analyzer)
|
||||
{
|
||||
if (!_uiSystem.HasUi(analyzer, PlantAnalyzerUiKey.Key))
|
||||
return;
|
||||
|
||||
_uiSystem.OpenUi(analyzer, PlantAnalyzerUiKey.Key, user);
|
||||
}
|
||||
|
||||
private void BeginAnalyzingEntity(Entity<PlantAnalyzerComponent> analyzer, EntityUid target)
|
||||
{
|
||||
analyzer.Comp.ScannedEntity = target;
|
||||
analyzer.Comp.NextUpdate = _gameTiming.CurTime;
|
||||
|
||||
_toggle.TryActivate(analyzer.Owner);
|
||||
|
||||
Timer.Spawn(100, () =>
|
||||
{
|
||||
UpdateScannedUser(analyzer.Owner, target, true);
|
||||
});
|
||||
}
|
||||
|
||||
private void StopAnalyzingEntity(Entity<PlantAnalyzerComponent> analyzer)
|
||||
{
|
||||
analyzer.Comp.ScannedEntity = null;
|
||||
|
||||
_toggle.TryDeactivate(analyzer.Owner);
|
||||
|
||||
if (_uiSystem.HasUi(analyzer.Owner, PlantAnalyzerUiKey.Key))
|
||||
{
|
||||
var actors = _uiSystem.GetActors(analyzer.Owner, PlantAnalyzerUiKey.Key);
|
||||
foreach (var actor in actors)
|
||||
{
|
||||
_uiSystem.CloseUi(analyzer.Owner, PlantAnalyzerUiKey.Key, actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateScannedUser(EntityUid analyzer, EntityUid target, bool scanMode)
|
||||
{
|
||||
if (!_uiSystem.HasUi(analyzer, PlantAnalyzerUiKey.Key))
|
||||
return;
|
||||
|
||||
if (!HasComp<PlantHolderComponent>(target))
|
||||
return;
|
||||
|
||||
if (!TryComp<PlantAnalyzerComponent>(analyzer, out var analyzerComponent))
|
||||
return;
|
||||
|
||||
_uiSystem.ServerSendUiMessage(analyzer, PlantAnalyzerUiKey.Key, GatherData(analyzerComponent, scanMode, target));
|
||||
}
|
||||
|
||||
private PlantAnalyzerScannedUserMessage GatherData(PlantAnalyzerComponent analyzer, bool? scanMode = null, EntityUid? target = null)
|
||||
{
|
||||
PlantAnalyzerPlantData? plantData = null;
|
||||
PlantAnalyzerTrayData? trayData = null;
|
||||
PlantAnalyzerTolerancesData? tolerancesData = null;
|
||||
PlantAnalyzerProduceData? produceData = null;
|
||||
|
||||
if (target != null && TryComp<PlantHolderComponent>(target, out var plantHolder))
|
||||
{
|
||||
if (plantHolder.Seed is not null)
|
||||
{
|
||||
plantData = new PlantAnalyzerPlantData(
|
||||
seedDisplayName: plantHolder.Seed.DisplayName,
|
||||
health: plantHolder.Health,
|
||||
endurance: plantHolder.Seed.Endurance,
|
||||
age: plantHolder.Age,
|
||||
lifespan: plantHolder.Seed.Lifespan,
|
||||
maturation: plantHolder.Seed.Maturation,
|
||||
dead: plantHolder.Dead,
|
||||
viable: plantHolder.Seed.Viable,
|
||||
mutating: plantHolder.MutationLevel > 0f,
|
||||
kudzu: plantHolder.Seed.TurnIntoKudzu
|
||||
);
|
||||
tolerancesData = new PlantAnalyzerTolerancesData(
|
||||
waterConsumption: plantHolder.Seed.WaterConsumption,
|
||||
nutrientConsumption: plantHolder.Seed.NutrientConsumption,
|
||||
toxinsTolerance: plantHolder.Seed.ToxinsTolerance,
|
||||
pestTolerance: plantHolder.Seed.PestTolerance,
|
||||
weedTolerance: plantHolder.Seed.WeedTolerance,
|
||||
lowPressureTolerance: plantHolder.Seed.LowPressureTolerance,
|
||||
highPressureTolerance: plantHolder.Seed.HighPressureTolerance,
|
||||
idealHeat: plantHolder.Seed.IdealHeat,
|
||||
heatTolerance: plantHolder.Seed.HeatTolerance,
|
||||
idealLight: plantHolder.Seed.IdealLight,
|
||||
lightTolerance: plantHolder.Seed.LightTolerance,
|
||||
consumeGasses: [.. plantHolder.Seed.ConsumeGasses.Keys]
|
||||
);
|
||||
produceData = new PlantAnalyzerProduceData(
|
||||
yield: plantHolder.Seed.ProductPrototypes.Count == 0 ? 0 : _botany.CalculateTotalYield(plantHolder.Seed.Yield, plantHolder.YieldMod),
|
||||
potency: plantHolder.Seed.Potency,
|
||||
chemicals: [.. plantHolder.Seed.Chemicals.Keys],
|
||||
produce: plantHolder.Seed.ProductPrototypes,
|
||||
exudeGasses: [.. plantHolder.Seed.ExudeGasses.Keys],
|
||||
seedless: plantHolder.Seed.Seedless
|
||||
);
|
||||
}
|
||||
trayData = new PlantAnalyzerTrayData(
|
||||
waterLevel: plantHolder.WaterLevel,
|
||||
nutritionLevel: plantHolder.NutritionLevel,
|
||||
toxins: plantHolder.Toxins,
|
||||
pestLevel: plantHolder.PestLevel,
|
||||
weedLevel: plantHolder.WeedLevel,
|
||||
chemicals: plantHolder.SoilSolution?.Comp.Solution.Contents.Select(r => r.Reagent.Prototype).ToList()
|
||||
);
|
||||
}
|
||||
|
||||
return new PlantAnalyzerScannedUserMessage(
|
||||
GetNetEntity(target),
|
||||
scanMode,
|
||||
plantData,
|
||||
trayData,
|
||||
tolerancesData,
|
||||
produceData,
|
||||
analyzer.PrintReadyAt
|
||||
);
|
||||
}
|
||||
|
||||
private void OnPrint(EntityUid uid, PlantAnalyzerComponent component, PlantAnalyzerPrintMessage args)
|
||||
{
|
||||
var user = args.Actor;
|
||||
|
||||
if (!_cell.HasDrawCharge(uid, user: user))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("plant-analyzer-no-power"), uid, user);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameTiming.CurTime < component.PrintReadyAt)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("plant-analyzer-printer-not-ready"), uid, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn a piece of paper.
|
||||
var printed = EntityManager.SpawnEntity(component.MachineOutput, Transform(uid).Coordinates);
|
||||
_handsSystem.PickupOrDrop(args.Actor, printed, checkActionBlocker: false);
|
||||
|
||||
if (!TryComp<PaperComponent>(printed, out var paperComp))
|
||||
{
|
||||
Log.Error("Printed paper did not have PaperComponent.");
|
||||
return;
|
||||
}
|
||||
|
||||
var target = component.ScannedEntity;
|
||||
var data = GatherData(component, true, target);
|
||||
|
||||
var missingData = Loc.GetString("plant-analyzer-printout-missing");
|
||||
|
||||
var seedName = data.PlantData is not null ? Loc.GetString(data.PlantData.SeedDisplayName) : null;
|
||||
(string, object)[] parameters = [
|
||||
("seedName", seedName ?? missingData),
|
||||
("produce", data.ProduceData is not null ? ProduceToLocalizedStrings(data.ProduceData.Produce).Plural : missingData),
|
||||
("water", data.TolerancesData?.WaterConsumption.ToString("0.00") ?? missingData),
|
||||
("nutrients", data.TolerancesData?.NutrientConsumption.ToString("0.00") ?? missingData),
|
||||
("toxins", data.TolerancesData?.ToxinsTolerance.ToString("0.00") ?? missingData),
|
||||
("pests", data.TolerancesData?.PestTolerance.ToString("0.00") ?? missingData),
|
||||
("weeds", data.TolerancesData?.WeedTolerance.ToString("0.00") ?? missingData),
|
||||
("gasesIn", data.TolerancesData is not null ? GasesToLocalizedStrings(data.TolerancesData.ConsumeGasses) : missingData),
|
||||
("kpa", data.TolerancesData?.IdealPressure.ToString("0.00") ?? missingData),
|
||||
("kpaTolerance", data.TolerancesData?.PressureTolerance.ToString("0.00") ?? missingData),
|
||||
("temp", data.TolerancesData?.IdealHeat.ToString("0.00") ?? missingData),
|
||||
("tempTolerance", data.TolerancesData?.HeatTolerance.ToString("0.00") ?? missingData),
|
||||
("lightLevel", data.TolerancesData?.IdealLight.ToString("0.00") ?? missingData),
|
||||
("lightTolerance", data.TolerancesData?.LightTolerance.ToString("0.00") ?? missingData),
|
||||
("yield", data.ProduceData?.Yield ?? -1),
|
||||
("potency", data.ProduceData is not null ? Loc.GetString(data.ProduceData.Potency) : missingData),
|
||||
("chemicals", data.ProduceData is not null ? ChemicalsToLocalizedStrings(data.ProduceData.Chemicals) : missingData),
|
||||
("gasesOut", data.ProduceData is not null ? GasesToLocalizedStrings(data.ProduceData.ExudeGasses) : missingData),
|
||||
("endurance", data.PlantData?.Endurance.ToString("0.00") ?? missingData),
|
||||
("lifespan", data.PlantData?.Lifespan.ToString("0.00") ?? missingData),
|
||||
("seeds", data.ProduceData is not null ? (data.ProduceData.Seedless ? "no" : "yes") : "other"),
|
||||
("viable", data.PlantData is not null ? (data.PlantData.Viable ? "yes" : "no") : "other"),
|
||||
("kudzu", data.PlantData is not null ? (data.PlantData.Kudzu ? "yes" : "no") : "other")
|
||||
];
|
||||
|
||||
_paperSystem.SetContent((printed, paperComp), Loc.GetString($"plant-analyzer-printout", [.. parameters]));
|
||||
_labelSystem.Label(printed, seedName);
|
||||
_audioSystem.PlayPvs(component.SoundPrint, uid,
|
||||
AudioParams.Default
|
||||
.WithVariation(0.25f)
|
||||
.WithVolume(3f)
|
||||
.WithRolloffFactor(2.8f)
|
||||
.WithMaxDistance(4.5f));
|
||||
|
||||
component.PrintReadyAt = _gameTiming.CurTime + component.PrintCooldown;
|
||||
}
|
||||
}
|
||||
@@ -341,6 +341,9 @@ namespace Content.Server.Carrying
|
||||
if (_hands.CountFreeHands(carrier) < carriedComp.FreeHandsRequired)
|
||||
return false;
|
||||
|
||||
if (TryComp<StandingStateComponent>(carrier, out var standing) && !standing.Standing)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Instruments;
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
|
||||
namespace Content.Server.Instruments;
|
||||
|
||||
public sealed partial class InstrumentSystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _action = default!;
|
||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||
|
||||
private void OnMapInit(EntityUid uid, InstrumentComponent component, ref MapInitEvent args)
|
||||
{
|
||||
@@ -17,4 +20,18 @@ public sealed partial class InstrumentSystem
|
||||
_action.RemoveAction(component.ActionUid);
|
||||
component.ActionUid = null;
|
||||
}
|
||||
|
||||
public EntityUid? GetInstrumentListener(EntityUid instrumentUid, SharedInstrumentComponent? component = null)
|
||||
{
|
||||
if (!ResolveInstrument(instrumentUid, ref component))
|
||||
return null;
|
||||
|
||||
if (TryComp<PrivateListeningComponent>(instrumentUid, out var privateListeting) && privateListeting.PrivateListening)
|
||||
{
|
||||
if (_toggle.IsActivated(instrumentUid) && HasComp<ClothingComponent>(instrumentUid))
|
||||
return Transform(instrumentUid).ParentUid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Pain;
|
||||
using Content.Shared.Armor;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Standing;
|
||||
@@ -142,7 +141,7 @@ public sealed partial class SurgerySystem
|
||||
|
||||
_audio.PlayPvs(GibSound, patient);
|
||||
if (!_mobState.IsDead(patient) && !HasComp<PainNumbnessComponent>(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
||||
_chat.TryEmoteWithoutChat(patient, _proto.Index<EmotePrototype>("Scream"), true);
|
||||
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
||||
|
||||
_pain.AdjustPain(patient, "Physical", 250f);
|
||||
if (HasComp<BloodstreamComponent>(patient))
|
||||
@@ -233,7 +232,7 @@ public sealed partial class SurgerySystem
|
||||
|
||||
_audio.PlayPvs(GibSound, entity);
|
||||
if (!_mobState.IsDead(entity) && !HasComp<PainNumbnessComponent>(entity) && !HasComp<SyntheticOperatedComponent>(entity))
|
||||
_chat.TryEmoteWithoutChat(entity, _proto.Index<EmotePrototype>("Scream"), true);
|
||||
_chat.TryEmoteWithoutChat(entity, _proto.Index(Scream), true);
|
||||
|
||||
_transform.SetCoordinates(limbId, Transform(entity).Coordinates);
|
||||
_physics.ApplyLinearImpulse(limbId, _random.NextVector2() * (50f + (float)damage));
|
||||
@@ -273,7 +272,8 @@ public sealed partial class SurgerySystem
|
||||
|
||||
private string? SelectBodyPart(EntityUid patient, InternalDamagePrototype damageProto)
|
||||
{
|
||||
var bodyParts = _body.GetBodyChildren(patient).ToList();
|
||||
var bodyParts = _body.GetBodyChildren(patient)
|
||||
.Where(b => !HasComp<SubdermalImplantComponent>(b.Id)).ToList();
|
||||
|
||||
if (bodyParts.Count == 0)
|
||||
return null;
|
||||
@@ -288,8 +288,11 @@ public sealed partial class SurgerySystem
|
||||
private List<string> FilterByBlacklist(List<(EntityUid Id, BodyPartComponent Component)> bodyParts, List<string> blacklist)
|
||||
{
|
||||
var result = new List<string>();
|
||||
foreach (var (_, component) in bodyParts)
|
||||
foreach (var (uid, component) in bodyParts)
|
||||
{
|
||||
if (HasComp<SubdermalImplantComponent>(uid))
|
||||
continue;
|
||||
|
||||
var partName = GetBodyPartName(component);
|
||||
if (!blacklist.Contains(partName))
|
||||
{
|
||||
|
||||
@@ -144,7 +144,7 @@ public sealed partial class SurgerySystem
|
||||
// Any action without anesthesia will cause pain.
|
||||
if (!HasComp<SleepingComponent>(patient) && !HasComp<PainNumbnessComponent>(patient) && !comp.OperatedPart
|
||||
&& !_mobState.IsDead(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
||||
_chat.TryEmoteWithoutChat(patient, _proto.Index<EmotePrototype>("Scream"), true);
|
||||
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
||||
}
|
||||
|
||||
#region Organic
|
||||
@@ -362,7 +362,7 @@ public sealed partial class SurgerySystem
|
||||
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
||||
HandleFailure(patient, failureEffect, requiredPart);
|
||||
|
||||
var slotId = ParseSlotId(requiredPart.ToLower(), "body_part_slot_");
|
||||
var slotId = ParseSlotId(requiredPart.ToLower(), SharedBodySystem.PartSlotContainerIdPrefix);
|
||||
if (string.IsNullOrEmpty(slotId))
|
||||
return;
|
||||
|
||||
@@ -373,6 +373,8 @@ public sealed partial class SurgerySystem
|
||||
if (!_body.AttachPart(parentPart, slotId, item.Value))
|
||||
return;
|
||||
|
||||
CreateChildSlotsForPart(item.Value, patient);
|
||||
|
||||
if (!HasComp<SterileComponent>(item.Value) && _random.Prob(0.4f))
|
||||
_disease.TryAddDisease(patient, "SurgicalSepsis");
|
||||
|
||||
@@ -602,6 +604,62 @@ public sealed partial class SurgerySystem
|
||||
: fullSlotId;
|
||||
}
|
||||
|
||||
private void CreateChildSlotsForPart(EntityUid attachedPart, Entity<OperatedComponent> patient)
|
||||
{
|
||||
if (!TryComp<BodyPartComponent>(attachedPart, out var partComp))
|
||||
return;
|
||||
|
||||
var defaultSlots = GetDefaultSlotsForPartType(partComp.PartType, partComp.Symmetry);
|
||||
foreach (var slotId in defaultSlots)
|
||||
{
|
||||
var containerId = SharedBodySystem.GetPartSlotContainerId(slotId);
|
||||
if (!_container.TryGetContainer(attachedPart, containerId, out _))
|
||||
{
|
||||
CreateSlotInPart(attachedPart, slotId, GetChildPartTypeForSlot(slotId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSlotInPart(EntityUid part, string slotId, BodyPartType slotType)
|
||||
{
|
||||
if (!TryComp<BodyPartComponent>(part, out var partComp))
|
||||
return;
|
||||
|
||||
_body.TryCreatePartSlot(part, slotId, slotType, out _, partComp);
|
||||
}
|
||||
|
||||
private List<string> GetDefaultSlotsForPartType(BodyPartType partType, BodyPartSymmetry symmetry)
|
||||
{
|
||||
var slots = new List<string>();
|
||||
switch (partType)
|
||||
{
|
||||
case BodyPartType.Arm:
|
||||
slots.Add(symmetry == BodyPartSymmetry.Left ? "left_hand" : "right_hand");
|
||||
break;
|
||||
case BodyPartType.Leg:
|
||||
slots.Add(symmetry == BodyPartSymmetry.Left ? "left_foot" : "right_foot");
|
||||
break;
|
||||
case BodyPartType.Torso:
|
||||
slots.AddRange(new[] { "left_arm", "right_arm", "left_leg", "right_leg", "head" });
|
||||
break;
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
private BodyPartType GetChildPartTypeForSlot(string slotId)
|
||||
{
|
||||
return slotId.ToLower() switch
|
||||
{
|
||||
var s when s.Contains("hand") => BodyPartType.Hand,
|
||||
var s when s.Contains("foot") => BodyPartType.Foot,
|
||||
var s when s.Contains("arm") => BodyPartType.Arm,
|
||||
var s when s.Contains("leg") => BodyPartType.Leg,
|
||||
var s when s.Contains("head") => BodyPartType.Head,
|
||||
_ => BodyPartType.Other
|
||||
};
|
||||
}
|
||||
|
||||
public void ApplyBloodToClothing(EntityUid surgeon, string bloodReagentId, float bloodAmount)
|
||||
{
|
||||
var bloodSolution = new Solution();
|
||||
@@ -635,7 +693,7 @@ public sealed partial class SurgerySystem
|
||||
break;
|
||||
case SurgeryFailedType.Pain:
|
||||
if (!HasComp<SleepingComponent>(patient) && !HasComp<PainNumbnessComponent>(patient) && !patient.Comp.OperatedPart && !_mobState.IsDead(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
||||
_chat.TryEmoteWithoutChat(patient, _proto.Index<EmotePrototype>("Scream"), true);
|
||||
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
||||
|
||||
_jittering.DoJitter(patient, TimeSpan.FromSeconds(5), true);
|
||||
break;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Body.Part;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Examine;
|
||||
@@ -46,6 +47,7 @@ public sealed partial class SurgerySystem : EntitySystem
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
|
||||
private static readonly ProtoId<EmotePrototype> Scream = "Scream";
|
||||
private static readonly ProtoId<DamageTypePrototype> BluntDamage = "Blunt";
|
||||
private static readonly ProtoId<DamageTypePrototype> SlashDamage = "Slash";
|
||||
private static readonly ProtoId<DamageTypePrototype> PiercingDamage = "Piercing";
|
||||
@@ -117,6 +119,16 @@ public sealed partial class SurgerySystem : EntitySystem
|
||||
operated.NextUpdateTick = 5f;
|
||||
}
|
||||
operated.NextUpdateTick -= frameTime;
|
||||
|
||||
if (operated.LimbRegeneration && !_mobState.IsDead(uid))
|
||||
{
|
||||
if (operated.NextRegenerationTick <= 0)
|
||||
{
|
||||
RegenerateMissingLimbs((uid, operated));
|
||||
operated.NextRegenerationTick = operated.RegenerationInterval;
|
||||
}
|
||||
operated.NextRegenerationTick -= frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
var sterileQuery = EntityQueryEnumerator<SterileComponent>();
|
||||
@@ -201,6 +213,169 @@ public sealed partial class SurgerySystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void RegenerateMissingLimbs(Entity<OperatedComponent> entity)
|
||||
{
|
||||
if (!TryComp<BodyComponent>(entity, out var body) || body.Prototype == null)
|
||||
return;
|
||||
|
||||
var rootPart = _body.GetRootPartOrNull(entity, body);
|
||||
if (rootPart == null)
|
||||
return;
|
||||
|
||||
var prototype = _proto.Index(body.Prototype.Value);
|
||||
var missingLimbs = new List<(string slotId, BodyPrototypeSlot slot)>();
|
||||
|
||||
var existingParts = new Dictionary<string, EntityUid>();
|
||||
foreach (var part in _body.GetBodyChildren(entity, body))
|
||||
{
|
||||
var parentAndSlot = _body.GetParentPartAndSlotOrNull(part.Id);
|
||||
if (parentAndSlot != null)
|
||||
{
|
||||
existingParts[parentAndSlot.Value.Slot] = part.Id;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (slotId, slot) in prototype.Slots)
|
||||
{
|
||||
if (slotId == prototype.Root || slot.Part == null)
|
||||
continue;
|
||||
|
||||
if (!existingParts.ContainsKey(slotId))
|
||||
missingLimbs.Add((slotId, slot));
|
||||
}
|
||||
|
||||
if (missingLimbs.Count == 0)
|
||||
return;
|
||||
|
||||
var limbsToRegenerate = missingLimbs
|
||||
.OrderBy(_ => _random.Next())
|
||||
.Take(entity.Comp.MaxLimbsPerCycle)
|
||||
.ToList();
|
||||
|
||||
foreach (var (slotId, slot) in limbsToRegenerate)
|
||||
{
|
||||
RegenerateSingleLimb(entity, slotId, slot, existingParts);
|
||||
}
|
||||
}
|
||||
|
||||
private void RegenerateSingleLimb(Entity<OperatedComponent> entity, string slotId, BodyPrototypeSlot slot, Dictionary<string, EntityUid>? existingParts = null)
|
||||
{
|
||||
if (!TryComp<BodyComponent>(entity, out var body) || body.Prototype == null)
|
||||
return;
|
||||
|
||||
var prototype = _proto.Index(body.Prototype.Value);
|
||||
|
||||
existingParts ??= new Dictionary<string, EntityUid>();
|
||||
foreach (var part in _body.GetBodyChildren(entity, body))
|
||||
{
|
||||
var parentAndSlot = _body.GetParentPartAndSlotOrNull(part.Id);
|
||||
if (parentAndSlot != null)
|
||||
{
|
||||
existingParts[parentAndSlot.Value.Slot] = part.Id;
|
||||
}
|
||||
}
|
||||
|
||||
string? parentSlotId = null;
|
||||
EntityUid? parentPart = null;
|
||||
foreach (var (potentialParentSlotId, potentialParentSlot) in prototype.Slots)
|
||||
{
|
||||
if (potentialParentSlot.Connections?.Contains(slotId) == true)
|
||||
{
|
||||
parentSlotId = potentialParentSlotId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentSlotId != null && existingParts.TryGetValue(parentSlotId, out var parentId))
|
||||
parentPart = parentId;
|
||||
|
||||
if (parentPart == null && parentSlotId != null && prototype.Slots.TryGetValue(parentSlotId, out var parentSlotDef))
|
||||
{
|
||||
if (parentSlotDef.Part != null)
|
||||
{
|
||||
var parentPartEntity = Spawn(parentSlotDef.Part, Transform(entity).Coordinates);
|
||||
var parentPartComp = Comp<BodyPartComponent>(parentPartEntity);
|
||||
|
||||
string? grandParentSlotId = null;
|
||||
EntityUid? grandParentPart = null;
|
||||
foreach (var (potentialGrandParentSlotId, potentialGrandParentSlot) in prototype.Slots)
|
||||
{
|
||||
if (potentialGrandParentSlot.Connections?.Contains(parentSlotId) == true)
|
||||
{
|
||||
grandParentSlotId = potentialGrandParentSlotId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (grandParentSlotId != null && existingParts.TryGetValue(grandParentSlotId, out var grandParentId))
|
||||
{
|
||||
grandParentPart = grandParentId;
|
||||
}
|
||||
|
||||
if (grandParentPart == null)
|
||||
{
|
||||
var rootPart = _body.GetRootPartOrNull(entity, body);
|
||||
if (rootPart != null)
|
||||
{
|
||||
grandParentPart = rootPart.Value.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
if (grandParentPart != null && _body.CanAttachPart(grandParentPart.Value, parentSlotId, parentPartEntity, Comp<BodyPartComponent>(grandParentPart.Value), parentPartComp))
|
||||
{
|
||||
_body.AttachPart(grandParentPart.Value, parentSlotId, parentPartEntity, Comp<BodyPartComponent>(grandParentPart.Value), parentPartComp);
|
||||
parentPart = parentPartEntity;
|
||||
|
||||
existingParts[parentSlotId] = parentPartEntity;
|
||||
|
||||
CreateChildSlotsForPart(parentPartEntity, entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueDel(parentPartEntity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parentPart == null)
|
||||
{
|
||||
var rootPart = _body.GetRootPartOrNull(entity, body);
|
||||
if (rootPart != null)
|
||||
{
|
||||
parentPart = rootPart.Value.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentPart == null || !Exists(parentPart) || Deleted(parentPart.Value))
|
||||
return;
|
||||
|
||||
var newPart = Spawn(slot.Part, Transform(entity).Coordinates);
|
||||
var newPartComp = Comp<BodyPartComponent>(newPart);
|
||||
|
||||
if (_body.CanAttachPart(parentPart.Value, slotId, newPart, Comp<BodyPartComponent>(parentPart.Value), newPartComp))
|
||||
{
|
||||
_body.AttachPart(parentPart.Value, slotId, newPart, Comp<BodyPartComponent>(parentPart.Value), newPartComp);
|
||||
|
||||
if (slot.Organs != null)
|
||||
{
|
||||
foreach (var (organSlot, organPrototype) in slot.Organs)
|
||||
{
|
||||
var newOrgan = Spawn(organPrototype, Transform(entity).Coordinates);
|
||||
_body.InsertOrgan(newOrgan, newPart, organSlot);
|
||||
}
|
||||
}
|
||||
|
||||
CreateChildSlotsForPart(newPart, entity);
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("surgery-limb-regenerated"), entity, entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueDel(newPart);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIsEquipping(Entity<OperatedComponent> ent, ref IsEquippingAttemptEvent args)
|
||||
{
|
||||
if ((args.SlotFlags == SlotFlags.FEET || args.SlotFlags == SlotFlags.SOCKS) &&
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Content.Shared.Crayon;
|
||||
/// Component holding the state of a crayon-like component
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SharedCrayonSystem))]
|
||||
// [Access(typeof(SharedCrayonSystem))] // Corvax-Wega-Edit
|
||||
public sealed partial class CrayonComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -23,6 +23,9 @@ public sealed partial class CrayonComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public Color Color;
|
||||
|
||||
[DataField, AutoNetworkedField] // Corvax-Wega-Add
|
||||
public Angle Angle; // Corvax-Wega-Add
|
||||
|
||||
/// <summary>
|
||||
/// Play a sound when drawing if specified.
|
||||
/// </summary>
|
||||
@@ -117,3 +120,16 @@ public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
// Corvax-Wega-Add-start
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CrayonRotateEvent : EntityEventArgs
|
||||
{
|
||||
public readonly Angle Angle;
|
||||
|
||||
public CrayonRotateEvent(Angle angle)
|
||||
{
|
||||
Angle = angle;
|
||||
}
|
||||
}
|
||||
// Corvax-Wega-Add-end
|
||||
|
||||
@@ -1,4 +1,49 @@
|
||||
namespace Content.Shared.Crayon;
|
||||
// Corvax-Wega-Full-Edit-start
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
|
||||
[Virtual]
|
||||
public abstract class SharedCrayonSystem : EntitySystem { }
|
||||
namespace Content.Shared.Crayon;
|
||||
|
||||
public abstract class SharedCrayonSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
|
||||
private readonly Angle _rotationIncrement = Angle.FromDegrees(2.5);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.MouseWheelUp, new PointerInputCmdHandler(MouseWheelUp))
|
||||
.Bind(ContentKeyFunctions.MouseWheelDown, new PointerInputCmdHandler(MouseWheelDown))
|
||||
.Register<SharedCrayonSystem>();
|
||||
}
|
||||
|
||||
private bool MouseWheelUp(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||
{
|
||||
return HandleMouseWheel(args, _rotationIncrement);
|
||||
}
|
||||
|
||||
private bool MouseWheelDown(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||
{
|
||||
return HandleMouseWheel(args, -_rotationIncrement);
|
||||
}
|
||||
|
||||
private bool HandleMouseWheel(in PointerInputCmdHandler.PointerInputCmdArgs args, Angle rotation)
|
||||
{
|
||||
if (args.Session?.AttachedEntity is not { } player)
|
||||
return false;
|
||||
|
||||
var active = _hands.GetActiveItem(player);
|
||||
if (!HasComp<CrayonComponent>(active))
|
||||
return false;
|
||||
|
||||
var rotateEvent = new CrayonRotateEvent(rotation);
|
||||
RaiseNetworkEvent(rotateEvent);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Corvax-Wega-Full-Edit-end
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction MovePulledObject = "MovePulledObject";
|
||||
public static readonly BoundKeyFunction ReleasePulledObject = "ReleasePulledObject";
|
||||
public static readonly BoundKeyFunction MouseMiddle = "MouseMiddle";
|
||||
public static readonly BoundKeyFunction MouseWheelUp = "MouseWheelUp"; // Corvax-Wega-Add
|
||||
public static readonly BoundKeyFunction MouseWheelDown = "MouseWheelDown"; // Corvax-Wega-Add
|
||||
public static readonly BoundKeyFunction RotateObjectClockwise = "RotateObjectClockwise";
|
||||
public static readonly BoundKeyFunction RotateObjectCounterclockwise = "RotateObjectCounterclockwise";
|
||||
public static readonly BoundKeyFunction FlipObject = "FlipObject";
|
||||
|
||||
@@ -23,7 +23,7 @@ public sealed class MovementModStatusSystem : EntitySystem
|
||||
public static readonly EntProtoId VomitingSlowdown = "VomitingSlowdownStatusEffect";
|
||||
public static readonly EntProtoId TaserSlowdown = "TaserSlowdownStatusEffect";
|
||||
public static readonly EntProtoId FlashSlowdown = "FlashSlowdownStatusEffect";
|
||||
public static readonly EntProtoId Slowdown = "StatusEffectSlowdown"; // Corvax-Wega-Add Суки ебаные
|
||||
public static readonly EntProtoId Slowdown = "BasicSlowdownStatusEffect"; // Corvax-Wega-Add Суки ебаные
|
||||
public static readonly EntProtoId StatusEffectFriction = "StatusEffectFriction";
|
||||
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Body.Components; // Corvax-Wega-Surgery
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Carrying; // Corvax-Wega
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
@@ -258,6 +259,9 @@ public abstract partial class SharedStunSystem
|
||||
if (!Resolve(entity, ref entity.Comp1, false) || !_cfgManager.GetCVar(CCVars.MovementCrawling))
|
||||
return;
|
||||
|
||||
if (HasComp<CarryingComponent>(entity.Owner)) // Corvax-Wega
|
||||
return; // Corvax-Wega
|
||||
|
||||
if (!Resolve(entity, ref entity.Comp2, false))
|
||||
{
|
||||
TryKnockdown(entity.Owner, entity.Comp1.DefaultKnockedDuration, true, false, false);
|
||||
|
||||
72
Content.Shared/_Wega/Botany/PlantAnalyzerComponent.cs
Normal file
72
Content.Shared/_Wega/Botany/PlantAnalyzerComponent.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Botany.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class PlantAnalyzerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// When will the analyzer be ready to print again?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan PrintReadyAt = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// How often can the analyzer print?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan PrintCooldown = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// The sound that's played when the analyzer prints off a report.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier SoundPrint = new SoundPathSpecifier("/Audio/Machines/short_print_and_rip.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// What the machine will print.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntProtoId<PaperComponent> MachineOutput = "PlantAnalyzerReportPaper";
|
||||
|
||||
/// <summary>
|
||||
/// Delay for scanning
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float ScanDelay = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Sound played when scanning starts
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier? ScanningBeginSound;
|
||||
|
||||
/// <summary>
|
||||
/// Sound played when scanning ends
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier? ScanningEndSound;
|
||||
|
||||
[DataField]
|
||||
public bool Silent;
|
||||
|
||||
[ViewVariables]
|
||||
public EntityUid? ScannedEntity;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(2.5);
|
||||
|
||||
[DataField]
|
||||
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public float MaxScanRange = 2f;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class PlantAnalyzerDoAfterEvent : SimpleDoAfterEvent;
|
||||
114
Content.Shared/_Wega/Botany/PlantAnalyzerMessages.cs
Normal file
114
Content.Shared/_Wega/Botany/PlantAnalyzerMessages.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Botany.PlantAnalyzer;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum PlantAnalyzerUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlantAnalyzerScannedUserMessage(NetEntity? targetEntity, bool? scanMode, PlantAnalyzerPlantData? plantData, PlantAnalyzerTrayData? trayData, PlantAnalyzerTolerancesData? tolerancesData, PlantAnalyzerProduceData? produceData, TimeSpan? printReadyAt) : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly NetEntity? TargetEntity = targetEntity;
|
||||
public bool? ScanMode = scanMode;
|
||||
public PlantAnalyzerPlantData? PlantData = plantData;
|
||||
public PlantAnalyzerTrayData? TrayData = trayData;
|
||||
public PlantAnalyzerTolerancesData? TolerancesData = tolerancesData;
|
||||
public PlantAnalyzerProduceData? ProduceData = produceData;
|
||||
public readonly TimeSpan? PrintReadyAt = printReadyAt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Everything that is kept independent of a given plant/seed.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlantAnalyzerTrayData(float waterLevel, float nutritionLevel, float toxins, float pestLevel, float weedLevel, List<string>? chemicals)
|
||||
{
|
||||
public float WaterLevel = waterLevel;
|
||||
public float NutritionLevel = nutritionLevel;
|
||||
public float Toxins = toxins;
|
||||
public float PestLevel = pestLevel;
|
||||
public float WeedLevel = weedLevel;
|
||||
public List<string>? Chemicals = chemicals;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All the information to keep the plant alive.
|
||||
/// Which is most of the "Tolerances" region plus the gases it may need.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlantAnalyzerTolerancesData(float waterConsumption, float nutrientConsumption, float toxinsTolerance, float pestTolerance, float weedTolerance, float lowPressureTolerance, float highPressureTolerance, float idealHeat, float heatTolerance, float idealLight, float lightTolerance, List<Gas> consumeGasses)
|
||||
{
|
||||
public float WaterConsumption = waterConsumption;
|
||||
public float NutrientConsumption = nutrientConsumption;
|
||||
public float ToxinsTolerance = toxinsTolerance;
|
||||
public float PestTolerance = pestTolerance;
|
||||
public float WeedTolerance = weedTolerance;
|
||||
public float IdealPressure = (lowPressureTolerance + highPressureTolerance) / 2;
|
||||
public float PressureTolerance = (lowPressureTolerance + highPressureTolerance) / 2 - lowPressureTolerance;
|
||||
public float IdealHeat = idealHeat;
|
||||
public float HeatTolerance = heatTolerance;
|
||||
public float IdealLight = idealLight;
|
||||
public float LightTolerance = lightTolerance;
|
||||
public List<Gas> ConsumeGasses = consumeGasses;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about the plant inside the tray.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlantAnalyzerPlantData(string seedDisplayName, float health, float endurance, float age, float lifespan, float maturation, bool dead, bool viable, bool mutating, bool kudzu)
|
||||
{
|
||||
public string SeedDisplayName = seedDisplayName;
|
||||
public float Health = health;
|
||||
public float Endurance = endurance;
|
||||
public float Age = age;
|
||||
public float Lifespan = lifespan;
|
||||
public float Maturation = maturation;
|
||||
public bool Dead = dead;
|
||||
public bool Viable = viable;
|
||||
public bool Mutating = mutating;
|
||||
public bool Kudzu = kudzu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about the output of a plant (produce and gas).
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlantAnalyzerProduceData(int yield, float potency, List<string> chemicals, List<EntProtoId> produce, List<Gas> exudeGasses, bool seedless)
|
||||
{
|
||||
public int Yield = yield;
|
||||
public string Potency = ObscurePotency(potency);
|
||||
public List<string> Chemicals = chemicals;
|
||||
public List<EntProtoId> Produce = produce;
|
||||
public List<Gas> ExudeGasses = exudeGasses;
|
||||
public bool Seedless = seedless;
|
||||
|
||||
private static string ObscurePotency(float potency)
|
||||
{
|
||||
var potencyFtl = "plant-analyzer-potency-" + potency switch
|
||||
{
|
||||
<= 5 => "tiny",
|
||||
> 5 and < 10 => "small",
|
||||
>= 10 and < 15 => "below-average",
|
||||
>= 15 and < 20 => "average",
|
||||
>= 20 and <= 25 => "above-average",
|
||||
>= 20 and < 30 => "large",
|
||||
>= 30 and < 40 => "huge",
|
||||
>= 40 and < 50 => "gigantic",
|
||||
>= 50 and < 60 => "ludicrous",
|
||||
_ => "immeasurable"
|
||||
};
|
||||
|
||||
return potencyFtl;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlantAnalyzerPrintMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
}
|
||||
75
Content.Shared/_Wega/Botany/SharedPlantAnalyzerSystem.cs
Normal file
75
Content.Shared/_Wega/Botany/SharedPlantAnalyzerSystem.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Prototypes;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Localizations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Botany.Systems;
|
||||
|
||||
public abstract partial class SharedPlantAnalyzerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
#region Loc
|
||||
|
||||
public string GasesToLocalizedStrings(List<Gas> gases)
|
||||
{
|
||||
if (gases.Count == 0)
|
||||
return "";
|
||||
|
||||
List<int> gasIds = [];
|
||||
foreach (var gas in gases)
|
||||
gasIds.Add((int)gas);
|
||||
|
||||
List<string> gasesLoc = [];
|
||||
foreach (var gas in _prototypeManager.EnumeratePrototypes<GasPrototype>())
|
||||
if (gasIds.Contains(int.Parse(gas.ID)))
|
||||
gasesLoc.Add(Loc.GetString(gas.Name));
|
||||
|
||||
return ContentLocalizationManager.FormatList(gasesLoc);
|
||||
}
|
||||
|
||||
public string ChemicalsToLocalizedStrings(List<string> ids)
|
||||
{
|
||||
if (ids.Count == 0)
|
||||
return "";
|
||||
|
||||
List<string> locStrings = [];
|
||||
foreach (var id in ids)
|
||||
locStrings.Add(_prototypeManager.TryIndex<ReagentPrototype>(id, out var prototype) ? prototype.LocalizedName : id);
|
||||
|
||||
return ContentLocalizationManager.FormatList(locStrings);
|
||||
}
|
||||
|
||||
public (string Singular, string Plural, string First) ProduceToLocalizedStrings(List<EntProtoId> ids)
|
||||
{
|
||||
if (ids.Count == 0)
|
||||
return ("", "", "");
|
||||
|
||||
List<string> singularStrings = [];
|
||||
List<string> pluralStrings = [];
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var singular = _prototypeManager.TryIndex(id, out var prototype) ? prototype.Name : id.Id;
|
||||
var plural = singular switch
|
||||
{
|
||||
string s when s.EndsWith("о") => s + "в",
|
||||
string s when s.EndsWith("е") => s + "й",
|
||||
string s when s.EndsWith("ь") => s.Substring(0, s.Length - 1) + "ей",
|
||||
string s when s.EndsWith("й") => s.Substring(0, s.Length - 1) + "ев",
|
||||
_ => singular
|
||||
};
|
||||
|
||||
singularStrings.Add(singular);
|
||||
pluralStrings.Add(plural);
|
||||
}
|
||||
|
||||
return (
|
||||
ContentLocalizationManager.FormatListToOr(singularStrings),
|
||||
ContentLocalizationManager.FormatListToOr(pluralStrings),
|
||||
singularStrings[0]
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Instruments;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class PrivateListeningComponent : Component
|
||||
{
|
||||
[DataField("privateListening"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool PrivateListening { get; set; } = true;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Content.Shared._Wega.Resomi.Abilities.Hearing;
|
||||
public sealed partial class ListenUpComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float radius = 8f;
|
||||
public float Radius = 8f;
|
||||
|
||||
[DataField]
|
||||
public SpriteSpecifier Sprite = new SpriteSpecifier.Rsi(new("/Textures/_Wega/Mobs/Species/Resomi/Abilities/noise_effect.rsi"), "noise");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using Content.Shared._Wega.Resomi.Abilities.Hearing;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Physics;
|
||||
@@ -39,9 +40,12 @@ public sealed class SoundInsulationSystem : EntitySystem
|
||||
var direction = listenerPos - sourcePos;
|
||||
var distance = direction.Length();
|
||||
|
||||
if (distance <= 0.1f)
|
||||
if (distance <= 0.5f)
|
||||
return 0f;
|
||||
|
||||
if (TryComp<ListenUpComponent>(listener, out var listen) && distance <= listen.Radius)
|
||||
return 0f; // Chickens have good hearing.
|
||||
|
||||
var normalizedDir = direction.Normalized();
|
||||
var ray = new CollisionRay(sourcePos, normalizedDir, (int)(CollisionGroup.WallLayer | CollisionGroup.AirlockLayer));
|
||||
var rayCastResults = _physics.IntersectRay(sourceXform.MapID, ray, distance, source, false);
|
||||
|
||||
@@ -72,6 +72,30 @@ public sealed partial class OperatedComponent : Component
|
||||
[ViewVariables, DataField]
|
||||
public float LimbLossChance = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Is gradual limb regeneration included.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool LimbRegeneration = false;
|
||||
|
||||
/// <summary>
|
||||
/// Regeneration interval in seconds.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float RegenerationInterval = 90f;
|
||||
|
||||
/// <summary>
|
||||
/// Timer until the next regeneration.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float NextRegenerationTick = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of limbs that can be regenerated in one cycle.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public int MaxLimbsPerCycle = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The level of sterility. It may affect the chance of success.
|
||||
/// </summary>
|
||||
|
||||
145
Resources/Locale/ru-RU/_wega/botany/plantanalyzer.ftl
Normal file
145
Resources/Locale/ru-RU/_wega/botany/plantanalyzer.ftl
Normal file
@@ -0,0 +1,145 @@
|
||||
# Основные элементы интерфейса
|
||||
plant-analyzer-component-no-seed = растение не найдено
|
||||
plant-analyzer-component-health = Здоровье:
|
||||
plant-analyzer-component-age = Возраст:
|
||||
plant-analyzer-component-maturation = Созревание:
|
||||
plant-analyzer-component-water = Вода:
|
||||
plant-analyzer-component-nutrition = Питание:
|
||||
plant-analyzer-component-toxins = Токсины:
|
||||
plant-analyzer-component-pests = Вредители:
|
||||
plant-analyzer-component-weeds = Сорняки:
|
||||
|
||||
# Статусы растений
|
||||
plant-analyzer-component-alive = ЖИВОЙ
|
||||
plant-analyzer-component-dead = МЁРТВЫЙ
|
||||
plant-analyzer-component-unviable = НЕЖИЗНЕСПОСОБНЫЙ
|
||||
plant-analyzer-component-mutating = МУТИРУЕТ
|
||||
plant-analyzer-component-kudzu = КУДЗУ
|
||||
|
||||
# Химикаты в почве
|
||||
plant-analyzer-soil = В этом {$holder} есть [color=white]{$chemicals}[/color], которые {$count ->
|
||||
[one]не был
|
||||
*[other]не были
|
||||
} поглощены.
|
||||
plant-analyzer-soil-empty = В этом {$holder} нет непоглощённых химикатов.
|
||||
|
||||
# Идеальные условия
|
||||
plant-analyzer-component-environemt = Этому [color=green]{$seedName}[/color] требуется атмосфера с давлением [color=lightblue]{$kpa} кПа ± {$kpaTolerance} кПа[/color], температурой [color=lightsalmon]{$temp} K ± {$tempTolerance} K[/color] и уровнем освещения [color=white]{$lightLevel} ± {$lightTolerance}[/color].
|
||||
plant-analyzer-component-environemt-void = Этот [color=green]{$seedName}[/color] должен расти [bolditalic]в вакууме космоса[/bolditalic] при уровне освещения [color=white]{$lightLevel} ± {$lightTolerance}[/color].
|
||||
plant-analyzer-component-environemt-gas = Этому [color=green]{$seedName}[/color] требуется атмосфера, содержащая [bold]{$gases}[/bold], с давлением [color=lightblue]{$kpa} кПа ± {$kpaTolerance} кПа[/color], температурой [color=lightsalmon]{$temp} K ± {$tempTolerance} K[/color] и уровнем освещения [color=white]{$lightLevel} ± {$lightTolerance}[/color].
|
||||
|
||||
# Выход растения
|
||||
plant-analyzer-output = {$yield ->
|
||||
[0]{$gasCount ->
|
||||
[0]Кажется, он только потребляет воду и питательные вещества.
|
||||
*[other]Кажется, он только превращает воду и питательные вещества в [bold]{$gases}[/bold].
|
||||
}
|
||||
*[other]У него [color=lightgreen]{$yield} {$potency}[/color]{$seedless ->
|
||||
[true]{" "}но [color=red]бессемянных[/color]
|
||||
*[false]{$nothing}
|
||||
}{" "}{$yield ->
|
||||
[one]цветок
|
||||
[few]цветка
|
||||
[many]цветков
|
||||
*[other]цветков
|
||||
}{" "}котор{$yield ->
|
||||
[one]ый
|
||||
*[other]ые
|
||||
}{ $gasCount ->
|
||||
[0]{$nothing}
|
||||
*[other]{$yield ->
|
||||
[one]{" "}выделяет
|
||||
*[other]{" "}выделяют
|
||||
}{" "}[bold]{$gases}[/bold] и
|
||||
}{" "}превратит{$yield ->
|
||||
[one]ся в {INDEFINITE($firstProduce)} [color=#a4885c]{$produce}[/color]
|
||||
*[other]ся в [color=#a4885c]{$producePlural}[/color]
|
||||
}.{$chemCount ->
|
||||
[0]{$nothing}
|
||||
*[other]{" "}В его стебле обнаружены следовые количества [color=white]{$chemicals}[/color].
|
||||
}
|
||||
}
|
||||
|
||||
# Уровни мощности
|
||||
plant-analyzer-potency-tiny = крошечный
|
||||
plant-analyzer-potency-small = маленький
|
||||
plant-analyzer-potency-below-average = ниже среднего
|
||||
plant-analyzer-potency-average = средний
|
||||
plant-analyzer-potency-above-average = выше среднего
|
||||
plant-analyzer-potency-large = довольно большой
|
||||
plant-analyzer-potency-huge = огромный
|
||||
plant-analyzer-potency-gigantic = гигантский
|
||||
plant-analyzer-potency-ludicrous = невероятно большой
|
||||
plant-analyzer-potency-immeasurable = неизмеримо большой
|
||||
|
||||
# Печать и интерфейс
|
||||
plant-analyzer-print = Печать
|
||||
plant-analyzer-printout-missing = Н/Д
|
||||
plant-analyzer-printout =
|
||||
{"[color=#9FED58][head=2]Отчёт анализатора растений[/head][/color]"}
|
||||
──────────────────────────────
|
||||
{"[bullet/]"} Вид: {$seedName}
|
||||
{" "}[bullet/] Жизнеспособен: {$viable ->
|
||||
[no][color=red]Нет[/color]
|
||||
[yes][color=green]Да[/color]
|
||||
*[other]{LOC("plant-analyzer-printout-missing")}
|
||||
}
|
||||
{" "}[bullet/] Выносливость: {$endurance}
|
||||
{" "}[bullet/] Продолжительность жизни: {$lifespan}
|
||||
{" "}[bullet/] Продукт: [color=#a4885c]{$produce}[/color]
|
||||
{" "}[bullet/] Кудзу: {$kudzu ->
|
||||
[no][color=green]Нет[/color]
|
||||
[yes][color=red]Да[/color]
|
||||
*[other]{LOC("plant-analyzer-printout-missing")}
|
||||
}
|
||||
{"[bullet/]"} Профиль роста:
|
||||
{" "}[bullet/] Вода: [color=cyan]{$water}[/color]
|
||||
{" "}[bullet/] Питание: [color=orange]{$nutrients}[/color]
|
||||
{" "}[bullet/] Токсины: [color=yellowgreen]{$toxins}[/color]
|
||||
{" "}[bullet/] Вредители: [color=magenta]{$pests}[/color]
|
||||
{" "}[bullet/] Сорняки: [color=red]{$weeds}[/color]
|
||||
{"[bullet/]"} Профиль среды:
|
||||
{" "}[bullet/] Состав: [bold]{$gasesIn}[/bold]
|
||||
{" "}[bullet/] Давление: [color=lightblue]{$kpa} кПа ± {$kpaTolerance} кПа[/color]
|
||||
{" "}[bullet/] Температура: [color=lightsalmon]{$temp} K ± {$tempTolerance} K[/color]
|
||||
{" "}[bullet/] Освещение: [color=gray][bold]{$lightLevel} ± {$lightTolerance}[/bold][/color]
|
||||
{"[bullet/]"} Цветы: {$yield ->
|
||||
[-1]{LOC("plant-analyzer-printout-missing")}
|
||||
[0][color=red]0[/color]
|
||||
*[other][color=lightgreen]{$yield} {$potency}[/color]
|
||||
}
|
||||
{"[bullet/]"} Семена: {$seeds ->
|
||||
[no][color=red]Нет[/color]
|
||||
[yes][color=green]Да[/color]
|
||||
*[other]{LOC("plant-analyzer-printout-missing")}
|
||||
}
|
||||
{"[bullet/]"} Химикаты: [color=gray][bold]{$chemicals}[/bold][/color]
|
||||
{"[bullet/]"} Выделения: [bold]{$gasesOut}[/bold]
|
||||
|
||||
# Новые элементы улучшенного интерфейса
|
||||
plant-analyzer-scan-active = СКАНИРОВАНИЕ
|
||||
plant-analyzer-scan-inactive = НЕАКТИВНО
|
||||
plant-analyzer-no-data = НЕТ ДАННЫХ
|
||||
plant-analyzer-no-plant = Растение не обнаружено
|
||||
plant-analyzer-unknown-container = Неизвестный контейнер
|
||||
plant-analyzer-tray-conditions = Состояние лотка
|
||||
plant-analyzer-soil-chemicals = Химикаты в почве
|
||||
plant-analyzer-ideal-environment = Идеальные условия
|
||||
plant-analyzer-plant-output = Выход растения
|
||||
plant-analyzer-print-report = Печать отчёта
|
||||
plant-analyzer-temperature = Температура
|
||||
plant-analyzer-pressure = Давление
|
||||
plant-analyzer-light = Освещение
|
||||
plant-analyzer-required-gases = Требуемые газы
|
||||
plant-analyzer-yield = Урожай
|
||||
plant-analyzer-potency = Мощность
|
||||
plant-analyzer-produces = Производит
|
||||
plant-analyzer-chemicals = Химикаты
|
||||
plant-analyzer-exudes = Выделяет газы
|
||||
plant-analyzer-seeds = Семена
|
||||
plant-analyzer-seedless = Бессемянное
|
||||
plant-analyzer-produces-seeds = Производит семена
|
||||
|
||||
# Всплывающие сообщения
|
||||
plant-analyzer-popup-scan-target = Производится сканирование {$target}
|
||||
plant-analyzer-printer-not-ready = Принтер ещё не готов
|
||||
@@ -88,6 +88,7 @@ surgery-welder-failed = Не получается включить инстру
|
||||
surgery-limb-torn-off = {$limb} отрезало
|
||||
surgery-decapitated = ГОЛОВА СПАЛА С ПЛЕЧ
|
||||
surgery-explosion-limb-torn-off = {$limb} ОТОРВАЛО
|
||||
surgery-limb-regenerated = Ваша конечность снова отросла
|
||||
|
||||
surgery-organ-removed = Орган успешно извлечен
|
||||
surgery-organ-inserted = Орган успешно присоединен
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
ent-PlantAnalyzerUnpowered = анализатор растений
|
||||
.desc = Сканер, используемый для оценки различных зон произрастания растения, генетических особенностей и химических веществ.
|
||||
ent-PlantAnalyzer = { ent-PlantAnalyzerUnpowered }
|
||||
.desc = { ent-PlantAnalyzerUnpowered.desc }
|
||||
.suffix = Заряженный
|
||||
ent-PlantAnalyzerEmpty = { ent-PlantAnalyzerUnpowered }
|
||||
.desc = { ent-PlantAnalyzerUnpowered.desc }
|
||||
.suffix = Пустой
|
||||
ent-PlantAnalyzerReportPaper = отчет об анализе растения
|
||||
.desc = Распечатка с анализатора растений.
|
||||
@@ -0,0 +1,2 @@
|
||||
ent-BasicSlowdownStatusEffect = простое замедление
|
||||
.desc = { ent-StatusEffectSlowdown.desc }
|
||||
@@ -17,6 +17,7 @@
|
||||
Bucket: 3
|
||||
BoxMouthSwab: 1
|
||||
BoxAgrichem: 1
|
||||
PlantAnalyzer: 4 # Corvax-Wega-Add
|
||||
#TO DO:
|
||||
#plant analyzer
|
||||
contrabandInventory:
|
||||
|
||||
@@ -9,22 +9,22 @@
|
||||
species: CorvaxVulpkanin
|
||||
- type: Carriable # Corvax-Wega-Carrying
|
||||
- type: DiseaseCarrier # Corvax-Wega-Disease
|
||||
- type: Hunger # on 1.5x more
|
||||
thresholds:
|
||||
Overfed: 250
|
||||
Okay: 200
|
||||
Peckish: 150
|
||||
Starving: 100
|
||||
Dead: 0
|
||||
baseDecayRate: 0.02
|
||||
- type: Thirst # on 1.5x more
|
||||
thresholds:
|
||||
OverHydrated: 650
|
||||
Okay: 500
|
||||
Thirsty: 350
|
||||
Parched: 200
|
||||
Dead: 0
|
||||
baseDecayRate: 0.15
|
||||
- type: Hunger # Corvax-Wega-Edit
|
||||
# thresholds:
|
||||
# Overfed: 250
|
||||
# Okay: 200
|
||||
# Peckish: 150
|
||||
# Starving: 100
|
||||
# Dead: 0
|
||||
# baseDecayRate: 0.02
|
||||
- type: Thirst # Corvax-Wega-Edit
|
||||
# thresholds:
|
||||
# OverHydrated: 650
|
||||
# Okay: 500
|
||||
# Thirsty: 350
|
||||
# Parched: 200
|
||||
# Dead: 0
|
||||
# baseDecayRate: 0.15
|
||||
- type: Icon
|
||||
sprite: Corvax/Mobs/Species/Vulpkanin/parts.rsi
|
||||
state: full
|
||||
@@ -74,7 +74,6 @@
|
||||
sprite: Corvax/Mobs/Species/displacement.rsi
|
||||
state: socks
|
||||
# Corvax-Wega-end
|
||||
|
||||
- type: ContentEye
|
||||
targetZoom: "1.0, 1.0"
|
||||
maxZoom: "1.0, 1.0"
|
||||
|
||||
@@ -38,3 +38,20 @@
|
||||
path: /Audio/Items/flashlight_on.ogg
|
||||
soundDeactivate:
|
||||
path: /Audio/Items/flashlight_off.ogg
|
||||
# Corvax-Wega-Add-start
|
||||
- type: ActivatableUI
|
||||
singleUser: true
|
||||
verbText: verb-instrument-openui
|
||||
key: enum.InstrumentUiKey.Key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.InstrumentUiKey.Key:
|
||||
type: InstrumentBoundUserInterface
|
||||
- type: Instrument
|
||||
program: 0
|
||||
bank: 0
|
||||
allowPercussion: true
|
||||
allowProgramChange: true
|
||||
respectMidiLimits: true
|
||||
- type: PrivateListening
|
||||
# Corvax-Wega-Add-end
|
||||
|
||||
@@ -96,9 +96,9 @@
|
||||
# Corvax-Wega-end
|
||||
- map: [ "gloves" ]
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "id" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ] # Corvax-Wega-Edit
|
||||
- map: [ "id" ] # Corvax-Wega-Edit
|
||||
- map: [ "enum.HumanoidVisualLayers.Tail" ] # Mentioned in moth code: This needs renaming lol.
|
||||
- map: [ "back" ]
|
||||
- map: [ "neck" ]
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "ears" ]
|
||||
- map: [ "eyes" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "id" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ] # Corvax-Wega-Edit
|
||||
- map: [ "id" ] # Corvax-Wega-Edit
|
||||
- map: [ "back" ]
|
||||
- map: [ "neck" ]
|
||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||
|
||||
@@ -107,9 +107,9 @@
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "ears" ]
|
||||
- map: [ "eyes" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "id" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ] # Corvax-Wega-Edit
|
||||
- map: [ "id" ] # Corvax-Wega-Edit
|
||||
- map: [ "enum.HumanoidVisualLayers.Tail" ] #in the utopian future we should probably have a wings enum inserted here so everyhting doesn't break
|
||||
- map: [ "back" ]
|
||||
- map: [ "neck" ]
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
scaling: true
|
||||
damage:
|
||||
types:
|
||||
Heat: 0.05
|
||||
Heat: 0.5 # Corvax-Wega-Edit
|
||||
- !type:PopupMessage
|
||||
type: Local
|
||||
visualType: Large
|
||||
@@ -121,6 +121,7 @@
|
||||
state: jumpsuit-female
|
||||
- type: Operated # Corvax-Wega-Surgery
|
||||
raceGraph: SlimeSurgery # Corvax-Wega-Surgery
|
||||
limbRegeneration: true # Corvax-Wega-Surgery
|
||||
# Corvax-Wega-Disease-start
|
||||
- type: DiseaseCarrier
|
||||
naturalImmunities:
|
||||
|
||||
@@ -126,9 +126,9 @@
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "ears" ]
|
||||
- map: [ "eyes" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "id" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ] # Corvax-Wega-Edit
|
||||
- map: [ "id" ] # Corvax-Wega-Edit
|
||||
- map: [ "back" ]
|
||||
- map: [ "neck" ]
|
||||
- map: [ "suitstorage" ] # This is not in the default order
|
||||
|
||||
@@ -81,13 +81,13 @@
|
||||
sprite: Objects/Misc/handcuffs.rsi
|
||||
state: body-overlay-2
|
||||
visible: false
|
||||
- map: [ "id" ]
|
||||
- map: [ "gloves" ]
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "ears" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ] # Corvax-Wega-Edit
|
||||
- map: [ "id" ] # Corvax-Wega-Edit
|
||||
- map: [ "eyes" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "neck" ]
|
||||
- map: [ "back" ]
|
||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
- PowerCellsStatic
|
||||
- ElectronicsStatic
|
||||
- ScienceStatic # Corvax-Wega
|
||||
- HydroponicsStatic # Corvax-Wega
|
||||
- type: EmagLatheRecipes
|
||||
emagStaticPacks:
|
||||
- SecurityAmmoStatic
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- Wall
|
||||
components:
|
||||
- type: Anchorable
|
||||
flags:
|
||||
flags:
|
||||
- Anchorable
|
||||
- type: Rotatable
|
||||
- type: RangedDamageSound
|
||||
@@ -1536,7 +1536,7 @@
|
||||
name: invisible wall
|
||||
components:
|
||||
- type: TimedDespawn
|
||||
lifetime: 15
|
||||
lifetime: 30 # Corvax-Wega-Edit
|
||||
- type: Tag
|
||||
tags:
|
||||
- Wall
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "ears" ]
|
||||
- map: [ "eyes" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "id" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "back" ]
|
||||
- map: [ "neck" ]
|
||||
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
- map: [ "shoes" ]
|
||||
- map: [ "ears" ]
|
||||
- map: [ "eyes" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "belt" ]
|
||||
- map: [ "id" ]
|
||||
- map: [ "outerClothing" ]
|
||||
- map: [ "back" ]
|
||||
- map: [ "neck" ]
|
||||
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
||||
@@ -95,7 +95,7 @@
|
||||
Bloodloss: -2
|
||||
bleedReductionAmount: 0.2
|
||||
- type: Temperature
|
||||
heatDamageThreshold: 326 # ~ 52 °C
|
||||
heatDamageThreshold: 326 # ~ 52 °C
|
||||
coldDamageThreshold: 280 # ~ 7 °C
|
||||
currentTemperature: 310.15
|
||||
specificHeat: 46
|
||||
@@ -125,22 +125,6 @@
|
||||
- type: NaturalNightVision
|
||||
tintColor: "#B0E0FF"
|
||||
- type: Inventory
|
||||
femaleDisplacements:
|
||||
jumpsuit:
|
||||
sizeMaps:
|
||||
32:
|
||||
sprite: _Wega/Mobs/Species/Ariral/displacement.rsi
|
||||
state: jumpsuit-female
|
||||
shoes:
|
||||
sizeMaps:
|
||||
32:
|
||||
sprite: _Wega/Mobs/Species/Ariral/displacement.rsi
|
||||
state: shoes
|
||||
socks:
|
||||
sizeMaps:
|
||||
32:
|
||||
sprite: _Wega/Mobs/Species/Ariral/displacement.rsi
|
||||
state: socks
|
||||
displacements:
|
||||
jumpsuit:
|
||||
sizeMaps:
|
||||
@@ -230,22 +214,6 @@
|
||||
prototype: Ariral
|
||||
requiredLegs: 2
|
||||
- type: Inventory
|
||||
femaleDisplacements:
|
||||
jumpsuit:
|
||||
sizeMaps:
|
||||
32:
|
||||
sprite: _Wega/Mobs/Species/Ariral/displacement.rsi
|
||||
state: jumpsuit-female
|
||||
shoes:
|
||||
sizeMaps:
|
||||
32:
|
||||
sprite: _Wega/Mobs/Species/Ariral/displacement.rsi
|
||||
state: shoes
|
||||
socks:
|
||||
sizeMaps:
|
||||
32:
|
||||
sprite: _Wega/Mobs/Species/Ariral/displacement.rsi
|
||||
state: socks
|
||||
displacements:
|
||||
jumpsuit:
|
||||
sizeMaps:
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
id: PlantAnalyzerUnpowered
|
||||
name: plant analyzer
|
||||
description: A scanner used to evaluate a plant's various areas of growth, genetic traits and chemicals.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: _Wega/Objects/Specific/Hydroponics/plant_analyzer.rsi
|
||||
layers:
|
||||
- state: icon
|
||||
- state: analyzer
|
||||
shader: unshaded
|
||||
visible: true
|
||||
map: [ "enum.PowerDeviceVisualLayers.Powered" ]
|
||||
- type: Item
|
||||
storedRotation: -90
|
||||
- type: Tag
|
||||
tags:
|
||||
- PlantAnalyzer
|
||||
- type: ActivatableUI
|
||||
key: enum.PlantAnalyzerUiKey.Key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.PlantAnalyzerUiKey.Key:
|
||||
type: PlantAnalyzerBoundUserInterface
|
||||
- type: ItemToggle
|
||||
onUse: false
|
||||
- type: PlantAnalyzer
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.PowerCellSlotVisuals.Enabled:
|
||||
enum.PowerDeviceVisualLayers.Powered:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
- type: GuideHelp
|
||||
guides:
|
||||
- Botany
|
||||
- Chemicals
|
||||
|
||||
- type: entity
|
||||
id: PlantAnalyzer
|
||||
parent: [ PlantAnalyzerUnpowered, PowerCellSlotSmallItem ]
|
||||
suffix: Powered
|
||||
components:
|
||||
- type: PowerCellDraw
|
||||
drawRate: 1.2
|
||||
- type: ToggleCellDraw
|
||||
- type: ActivatableUIRequiresPowerCell
|
||||
|
||||
- type: entity
|
||||
id: PlantAnalyzerEmpty
|
||||
parent: PlantAnalyzer
|
||||
suffix: Empty
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: icon
|
||||
- state: analyzer
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.PowerDeviceVisualLayers.Powered" ]
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
cell_slot:
|
||||
name: power-cell-slot-component-slot-name-default
|
||||
|
||||
- type: entity
|
||||
name: plant analyzer report
|
||||
parent: Paper
|
||||
id: PlantAnalyzerReportPaper
|
||||
description: A printout from a plant analyzer.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/bureaucracy.rsi
|
||||
layers:
|
||||
- state: paper_receipt_horizontal
|
||||
- state: paper_receipt_horizontal_words
|
||||
map: ["enum.PaperVisualLayers.Writing"]
|
||||
visible: false
|
||||
- state: paper_stamp-generic
|
||||
map: ["enum.PaperVisualLayers.Stamp"]
|
||||
visible: false
|
||||
- type: PaperVisuals
|
||||
backgroundImagePath: "/Textures/Interface/Paper/paper_background_perforated.svg.96dpi.png"
|
||||
backgroundImageTile: true
|
||||
backgroundPatchMargin: 6.0, 0.0, 6.0, 0.0
|
||||
contentMargin: 6.0, 6.0, 6.0, 6.0
|
||||
maxWritableArea: 375.0, 600.0
|
||||
@@ -0,0 +1,4 @@
|
||||
- type: entity
|
||||
parent: StatusEffectSlowdown
|
||||
id: BasicSlowdownStatusEffect
|
||||
name: basic slowdown
|
||||
@@ -25,4 +25,42 @@
|
||||
- MindRoleVampire
|
||||
briefing:
|
||||
sound: "/Audio/_Wega/Ambience/Antag/vampare_start.ogg"
|
||||
|
||||
|
||||
# Extended testing
|
||||
- type: entity
|
||||
parent: BaseGameRule
|
||||
id: SubThiefExtended
|
||||
components:
|
||||
- type: ThiefRule
|
||||
- type: AntagObjectives
|
||||
objectives:
|
||||
- EscapeThiefShuttleObjective
|
||||
- type: AntagRandomObjectives
|
||||
sets:
|
||||
- groups: ThiefBigObjectiveGroups
|
||||
prob: 0.7
|
||||
maxPicks: 1
|
||||
- groups: ThiefObjectiveGroups
|
||||
maxPicks: 10
|
||||
maxDifficulty: 2.5
|
||||
- type: AntagSelection
|
||||
agentName: thief-round-end-agent-name
|
||||
selectionTime: IntraPlayerSpawn
|
||||
definitions:
|
||||
- prefRoles: [ Thief ]
|
||||
max: 4
|
||||
playerRatio: 15
|
||||
lateJoinAdditional: true
|
||||
allowNonHumans: true
|
||||
multiAntagSetting: NotExclusive
|
||||
startingGear: ThiefGear
|
||||
components:
|
||||
- type: Thieving
|
||||
stripTimeReduction: 2
|
||||
stealthy: true
|
||||
mindRoles:
|
||||
- MindRoleThief
|
||||
briefing:
|
||||
sound: "/Audio/Misc/thief_greeting.ogg"
|
||||
- type: DynamicRuleCost
|
||||
cost: 75
|
||||
|
||||
10
Resources/Prototypes/_Wega/Recipes/Lathes/Packs/service.yml
Normal file
10
Resources/Prototypes/_Wega/Recipes/Lathes/Packs/service.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
- type: latheRecipePack
|
||||
id: HydroponicsStatic
|
||||
recipes:
|
||||
- HydroponicsToolMiniHoe
|
||||
- HydroponicsToolScythe
|
||||
- HydroponicsToolHatchet
|
||||
- HydroponicsToolSpade
|
||||
- HydroponicsToolClippers
|
||||
- PlantAnalyzer
|
||||
4
Resources/Prototypes/_Wega/Recipes/Lathes/botany.yml
Normal file
4
Resources/Prototypes/_Wega/Recipes/Lathes/botany.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- type: latheRecipe
|
||||
parent: HandheldHealthAnalyzer
|
||||
id: PlantAnalyzer
|
||||
result: PlantAnalyzerEmpty
|
||||
17
Resources/Prototypes/_Wega/Recipes/Reactions/chemicals.yml
Normal file
17
Resources/Prototypes/_Wega/Recipes/Reactions/chemicals.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
- type: reaction
|
||||
id: SodiumExplosion
|
||||
impact: Medium
|
||||
priority: 20
|
||||
conserveEnergy: false
|
||||
reactants:
|
||||
Water:
|
||||
amount: 1
|
||||
Sodium:
|
||||
amount: 1
|
||||
effects:
|
||||
- !type:Explosion
|
||||
explosionType: Default
|
||||
intensityPerUnit: 0.15
|
||||
maxTotalIntensity: 60
|
||||
intensitySlope: 3
|
||||
maxIntensity: 5
|
||||
@@ -76,13 +76,14 @@
|
||||
- extended
|
||||
- shittersafari
|
||||
name: extended-title
|
||||
showInVote: true # Corvax-Wega-Edit
|
||||
showInVote: true # Corvax-Wega-Edit
|
||||
description: extended-description
|
||||
rules:
|
||||
- LightStationEventScheduler # Corvax-Wega-Edit
|
||||
- LightStationEventScheduler # Corvax-Wega-Edit
|
||||
- MeteorSwarmScheduler
|
||||
- SpaceTrafficControlEventScheduler
|
||||
- BasicRoundstartVariation
|
||||
- SubThiefExtended # Corvax-Wega-Testing
|
||||
|
||||
- type: gamePreset
|
||||
id: Greenshift
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 468 B |
@@ -14,10 +14,6 @@
|
||||
"name": "jumpsuit",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "jumpsuit-female",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "shoes",
|
||||
"directions" : 4
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 805 B |
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/f032bb1d4d982d4ee57c214c18238aaddb45d512/icons/obj/devices/scanner.dmi",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "analyzer",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "icon"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user