From 5cda653509394dae78d10896686d89185904519c Mon Sep 17 00:00:00 2001
From: Kill_Me_I_Noobs <118206719+Vonsant@users.noreply.github.com>
Date: Sun, 1 Dec 2024 14:44:19 +0300
Subject: [PATCH] =?UTF-8?q?=D0=90=D0=BD=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0?=
=?UTF-8?q?=D1=82=D0=BE=D1=80=20=D1=81=D0=B5=D0=BC=D1=8F=D0=BD=20=D0=B8=20?=
=?UTF-8?q?=D1=80=D0=B0=D1=81=D1=82=D0=B5=D0=BD=D0=B8=D0=B9=20(#102)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* AnalPlant
* LittleFixes
* LittleFixes2
---
.../UI/PlantAnalyzerBoundUserInterface.cs | 53 ++++
.../PlantAnalyzer/UI/PlantAnalyzerWindow.xaml | 50 ++++
.../UI/PlantAnalyzerWindow.xaml.cs | 203 ++++++++++++++
.../Components/PlantAnalyzerComponent.cs | 33 +++
.../Botany/Systems/PlantAnalyzerSystem.cs | 259 ++++++++++++++++++
.../PlantAnalyzerDoAfterEvent.cs | 9 +
...lantAnalyzerScannedSeedPlantInformation.cs | 93 +++++++
.../PlantAnalyzer/PlantAnalyzerUiKey.cs | 9 +
.../plantanalyzer/plantanalyzer.ftl | 53 ++++
.../plantanalyzer/plantanalyzer.ftl | 53 ++++
.../objects/devices/plant_analyzer.ftl | 10 +
.../VendingMachines/Inventories/nutri.yml | 1 +
.../Entities/Structures/Machines/lathe.yml | 1 +
.../Prototypes/Research/civilianservices.yml | 1 +
.../Objects/Devices/plant_analyzer.yml | 65 +++++
.../_CorvaxNext/Recipes/Lathes/botany.yml | 9 +
.../_CorvaxNext/Recipes/Lathes/devices.yml | 9 +
.../Devices/plant_analyzer.rsi/icon.png | Bin 0 -> 2189 bytes
.../Devices/plant_analyzer.rsi/meta.json | 40 +++
.../plant_analyzer.rsi/plantanalyzer.png | Bin 0 -> 364 bytes
.../Devices/plant_analyzer.rsi/unlit.png | Bin 0 -> 1181 bytes
21 files changed, 951 insertions(+)
create mode 100644 Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerBoundUserInterface.cs
create mode 100644 Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml
create mode 100644 Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml.cs
create mode 100644 Content.Server/_CorvaxNext/Botany/Components/PlantAnalyzerComponent.cs
create mode 100644 Content.Server/_CorvaxNext/Botany/Systems/PlantAnalyzerSystem.cs
create mode 100644 Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerDoAfterEvent.cs
create mode 100644 Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerScannedSeedPlantInformation.cs
create mode 100644 Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerUiKey.cs
create mode 100644 Resources/Locale/en-US/_corvaxnext/plantanalyzer/plantanalyzer.ftl
create mode 100644 Resources/Locale/ru-RU/_CorvaxNext/plantanalyzer/plantanalyzer.ftl
create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/plant_analyzer.ftl
create mode 100644 Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/plant_analyzer.yml
create mode 100644 Resources/Prototypes/_CorvaxNext/Recipes/Lathes/botany.yml
create mode 100644 Resources/Prototypes/_CorvaxNext/Recipes/Lathes/devices.yml
create mode 100644 Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/icon.png
create mode 100644 Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/meta.json
create mode 100644 Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/plantanalyzer.png
create mode 100644 Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/unlit.png
diff --git a/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerBoundUserInterface.cs b/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerBoundUserInterface.cs
new file mode 100644
index 00000000000..8c8d571e7a7
--- /dev/null
+++ b/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerBoundUserInterface.cs
@@ -0,0 +1,53 @@
+using Content.Shared._CorvaxNext.PlantAnalyzer;
+using JetBrains.Annotations;
+
+namespace Content.Client._CorvaxNext.PlantAnalyzer.UI;
+
+[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 = new PlantAnalyzerWindow(this)
+ {
+ Title = Loc.GetString("plant-analyzer-interface-title"),
+ };
+ _window.OnClose += Close;
+ _window.OpenCenteredLeft();
+ }
+
+ protected override void ReceiveMessage(BoundUserInterfaceMessage message)
+ {
+ if (_window == null)
+ return;
+
+ if (message is not PlantAnalyzerScannedSeedPlantInformation cast)
+ return;
+ _window.Populate(cast);
+ }
+
+ public void AdvPressed(bool scanMode)
+ {
+ SendMessage(new PlantAnalyzerSetMode(scanMode));
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ if (_window != null)
+ _window.OnClose -= Close;
+
+ _window?.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml b/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml
new file mode 100644
index 00000000000..5405a858d89
--- /dev/null
+++ b/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml.cs b/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml.cs
new file mode 100644
index 00000000000..df86ef9e494
--- /dev/null
+++ b/Content.Client/_CorvaxNext/PlantAnalyzer/UI/PlantAnalyzerWindow.xaml.cs
@@ -0,0 +1,203 @@
+using Content.Shared._CorvaxNext.PlantAnalyzer;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using System.Linq;
+using System.Text;
+using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
+
+namespace Content.Client._CorvaxNext.PlantAnalyzer.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class PlantAnalyzerWindow : FancyWindow
+{
+ private readonly IEntityManager _entityManager;
+ private readonly ButtonGroup _buttonGroup = new();
+
+ private const string IndentedNewline = "\n ";
+
+ public PlantAnalyzerWindow(PlantAnalyzerBoundUserInterface owner)
+ {
+ RobustXamlLoader.Load(this);
+
+ var dependencies = IoCManager.Instance!;
+ _entityManager = dependencies.Resolve();
+
+ OnButton.Group = _buttonGroup;
+ OnButton.ToggleMode = true;
+ OffButton.Group = _buttonGroup;
+ OffButton.ToggleMode = true;
+
+ OnButton.OnPressed += _ => owner.AdvPressed(true);
+ OffButton.OnPressed += _ => owner.AdvPressed(false);
+ }
+
+ public void Populate(PlantAnalyzerScannedSeedPlantInformation msg)
+ {
+ var target = _entityManager.GetEntity(msg.TargetEntity);
+ Title = Loc.GetString("plant-analyzer-interface-title");
+
+ if (target is null)
+ {
+ NoData.Visible = true;
+ return;
+ }
+ NoData.Visible = false;
+
+ if (msg.AdvancedInfo is not null)
+ {
+ OnButton.Pressed = true;
+ }
+ else
+ {
+ OffButton.Pressed = true;
+ }
+
+ // Process message fields into strings.
+ StringBuilder chemString = new();
+ if (msg.SeedChem is not null)
+ {
+ foreach (var chem in msg.SeedChem)
+ {
+ chemString.Append(IndentedNewline);
+ chemString.Append(chem);
+ }
+ }
+
+ StringBuilder exudeGases = GetStringFromGasFlags(msg.ExudeGases);
+ StringBuilder consudeGases = GetStringFromGasFlags(msg.ConsumeGases);
+
+ if (msg.IsTray)
+ PlantName.Text = Loc.GetString("plant-analyzer-window-label-name-scanned-plant", ("seedName", Loc.GetString(string.IsNullOrEmpty(msg.SeedName) ? "plant-analyzer-unknown-plant" : msg.SeedName)));
+ else
+ PlantName.Text = Loc.GetString("plant-analyzer-window-label-name-scanned-seed", ("seedName", Loc.GetString(string.IsNullOrEmpty(msg.SeedName) ? "plant-analyzer-unknown-plant" : msg.SeedName)));
+ // Basics
+ PlantYield.Text = Loc.GetString("plant-analyzer-plant-yield-text", ("seedYield", $"{msg.SeedYield:D0}"));
+ Potency.Text = Loc.GetString("plant-analyzer-plant-potency-text", ("seedPotency", $"{msg.SeedPotency:F0}"));
+ Repeat.Text = Loc.GetString("plant-analyzer-plant-harvest-text", ("plantHarvestType", Loc.GetString($"plant-analyzer-harvest-{msg.HarvestType}").ToString()));
+ Endurance.Text = Loc.GetString("plant-analyzer-plant-endurance-text", ("seedEndurance", $"{msg.Endurance:F0}"));
+ Chemicals.Text = Loc.GetString("plant-analyzer-plant-chemistry-text", ("seedChem", chemString));
+ ExudeGases.Text = Loc.GetString("plant-analyzer-plant-exude-text", ("gases", exudeGases.Length == 0 ? Loc.GetString("plant-analyzer-plant-gases-none") : exudeGases.ToString()));
+ ConsumeGases.Text = Loc.GetString("plant-analyzer-plant-consume-text", ("gases", consudeGases.Length == 0 ? Loc.GetString("plant-analyzer-plant-gases-none") : consudeGases.ToString()));
+ Lifespan.Text = Loc.GetString("plant-analyzer-plant-lifespan-text", ("lifespan", $"{msg.Lifespan:F1}"));
+ Maturation.Text = Loc.GetString("plant-analyzer-plant-maturation-text", ("maturation", $"{msg.Maturation:F1}"));
+ Production.Text = Loc.GetString("plant-analyzer-plant-production-text", ("production", $"{msg.Production:F1}"));
+ GrowthStages.Text = Loc.GetString("plant-analyzer-plant-growthstages-text", ("growthStages", $"{msg.GrowthStages:D0}"));
+ // Tolerances
+ var adv = msg.AdvancedInfo;
+ NutrientUsage.Text = Loc.GetString("plant-analyzer-tolerance-nutrient-usage", ("nutrientUsage", adv is null ? "-" : $"{adv.Value.NutrientConsumption:F2}"));
+ WaterUsage.Text = Loc.GetString("plant-analyzer-tolerance-water-usage", ("waterUsage", adv is null ? "-" : $"{adv.Value.WaterConsumption:F2}"));
+ IdealHeat.Text = Loc.GetString("plant-analyzer-tolerance-ideal-heat", ("idealHeat", adv is null ? "-" : $"{adv.Value.IdealHeat:F0}"));
+ HeatTolerance.Text = Loc.GetString("plant-analyzer-tolerance-heat-tolerance", ("heatTolerance", adv is null ? "-" : $"{adv.Value.HeatTolerance:F1}"));
+ IdealLight.Text = Loc.GetString("plant-analyzer-tolerance-ideal-light", ("idealLight", adv is null ? "-" : $"{adv.Value.IdealLight:F1}"));
+ LightTolerance.Text = Loc.GetString("plant-analyzer-tolerance-light-tolerance", ("lightTolerance", adv is null ? "-" : $"{adv.Value.LightTolerance:F1}"));
+ ToxinsTolerance.Text = Loc.GetString("plant-analyzer-tolerance-toxin-tolerance", ("toxinsTolerance", adv is null ? "-" : $"{adv.Value.ToxinsTolerance:F1}"));
+ LowPressureTolerance.Text = Loc.GetString("plant-analyzer-tolerance-low-pressure", ("lowPressureTolerance", adv is null ? "-" : $"{adv.Value.LowPressureTolerance:F1}")); ;
+ HighPressureTolerance.Text = Loc.GetString("plant-analyzer-tolerance-high-pressure", ("highPressureTolerance", adv is null ? "-" : $"{adv.Value.HighPressureTolerance:F1}"));
+ PestTolerance.Text = Loc.GetString("plant-analyzer-tolerance-pest-tolerance", ("pestTolerance", adv is null ? "-" : $"{adv.Value.PestTolerance:F1}"));
+ WeedTolerance.Text = Loc.GetString("plant-analyzer-tolerance-weed-tolerance", ("weedTolerance", adv is null ? "-" : $"{adv.Value.WeedTolerance:F1}"));
+ // Misc
+
+ if (adv is not null)
+ {
+ var advInst = adv.Value;
+ StringBuilder mutations = new();
+ if (advInst.Mutations.HasFlag(MutationFlags.TurnIntoKudzu))
+ {
+ mutations.Append(IndentedNewline);
+ mutations.Append(Loc.GetString("plant-analyzer-mutation-turnintokudzu"));
+ }
+ if (advInst.Mutations.HasFlag(MutationFlags.Seedless))
+ {
+ mutations.Append(IndentedNewline);
+ mutations.Append(Loc.GetString("plant-analyzer-mutation-seedless"));
+ }
+ if (advInst.Mutations.HasFlag(MutationFlags.Ligneous))
+ {
+ mutations.Append(IndentedNewline);
+ mutations.Append(Loc.GetString("plant-analyzer-mutation-ligneous"));
+ }
+ if (advInst.Mutations.HasFlag(MutationFlags.CanScream))
+ {
+ mutations.Append(IndentedNewline);
+ mutations.Append(Loc.GetString("plant-analyzer-mutation-canscream"));
+ }
+
+ Traits.Text = Loc.GetString("plant-analyzer-plant-mutations-text", ("traits", mutations.ToString()));
+ }
+ else
+ {
+ Traits.Text = Loc.GetString("plant-analyzer-plant-mutations-text", ("traits", "-"));
+ }
+
+ StringBuilder speciation = new();
+ if (msg.Speciation is null)
+ {
+ speciation.Append("-");
+ }
+ else
+ {
+ foreach (var species in msg.Speciation)
+ {
+ speciation.Append(IndentedNewline);
+ speciation.Append(Loc.GetString(species));
+ }
+ }
+
+ PlantSpeciation.Text = Loc.GetString("plant-analyzer-plant-speciation-text", ("speciation", speciation.ToString()));
+ }
+
+ private StringBuilder GetStringFromGasFlags(GasFlags flags)
+ {
+ StringBuilder output = new();
+ if (flags.HasFlag(GasFlags.Nitrogen))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-nitrogen"));
+ }
+ if (flags.HasFlag(GasFlags.Oxygen))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-oxygen"));
+ }
+ if (flags.HasFlag(GasFlags.CarbonDioxide))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-co2"));
+ }
+ if (flags.HasFlag(GasFlags.Plasma))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-plasma"));
+ }
+ if (flags.HasFlag(GasFlags.Tritium))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-tritium"));
+ }
+ if (flags.HasFlag(GasFlags.WaterVapor))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-water-vapor"));
+ }
+ if (flags.HasFlag(GasFlags.Ammonia))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-ammonia"));
+ }
+ if (flags.HasFlag(GasFlags.NitrousOxide))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-n2o"));
+ }
+ if (flags.HasFlag(GasFlags.Frezon))
+ {
+ output.Append(IndentedNewline);
+ output.Append(Loc.GetString("gases-frezon"));
+ }
+ return output;
+ }
+}
\ No newline at end of file
diff --git a/Content.Server/_CorvaxNext/Botany/Components/PlantAnalyzerComponent.cs b/Content.Server/_CorvaxNext/Botany/Components/PlantAnalyzerComponent.cs
new file mode 100644
index 00000000000..a6ff539ff23
--- /dev/null
+++ b/Content.Server/_CorvaxNext/Botany/Components/PlantAnalyzerComponent.cs
@@ -0,0 +1,33 @@
+using Content.Shared.DoAfter;
+using Robust.Shared.Audio;
+
+namespace Content.Server.Botany.Components; // This is how it supposed to be
+
+///
+/// After scanning, retrieves the target Uid to use with its related UI.
+///
+[RegisterComponent]
+public sealed partial class PlantAnalyzerComponent : Component
+{
+ [DataDefinition]
+ public partial struct PlantAnalyzerSettings
+ {
+ [DataField]
+ public bool AdvancedScan;
+
+ [DataField]
+ public float ScanDelay;
+
+ [DataField]
+ public float AdvScanDelay;
+ }
+
+ [DataField, ViewVariables]
+ public PlantAnalyzerSettings Settings = new();
+
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
+ public DoAfterId? DoAfter;
+
+ [DataField]
+ public SoundSpecifier? ScanningEndSound;
+}
\ No newline at end of file
diff --git a/Content.Server/_CorvaxNext/Botany/Systems/PlantAnalyzerSystem.cs b/Content.Server/_CorvaxNext/Botany/Systems/PlantAnalyzerSystem.cs
new file mode 100644
index 00000000000..6ff759b62ac
--- /dev/null
+++ b/Content.Server/_CorvaxNext/Botany/Systems/PlantAnalyzerSystem.cs
@@ -0,0 +1,259 @@
+using Content.Server.Botany.Components;
+using Content.Server.PowerCell;
+using Content.Shared.DoAfter;
+using Content.Shared.Interaction;
+using Content.Shared._CorvaxNext.PlantAnalyzer;
+using Robust.Server.GameObjects;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+using System.Linq;
+using System.Text;
+using Content.Shared.Atmos;
+
+namespace Content.Server.Botany.Systems; // This is how it supposed to be
+
+public sealed class PlantAnalyzerSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly PowerCellSystem _cell = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+ [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnAfterInteract);
+ SubscribeLocalEvent(OnDoAfter);
+ SubscribeLocalEvent(OnModeSelected);
+ }
+
+ private void OnAfterInteract(Entity ent, ref AfterInteractEvent args)
+ {
+ if (args.Target == null || !args.CanReach || !_cell.HasActivatableCharge(ent, user: args.User))
+ return;
+
+ if (ent.Comp.DoAfter != null)
+ return;
+
+ if (HasComp(args.Target) || TryComp(args.Target, out var plantHolder) && plantHolder.Seed != null)
+ {
+
+ if (ent.Comp.Settings.AdvancedScan)
+ {
+ var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.Settings.AdvScanDelay, new PlantAnalyzerDoAfterEvent(), ent, target: args.Target, used: ent)
+ {
+ NeedHand = true,
+ BreakOnDamage = true,
+ BreakOnMove = true,
+ MovementThreshold = 0.01f
+ };
+ _doAfterSystem.TryStartDoAfter(doAfterArgs, out ent.Comp.DoAfter);
+ }
+ else
+ {
+ var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.Settings.ScanDelay, new PlantAnalyzerDoAfterEvent(), ent, target: args.Target, used: ent)
+ {
+ NeedHand = true,
+ BreakOnDamage = true,
+ BreakOnMove = true,
+ MovementThreshold = 0.01f
+ };
+ _doAfterSystem.TryStartDoAfter(doAfterArgs, out ent.Comp.DoAfter);
+ }
+ }
+ }
+
+ private void OnDoAfter(Entity ent, ref PlantAnalyzerDoAfterEvent args)
+ {
+ ent.Comp.DoAfter = null;
+ // Double charge use for advanced scan.
+ if (ent.Comp.Settings.AdvancedScan)
+ {
+ if (!_cell.TryUseActivatableCharge(ent, user: args.User))
+ return;
+ }
+ if (args.Handled || args.Cancelled || args.Args.Target == null || !_cell.TryUseActivatableCharge(ent.Owner, user: args.User))
+ return;
+
+ _audio.PlayPvs(ent.Comp.ScanningEndSound, ent);
+
+ OpenUserInterface(args.User, ent);
+ UpdateScannedUser(ent, args.Args.Target.Value);
+
+ args.Handled = true;
+ }
+
+ private void OpenUserInterface(EntityUid user, EntityUid analyzer)
+ {
+ if (!TryComp(user, out var actor) || !_uiSystem.HasUi(analyzer, PlantAnalyzerUiKey.Key))
+ return;
+
+ _uiSystem.OpenUi(analyzer, PlantAnalyzerUiKey.Key, actor.PlayerSession);
+ }
+
+ public void UpdateScannedUser(Entity ent, EntityUid target)
+ {
+ if (!_uiSystem.HasUi(ent, PlantAnalyzerUiKey.Key))
+ return;
+
+ if (TryComp(target, out var seedComp))
+ {
+ if (seedComp.Seed != null)
+ {
+ var state = ObtainingGeneDataSeed(seedComp.Seed, target, false, ent.Comp.Settings.AdvancedScan);
+ _uiSystem.ServerSendUiMessage(ent.Owner, PlantAnalyzerUiKey.Key, state);
+ }
+ else if (seedComp.SeedId != null && _prototypeManager.TryIndex(seedComp.SeedId, out SeedPrototype? protoSeed))
+ {
+ var state = ObtainingGeneDataSeed(protoSeed, target, false, ent.Comp.Settings.AdvancedScan);
+ _uiSystem.ServerSendUiMessage(ent.Owner, PlantAnalyzerUiKey.Key, state);
+ }
+ }
+ else if (TryComp(target, out var plantComp))
+ {
+ if (plantComp.Seed != null)
+ {
+ var state = ObtainingGeneDataSeed(plantComp.Seed, target, true, ent.Comp.Settings.AdvancedScan);
+ _uiSystem.ServerSendUiMessage(ent.Owner, PlantAnalyzerUiKey.Key, state);
+ }
+ }
+ }
+
+ ///
+ /// Analysis of seed from prototype.
+ ///
+ public PlantAnalyzerScannedSeedPlantInformation ObtainingGeneDataSeed(SeedData seedData, EntityUid target, bool isTray, bool scanIsAdvanced)
+ {
+ // Get trickier fields first.
+ AnalyzerHarvestType harvestType = AnalyzerHarvestType.Unknown;
+ switch (seedData.HarvestRepeat)
+ {
+ case HarvestType.Repeat:
+ harvestType = AnalyzerHarvestType.Repeat;
+ break;
+ case HarvestType.NoRepeat:
+ harvestType = AnalyzerHarvestType.NoRepeat;
+ break;
+ case HarvestType.SelfHarvest:
+ harvestType = AnalyzerHarvestType.SelfHarvest;
+ break;
+ default:
+ break;
+ }
+
+ var mutationProtos = seedData.MutationPrototypes;
+ List mutationStrings = new();
+ foreach (var mutationProto in mutationProtos)
+ {
+ if (_prototypeManager.TryIndex(mutationProto, out var seed))
+ {
+ mutationStrings.Add(seed.DisplayName);
+ }
+ }
+
+ PlantAnalyzerScannedSeedPlantInformation ret = new()
+ {
+ TargetEntity = GetNetEntity(target),
+ IsTray = isTray,
+ SeedName = seedData.DisplayName,
+ SeedChem = seedData.Chemicals.Keys.ToArray(),
+ HarvestType = harvestType,
+ ExudeGases = GetGasFlags(seedData.ExudeGasses.Keys),
+ ConsumeGases = GetGasFlags(seedData.ConsumeGasses.Keys),
+ Endurance = seedData.Endurance,
+ SeedYield = seedData.Yield,
+ Lifespan = seedData.Lifespan,
+ Maturation = seedData.Maturation,
+ Production = seedData.Production,
+ GrowthStages = seedData.GrowthStages,
+ SeedPotency = seedData.Potency,
+ Speciation = mutationStrings.ToArray()
+ };
+
+ if (scanIsAdvanced)
+ {
+ AdvancedScanInfo advancedInfo = new()
+ {
+ NutrientConsumption = seedData.NutrientConsumption,
+ WaterConsumption = seedData.WaterConsumption,
+ IdealHeat = seedData.IdealHeat,
+ HeatTolerance = seedData.HeatTolerance,
+ IdealLight = seedData.IdealLight,
+ LightTolerance = seedData.LightTolerance,
+ ToxinsTolerance = seedData.ToxinsTolerance,
+ LowPressureTolerance = seedData.LowPressureTolerance,
+ HighPressureTolerance = seedData.HighPressureTolerance,
+ PestTolerance = seedData.PestTolerance,
+ WeedTolerance = seedData.WeedTolerance,
+ Mutations = GetMutationFlags(seedData)
+ };
+
+ ret.AdvancedInfo = advancedInfo;
+ }
+ return ret;
+ }
+
+ public MutationFlags GetMutationFlags(SeedData plant)
+ {
+ MutationFlags ret = MutationFlags.None;
+ if (plant.TurnIntoKudzu) ret |= MutationFlags.TurnIntoKudzu;
+ if (plant.Seedless) ret |= MutationFlags.Seedless;
+ if (plant.Ligneous) ret |= MutationFlags.Ligneous;
+ if (plant.CanScream) ret |= MutationFlags.CanScream;
+
+ return ret;
+ }
+
+ public GasFlags GetGasFlags(IEnumerable gases)
+ {
+ var gasFlags = GasFlags.None;
+ foreach (var gas in gases)
+ {
+ switch (gas)
+ {
+ case Gas.Nitrogen:
+ gasFlags |= GasFlags.Nitrogen;
+ break;
+ case Gas.Oxygen:
+ gasFlags |= GasFlags.Oxygen;
+ break;
+ case Gas.CarbonDioxide:
+ gasFlags |= GasFlags.CarbonDioxide;
+ break;
+ case Gas.Plasma:
+ gasFlags |= GasFlags.Plasma;
+ break;
+ case Gas.Tritium:
+ gasFlags |= GasFlags.Tritium;
+ break;
+ case Gas.WaterVapor:
+ gasFlags |= GasFlags.WaterVapor;
+ break;
+ case Gas.Ammonia:
+ gasFlags |= GasFlags.Ammonia;
+ break;
+ case Gas.NitrousOxide:
+ gasFlags |= GasFlags.NitrousOxide;
+ break;
+ case Gas.Frezon:
+ gasFlags |= GasFlags.Frezon;
+ break;
+ }
+ }
+ return gasFlags;
+ }
+
+ private void OnModeSelected(Entity ent, ref PlantAnalyzerSetMode args)
+ {
+ SetMode(ent, args.AdvancedScan);
+ }
+
+ public void SetMode(Entity ent, bool isAdvMode)
+ {
+ if (ent.Comp.DoAfter != null)
+ return;
+ ent.Comp.Settings.AdvancedScan = isAdvMode;
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerDoAfterEvent.cs b/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerDoAfterEvent.cs
new file mode 100644
index 00000000000..e9334e29eff
--- /dev/null
+++ b/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerDoAfterEvent.cs
@@ -0,0 +1,9 @@
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._CorvaxNext.PlantAnalyzer;
+
+[Serializable, NetSerializable]
+public sealed partial class PlantAnalyzerDoAfterEvent : SimpleDoAfterEvent
+{
+}
\ No newline at end of file
diff --git a/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerScannedSeedPlantInformation.cs b/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerScannedSeedPlantInformation.cs
new file mode 100644
index 00000000000..689fdaf6692
--- /dev/null
+++ b/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerScannedSeedPlantInformation.cs
@@ -0,0 +1,93 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._CorvaxNext.PlantAnalyzer;
+
+///
+/// The information about the last scanned plant/seed is stored here.
+///
+[Serializable, NetSerializable]
+public sealed class PlantAnalyzerScannedSeedPlantInformation : BoundUserInterfaceMessage
+{
+ public NetEntity? TargetEntity;
+ public bool IsTray;
+
+ public string? SeedName;
+ public string[]? SeedChem;
+ public AnalyzerHarvestType HarvestType;
+ public GasFlags ExudeGases;
+ public GasFlags ConsumeGases;
+ public float Endurance;
+ public int SeedYield;
+ public float Lifespan;
+ public float Maturation;
+ public float Production;
+ public int GrowthStages;
+ public float SeedPotency;
+ public string[]? Speciation; // Currently only available on server, we need to send strings to the client.
+ public AdvancedScanInfo? AdvancedInfo;
+}
+
+///
+/// Information gathered in an advanced scan.
+///
+[Serializable, NetSerializable]
+public struct AdvancedScanInfo
+{
+ public float NutrientConsumption;
+ public float WaterConsumption;
+ public float IdealHeat;
+ public float HeatTolerance;
+ public float IdealLight;
+ public float LightTolerance;
+ public float ToxinsTolerance;
+ public float LowPressureTolerance;
+ public float HighPressureTolerance;
+ public float PestTolerance;
+ public float WeedTolerance;
+ public MutationFlags Mutations;
+}
+
+// Note: currently leaving out Viable.
+[Flags]
+public enum MutationFlags : byte
+{
+ None = 0,
+ TurnIntoKudzu = 1,
+ Seedless = 2,
+ Ligneous = 4,
+ CanScream = 8,
+}
+
+[Flags]
+public enum GasFlags : short
+{
+ None = 0,
+ Nitrogen = 1,
+ Oxygen = 2,
+ CarbonDioxide = 4,
+ Plasma = 8,
+ Tritium = 16,
+ WaterVapor = 32,
+ Ammonia = 64,
+ NitrousOxide = 128,
+ Frezon = 256,
+}
+
+public enum AnalyzerHarvestType : byte
+{
+ Unknown, // Just in case the backing enum type changes and we haven't caught it.
+ Repeat,
+ NoRepeat,
+ SelfHarvest
+}
+
+
+[Serializable, NetSerializable]
+public sealed class PlantAnalyzerSetMode : BoundUserInterfaceMessage
+{
+ public bool AdvancedScan { get; }
+ public PlantAnalyzerSetMode(bool advancedScan)
+ {
+ AdvancedScan = advancedScan;
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerUiKey.cs b/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerUiKey.cs
new file mode 100644
index 00000000000..595a4cc8c2b
--- /dev/null
+++ b/Content.Shared/_CorvaxNext/PlantAnalyzer/PlantAnalyzerUiKey.cs
@@ -0,0 +1,9 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._CorvaxNext.PlantAnalyzer;
+
+[Serializable, NetSerializable]
+public enum PlantAnalyzerUiKey : byte
+{
+ Key
+}
\ No newline at end of file
diff --git a/Resources/Locale/en-US/_corvaxnext/plantanalyzer/plantanalyzer.ftl b/Resources/Locale/en-US/_corvaxnext/plantanalyzer/plantanalyzer.ftl
new file mode 100644
index 00000000000..7517014adc6
--- /dev/null
+++ b/Resources/Locale/en-US/_corvaxnext/plantanalyzer/plantanalyzer.ftl
@@ -0,0 +1,53 @@
+plant-analyzer-interface-title = Agrinfo 3000 Plant Analyzer
+plant-analyzer-window-no-seed-information-text = No seed/plant scanned.
+plant-analyzer-window-tab-basics = Basic Info
+plant-analyzer-window-tab-tolerances = Tolerances
+plant-analyzer-window-tab-mutations = Mutations
+plant-analyzer-window-scanmode = Advanced scan mode:
+plant-analyzer-window-mode-on = On
+plant-analyzer-window-mode-off = Off
+
+plant-analyzer-window-label-name-scanned-plant = Scanned plant: {$seedName}
+plant-analyzer-window-label-name-scanned-seed = Scanned seed: {$seedName}
+plant-analyzer-unknown-plant = Unknown
+plant-analyzer-plant-endurance-text = Plant endurance: {$seedEndurance}
+plant-analyzer-plant-chemistry-text = Сontained substances: {$seedChem}
+plant-analyzer-plant-gases-none = None
+plant-analyzer-plant-yield-text = Plant yield: {$seedYield}
+plant-analyzer-plant-harvest-text = Harvest type: {$plantHarvestType}
+plant-analyzer-plant-potency-text = Plant potency: {$seedPotency}%
+plant-analyzer-plant-exude-text = Emitted gases: {$gases}
+plant-analyzer-plant-consume-text = Consumed gases: {$gases}
+plant-analyzer-plant-lifespan-text = Lifespan: {$lifespan}
+plant-analyzer-plant-maturation-text = Maturation: {$maturation}
+plant-analyzer-plant-production-text = Production: {$production}
+plant-analyzer-plant-growthstages-text = Growth stages: {$growthStages}
+
+plant-analyzer-tolerance-nutrient-usage = Nutrient usage: {$nutrientUsage} u/stage
+plant-analyzer-tolerance-water-usage = Water usage: {$waterUsage} u/stage
+plant-analyzer-tolerance-ideal-heat = Ideal heat: {$idealHeat} K
+plant-analyzer-tolerance-heat-tolerance = Heat tolerance: ±{$heatTolerance} K
+plant-analyzer-tolerance-ideal-light = Ideal light: {$idealLight} cd
+plant-analyzer-tolerance-light-tolerance = Light tolerance: ±{$lightTolerance} cd
+plant-analyzer-tolerance-toxin-tolerance = Toxin tolerance: {$toxinsTolerance}
+plant-analyzer-tolerance-low-pressure = Low pressure: {$lowPressureTolerance} kPa
+plant-analyzer-tolerance-high-pressure = High pressure: {$highPressureTolerance} kPa
+plant-analyzer-tolerance-pest-tolerance = Pest tolerance: {$pestTolerance}
+plant-analyzer-tolerance-weed-tolerance = Weed tolerance: {$weedTolerance}
+
+plant-analyzer-plant-mutations-text = Mutations: {$traits}
+plant-analyzer-plant-speciation-text = Possible subtypes: {$speciation}
+
+plant-analyzer-mutation-unviable = Unviable
+plant-analyzer-mutation-turnintokudzu = Kudzufication
+plant-analyzer-mutation-seedless = Seedless
+plant-analyzer-mutation-slip = Slippery
+plant-analyzer-mutation-sentient = Sentient
+plant-analyzer-mutation-ligneous = Ligneous
+plant-analyzer-mutation-bioluminescent = Bioluminescent
+plant-analyzer-mutation-canscream = Mandragora
+
+plant-analyzer-harvest-Unknown = Unknown
+plant-analyzer-harvest-Repeat = Perennial
+plant-analyzer-harvest-NoRepeat = Ephemeral
+plant-analyzer-harvest-SelfHarvest = Self-harvesting
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/_CorvaxNext/plantanalyzer/plantanalyzer.ftl b/Resources/Locale/ru-RU/_CorvaxNext/plantanalyzer/plantanalyzer.ftl
new file mode 100644
index 00000000000..29ef19bdb64
--- /dev/null
+++ b/Resources/Locale/ru-RU/_CorvaxNext/plantanalyzer/plantanalyzer.ftl
@@ -0,0 +1,53 @@
+plant-analyzer-interface-title = АгроСкан 3000
+plant-analyzer-window-no-seed-information-text = Растение не обнаружено.
+plant-analyzer-window-tab-basics = Основное
+plant-analyzer-window-tab-tolerances = Устойчивость
+plant-analyzer-window-tab-mutations = Мутации
+plant-analyzer-window-scanmode = Расширенное сканирование:
+plant-analyzer-window-mode-on = Вкл
+plant-analyzer-window-mode-off = Выкл
+
+plant-analyzer-window-label-name-scanned-plant = Растение: {$seedName}
+plant-analyzer-window-label-name-scanned-seed = Семя: {$seedName}
+plant-analyzer-unknown-plant = Неизвестно
+plant-analyzer-plant-endurance-text = Выносливость: {$seedEndurance}
+plant-analyzer-plant-chemistry-text = Вещества: {$seedChem}
+plant-analyzer-plant-gases-none = Нет
+plant-analyzer-plant-yield-text = Урожайность: {$seedYield}
+plant-analyzer-plant-harvest-text = Тип урожая: {$plantHarvestType}
+plant-analyzer-plant-potency-text = Потенция: {$seedPotency}%
+plant-analyzer-plant-exude-text = Выделяемые газы: {$gases}
+plant-analyzer-plant-consume-text = Поглощаемые газы: {$gases}
+plant-analyzer-plant-lifespan-text = Срок жизни: {$lifespan}
+plant-analyzer-plant-maturation-text = Созревание: {$maturation}
+plant-analyzer-plant-production-text = Производство: {$production}
+plant-analyzer-plant-growthstages-text = Стадии роста: {$growthStages}
+
+plant-analyzer-tolerance-nutrient-usage = Потребление нутриентов: {$nutrientUsage} ед/стад.
+plant-analyzer-tolerance-water-usage = Потребление воды: {$waterUsage} ед/стад
+plant-analyzer-tolerance-ideal-heat = Оптимальная температура: {$idealHeat} K
+plant-analyzer-tolerance-heat-tolerance = Теплостойкость: ±{$heatTolerance} K
+plant-analyzer-tolerance-ideal-light = Оптимальное освещение: {$idealLight} кд
+plant-analyzer-tolerance-light-tolerance = Светостойкость: ±{$lightTolerance} кд
+plant-analyzer-tolerance-toxin-tolerance = Устойчивость к токсинам: {$toxinsTolerance}
+plant-analyzer-tolerance-low-pressure = Низкое давление: {$lowPressureTolerance} кПа
+plant-analyzer-tolerance-high-pressure = Высокое давление: {$highPressureTolerance} кПа
+plant-analyzer-tolerance-pest-tolerance = Защита от вредителей: {$pestTolerance}
+plant-analyzer-tolerance-weed-tolerance = Защита от сорняков: {$weedTolerance}
+
+plant-analyzer-plant-mutations-text = Мутации: {$traits}
+plant-analyzer-plant-speciation-text = Подвиды: {$speciation}
+
+plant-analyzer-mutation-unviable = Нежизнеспособно
+plant-analyzer-mutation-turnintokudzu = Кудзуфикация
+plant-analyzer-mutation-seedless = Бесплодное
+plant-analyzer-mutation-slip = Скользкое
+plant-analyzer-mutation-sentient = Разумное
+plant-analyzer-mutation-ligneous = Древо-подобное
+plant-analyzer-mutation-bioluminescent = Светящееся
+plant-analyzer-mutation-canscream = Кричащее
+
+plant-analyzer-harvest-Unknown = Неизвестно
+plant-analyzer-harvest-Repeat = Многолетнее
+plant-analyzer-harvest-NoRepeat = Однолетнее
+plant-analyzer-harvest-SelfHarvest = Самосбор
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/plant_analyzer.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/plant_analyzer.ftl
new file mode 100644
index 00000000000..6347d566c73
--- /dev/null
+++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/plant_analyzer.ftl
@@ -0,0 +1,10 @@
+ent-BasePlantAnalyzer = анализатор растений
+ .desc = Портативный прибор, позволяющий сканировать семена и растения для получения подробной информации об их генах.
+ent-PlantAnalyzer = { ent-BasePlantAnalyzer }
+ .desc = { ent-BasePlantAnalyzer.desc }
+ent-PlantAnalyzerEmpty = { ent-BasePlantAnalyzer }
+ .desc = { ent-BasePlantAnalyzer.desc }
+ .suffix = Пустой
+ent-PlantAnalyzerDebug = { ent-BasePlantAnalyzer }
+ .desc = { ent-BasePlantAnalyzer.desc }
+ .suffix = DEBUG
\ No newline at end of file
diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/nutri.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/nutri.yml
index b194755e864..750c433c761 100644
--- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/nutri.yml
+++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/nutri.yml
@@ -17,6 +17,7 @@
Bucket: 3
DiseaseSwab: 20
BoxAgrichem: 1
+ PlantAnalyzer: 1 # Corvax-Next-PlantAnalyzer
#TO DO:
#plant analyzer
emaggedInventory:
diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
index a589c1258db..68656f3c3aa 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
@@ -201,6 +201,7 @@
- WetFloorSign
- ClothingHeadHatCone
- FreezerElectronics
+ - PlantAnalyzer # Corvax-Next-PlantAnalyzer
- type: EmagLatheRecipes
emagStaticRecipes:
- BoxLethalshot
diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml
index bb146b09a33..c3b2e173153 100644
--- a/Resources/Prototypes/Research/civilianservices.yml
+++ b/Resources/Prototypes/Research/civilianservices.yml
@@ -15,6 +15,7 @@
- SeedExtractorMachineCircuitboard
- HydroponicsTrayMachineCircuitboard
- ReagentGrinderIndustrialMachineCircuitboard
+ - PlantAnalyzer # Corvax-Next-PlantAnalyzer
- type: technology
id: CritterMechs
diff --git a/Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/plant_analyzer.yml b/Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/plant_analyzer.yml
new file mode 100644
index 00000000000..2e53efac01b
--- /dev/null
+++ b/Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/plant_analyzer.yml
@@ -0,0 +1,65 @@
+- type: entity
+ name: plant analyzer
+ parent: BaseItem
+ id: BasePlantAnalyzer
+ description: A handheld device that allows you to scan seeds and plants to get detailed information about their genes.
+ abstract: true
+ components:
+ - type: Sprite
+ sprite: _CorvaxNext/Objects/Devices/plant_analyzer.rsi
+ layers:
+ - state: plantanalyzer
+ - state: unlit
+ map: [ "enum.PowerDeviceVisualLayers.Powered" ]
+ shader: unshaded
+ - type: Item
+ storedRotation: -90
+ - type: ActivatableUI
+ key: enum.PlantAnalyzerUiKey.Key
+ inHandsOnly: true
+ - type: UserInterface
+ interfaces:
+ enum.PlantAnalyzerUiKey.Key:
+ type: PlantAnalyzerBoundUserInterface
+ - type: PlantAnalyzer
+ settings:
+ scanDelay: 10
+ advScanDelay: 20
+ scanningEndSound:
+ path: "/Audio/Items/Medical/healthscanner.ogg"
+ - type: Appearance
+ - type: GenericVisualizer
+ visuals:
+ enum.PowerCellSlotVisuals.Enabled:
+ enum.PowerDeviceVisualLayers.Powered:
+ True: { visible: true }
+ False: { visible: false }
+
+- type: entity
+ id: PlantAnalyzer
+ parent: [BasePlantAnalyzer, PowerCellSlotSmallItem]
+ components:
+ - type: PowerCellDraw
+ drawRate: 0
+ useRate: 40
+ - type: ActivatableUIRequiresPowerCell
+
+- type: entity
+ id: PlantAnalyzerEmpty
+ parent: PlantAnalyzer
+ suffix: Empty
+ components:
+ - type: ItemSlots
+ slots:
+ cell_slot:
+ name: power-cell-slot-component-slot-name-default
+
+- type: entity
+ id: PlantAnalyzerDebug
+ parent: BasePlantAnalyzer
+ suffix: Debug
+ components:
+ - type: PlantAnalyzer
+ settings:
+ scanDelay: 0.1
+ advScanDelay: 0.5
\ No newline at end of file
diff --git a/Resources/Prototypes/_CorvaxNext/Recipes/Lathes/botany.yml b/Resources/Prototypes/_CorvaxNext/Recipes/Lathes/botany.yml
new file mode 100644
index 00000000000..1461003bce6
--- /dev/null
+++ b/Resources/Prototypes/_CorvaxNext/Recipes/Lathes/botany.yml
@@ -0,0 +1,9 @@
+- type: latheRecipe
+ id: Plantanalyzer
+ result: PlantAnalyzerEmpty
+ completetime: 2
+ materials:
+ Glass: 500
+ Steel: 500
+ Plastic: 100
+ Gold: 100
\ No newline at end of file
diff --git a/Resources/Prototypes/_CorvaxNext/Recipes/Lathes/devices.yml b/Resources/Prototypes/_CorvaxNext/Recipes/Lathes/devices.yml
new file mode 100644
index 00000000000..8e3cc546c02
--- /dev/null
+++ b/Resources/Prototypes/_CorvaxNext/Recipes/Lathes/devices.yml
@@ -0,0 +1,9 @@
+- type: latheRecipe
+ id: PlantAnalyzer
+ result: PlantAnalyzerEmpty
+ completetime: 2
+ materials:
+ Glass: 500
+ Steel: 500
+ Plastic: 100
+ Gold: 100
\ No newline at end of file
diff --git a/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/icon.png b/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb0ca5b03a2b811cc3210eb031c9f72147bd6c33
GIT binary patch
literal 2189
zcmbVNdrT8|9KTvX)XBSVSlj)K*rT$iOJqyK6b2y>fRzA#-bW
z1~KZGarl@sY%=~Z*>Fs=B_@DF3{!k;#QE4V-GaC*n;JLW68HKYln$RWYjbzM`~5z@
z&-e3v{Jz&*;#e|$>a3{{1Whk4vN^#MR<8IsaF-eze}y2`JiefyWHBqaIoP=*AJ!5U
zi#Zd5v}c)$3Nj$qh+I-d#lY$8%&B6%bMxk{&Wc56dr$NZLUGNX_s4EmsdC}EHLtG8
zQpf#Xw=JA#Tl-b)p8mX9SC{IvS*kBz3tiM^95L?+hrfUZ&gigAXp8=gs;h8E*Rh|b
z!*wqewdh;c#|(@tYdX{!x>&5ao2)q+|HBmc-n63i)b7*ARmRDmwW>Z^=FHB!OPa5z
zx@K@4-ksW+Wo2bq=8oa{?`OrVnz5^WWJY#yB;$JObHk|_t?eDq`TTDt$8FGTYSRos
z>kgk|+eoNi{rVk?W_I^hw^!qiZ&_XDisMK!EqNq;aCiE#-m5)3!{KYs<`1^-3e({P
zwfb+@>$~^PRc{RCs{6VvQ16Kqf2L8!)gW+F0!1%N5Hu}Wxm3{JeKR2__7?9dl}qi*
zDMs+=XeFGwpf3Q>5VUYnFhDa^oD91;FYmV^H;?upFwa_%RXKLt9w^`{`JzUVTiNJv
zF^yG>g+&(S!V7~GAnjFo8&fkz>7l@%GwU#YzWE)YZxHtKMc
z!Eq8cS#*TaU@+nfU=k;B49~#`5+#fjZlDMg9Qh%@n#g)6r|rdvE%0VVDrGr9VOV{A
zy{=xb6GSftYQuuzBu0`bK%i2CU#5eoUrHZmuyGP2@&TC_{IJ4EyMafRQji6yQg{q?boo95
zAhdRS)W##V_1r4
zsFHEXFiGf0Si6e%vqHU;Ibj25qh-#DfUN0J+<+2h7mh1gBlX$1p2G1ls9j)rPs77d
zf+7IOd><5)3`@)Oe}Y+t@(7}j1`hK++RI@9zZZd{VWbL#YC!~sfja%f`C`yVM8U&X
zgM|nC$*5YpohtTAGVN!$Vw)8KvC;87OXc8tH~5=TlhH$>CX2y6q@Ey1fM)>Q*<5>>JvZ4K*Jt|G&-INzXF)h%@tgS@b
zowW74mOj(w+SGkB0s16qad|^`X5RkPfgAZ(GP<|#`Q^sYy`*>|H0|1@9hXD%vdOn*
zhP;Cx&puVf-8%^#HVu@VIXIM6vod*H6gD_
zr8!0HJmtHv?YjjBg-LmI%JTQfW>|af$fZEs<(QE7MrVw=rSFz?P9Az?Qm(qGqXru8
zv8co4dFm#cFJ*8`PwZcP2Rw7$&2Z*Jza`x^+KT>2>Ue$!a3QJp3iOI9>D=|Q{=7b1
P`TP|ZI&2^2S8VtPoi*P5
literal 0
HcmV?d00001
diff --git a/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/meta.json b/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/meta.json
new file mode 100644
index 00000000000..eb641eb55e3
--- /dev/null
+++ b/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/meta.json
@@ -0,0 +1,40 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/4397d63a55dac7d0536eb9bcc0a0f68634858c50/code/modules/hydroponics/hydroitemdefines.dm",
+ "states":
+ [
+ {
+ "name": "plantanalyzer"
+ },
+ {
+ "name": "unlit",
+ "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"
+ }
+ ]
+
+}
\ No newline at end of file
diff --git a/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/plantanalyzer.png b/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/plantanalyzer.png
new file mode 100644
index 0000000000000000000000000000000000000000..53e939ad43caae84f2323e7f85e475414508c7b2
GIT binary patch
literal 364
zcmV-y0h9iTP)F
z11N=LVOiRZWsy&@55&xBW@m}9hF9Z)Gq+)ZX7_bOEjg?y=OalK_={*FrKz%0c2|5gbs(p;rIe7q;X~Gf2n=|0000<
KMNUMnLSTX@F_(S-
literal 0
HcmV?d00001
diff --git a/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/unlit.png b/Resources/Textures/_CorvaxNext/Objects/Devices/plant_analyzer.rsi/unlit.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac0384720929499bcc578a034b5b19e3ef4a41ee
GIT binary patch
literal 1181
zcmeAS@N?(olHy`uVBq!ia0vp^4M3d0!3HF+R#ka2FtDU}x;TbZ%y~O^f6*-mfn(t_
zZ5mFOtXOh}r`M}=(dnMA0+kHqc@1hSD;PfUmGxTfTxwwyI&G)B~
zKmQo@Uiq)3be{RVzLqT_m4E)X
z$(1#gf99BN)0^=1wI|2IdXR$oXOd5){S3Vw5`UZF=*!Nqo6KuBv1|GH_c90Am~98D
zY&8?S_-tQJPVDRaE?vQkrfle!;v<{8ny;Kg&-mhUBBi9k)z;yzkd7
zwg*h&iw`sO&FE@$%WhbyQt;|QEJJYu!!uyuG&0U$W;S4FOW@-W5KEeT(8Rx1$RmF`PKsx$o6u
zSI-iTzg50tg;qj0Us
z34vXDzm+>aUjMBB`gKozKt0392$l(FlDo>f+caa8wzSS$(f@s^{+8F4g?Hs0udV-{
zA@K9M<8QeI?`k&cGyT|jU-)m$eJiJ>`U0gN|9vT5y08BqgVX~8Q;B-aOstg-p%g6
zIFIpw!7nL>?^uH#9w)}L&NKb;i%rtHrMKAdfCR@~d4(1`Jw{M;HUDEc^L6fB<`viW
z-CQN)clku4sIztqHE2zAyi--7htX@9ZnCFJH~}?vzID>&L0J
z(d@^|YWL~Z2JbPmc;PkawXyenW|2*L2e#HpSBQh-|JYwfJ6rpUzbfASa{J2|^4sO|
zn*QhOfLUTkO?_?MUH@lW@7sn=`P-%c^=17p_Jz)Wqc^AiJzMMf$o@gt*WI7;7p?zq
z_8}tB?Dvi7_1o|0X2ibuclF