Пак изменений (#236)

* changepack

* 5
This commit is contained in:
Zekins
2025-11-02 16:50:41 +03:00
committed by GitHub
parent 7b43a97c8b
commit 8a29e2b83c
60 changed files with 2084 additions and 110 deletions

View File

@@ -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)));
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -0,0 +1,7 @@
using Content.Shared.Botany.Systems;
namespace Content.Client.Botany.Systems;
public sealed class PlantAnalyzerSystem : SharedPlantAnalyzerSystem
{
}

View 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>

View 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;
}
}

View 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);
}
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)
{

View 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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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))
{

View File

@@ -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;

View File

@@ -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) &&

View File

@@ -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

View File

@@ -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

View File

@@ -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";

View File

@@ -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!;

View File

@@ -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);

View 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;

View 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
{
}

View 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
}

View File

@@ -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;
}

View File

@@ -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");

View File

@@ -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);

View File

@@ -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>

View 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 = Принтер ещё не готов

View File

@@ -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 = Орган успешно присоединен

View File

@@ -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 = Распечатка с анализатора растений.

View File

@@ -0,0 +1,2 @@
ent-BasicSlowdownStatusEffect = простое замедление
.desc = { ent-StatusEffectSlowdown.desc }

View File

@@ -17,6 +17,7 @@
Bucket: 3
BoxMouthSwab: 1
BoxAgrichem: 1
PlantAnalyzer: 4 # Corvax-Wega-Add
#TO DO:
#plant analyzer
contrabandInventory:

View File

@@ -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"

View File

@@ -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

View File

@@ -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" ]

View File

@@ -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" ]

View File

@@ -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" ]

View File

@@ -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:

View File

@@ -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

View File

@@ -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" ]

View File

@@ -134,6 +134,7 @@
- PowerCellsStatic
- ElectronicsStatic
- ScienceStatic # Corvax-Wega
- HydroponicsStatic # Corvax-Wega
- type: EmagLatheRecipes
emagStaticPacks:
- SecurityAmmoStatic

View File

@@ -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

View File

@@ -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" ]

View File

@@ -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:

View File

@@ -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

View File

@@ -0,0 +1,4 @@
- type: entity
parent: StatusEffectSlowdown
id: BasicSlowdownStatusEffect
name: basic slowdown

View File

@@ -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

View File

@@ -0,0 +1,10 @@
- type: latheRecipePack
id: HydroponicsStatic
recipes:
- HydroponicsToolMiniHoe
- HydroponicsToolScythe
- HydroponicsToolHatchet
- HydroponicsToolSpade
- HydroponicsToolClippers
- PlantAnalyzer

View File

@@ -0,0 +1,4 @@
- type: latheRecipe
parent: HandheldHealthAnalyzer
id: PlantAnalyzer
result: PlantAnalyzerEmpty

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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"
}
]
}